diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/assets/icons/trim.svg | 14 | ||||
-rw-r--r-- | src/assets/images/volume-on.png | bin | 0 -> 5479 bytes | |||
-rw-r--r-- | src/components/camera/SaveButton.tsx | 14 | ||||
-rw-r--r-- | src/components/moments/TrimmerPlayer.tsx (renamed from src/components/moments/trimmer.tsx) | 80 | ||||
-rw-r--r-- | src/components/moments/index.ts | 1 | ||||
-rw-r--r-- | src/routes/main/MainStackNavigator.tsx | 2 | ||||
-rw-r--r-- | src/routes/main/MainStackScreen.tsx | 6 | ||||
-rw-r--r-- | src/screens/index.ts | 1 | ||||
-rw-r--r-- | src/screens/moments/CameraScreen.tsx | 161 | ||||
-rw-r--r-- | src/screens/upload/EditMedia.tsx (renamed from src/components/comments/ZoomInCropper.tsx) | 278 | ||||
-rw-r--r-- | src/screens/upload/index.ts | 1 | ||||
-rw-r--r-- | src/utils/camera.ts | 7 |
12 files changed, 325 insertions, 240 deletions
diff --git a/src/assets/icons/trim.svg b/src/assets/icons/trim.svg new file mode 100644 index 00000000..b966c7f7 --- /dev/null +++ b/src/assets/icons/trim.svg @@ -0,0 +1,14 @@ +<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M24.9544 12.5523C24.5778 11.8828 23.612 11.6806 23.1761 11.6632L23.0715 3.08577C23.0715 2.33264 22.5834 1.97001 22.3393 1.88285H2.72636C2.01506 1.92469 1.94184 2.38842 1.99414 2.61506V21.9665C1.99414 22.7197 2.48228 22.9777 2.72636 23.0126H11.5653C11.3979 23.5565 12.0534 24.5642 12.4021 25H2.72636C0.38326 24.9163 -0.0630441 23.1869 0.00669092 22.3326V2.61506C0.00669092 0.523013 1.8198 0 2.72636 0H22.3393C24.4314 0.125523 24.9544 1.79568 24.9544 2.61506V12.5523Z" fill="white"/> +<circle cx="22.182" cy="16.5267" r="1.5" stroke="white" stroke-width="1.39331"/> +<path d="M9.99707 15.7949H20.2481C20.0389 16.2552 20.1609 16.9979 20.2481 17.3117H11.6184C10.6979 17.1861 10.154 16.2482 9.99707 15.7949Z" fill="white"/> +<circle r="1.5" transform="matrix(4.37114e-08 1 1 -4.37114e-08 16.3246 22.3324)" stroke="white" stroke-width="1.39331"/> +<path d="M15.5928 10.1465L15.5928 20.3975C16.053 20.1883 16.7957 20.3104 17.1095 20.3975L17.1095 11.7678C16.984 10.8473 16.0461 10.3034 15.5928 10.1465Z" fill="white"/> +<rect x="3.56348" y="3.97461" width="2.30126" height="1.67364" rx="0.83682" fill="white"/> +<rect x="3.56348" y="19.3516" width="2.30126" height="1.67364" rx="0.83682" fill="white"/> +<rect x="7.43359" y="3.97461" width="2.30126" height="1.67364" rx="0.83682" fill="white"/> +<rect x="7.43359" y="19.3516" width="2.30126" height="1.67364" rx="0.83682" fill="white"/> +<rect x="11.3037" y="3.97461" width="2.30126" height="1.67364" rx="0.83682" fill="white"/> +<rect x="15.1738" y="3.97461" width="2.30126" height="1.67364" rx="0.83682" fill="white"/> +<rect x="19.0449" y="3.97461" width="2.30126" height="1.67364" rx="0.83682" fill="white"/> +</svg> diff --git a/src/assets/images/volume-on.png b/src/assets/images/volume-on.png Binary files differnew file mode 100644 index 00000000..7cbbaa84 --- /dev/null +++ b/src/assets/images/volume-on.png diff --git a/src/components/camera/SaveButton.tsx b/src/components/camera/SaveButton.tsx index 0e220497..d1b87e65 100644 --- a/src/components/camera/SaveButton.tsx +++ b/src/components/camera/SaveButton.tsx @@ -1,23 +1,19 @@ import React from 'react'; -import {Text, TouchableOpacity} from 'react-native'; +import {StyleProp, Text, TouchableOpacity, ViewStyle} from 'react-native'; import SaveIcon from '../../assets/icons/camera/save.svg'; -import {saveImageToGallery} from '../../utils/camera'; import {styles} from './styles'; interface SaveButtonProps { - capturedImageURI: string; + onPress: () => void; + style?: StyleProp<ViewStyle>; } /* * Appears when a picture has been taken, * On click, saves the captured image to "Recents" album on device gallery */ -export const SaveButton: React.FC<SaveButtonProps> = ({capturedImageURI}) => ( - <TouchableOpacity - onPress={() => { - saveImageToGallery(capturedImageURI); - }} - style={styles.saveButton}> +export const SaveButton: React.FC<SaveButtonProps> = ({onPress, style}) => ( + <TouchableOpacity onPress={onPress} style={[styles.saveButton, style]}> <SaveIcon width={40} height={40} /> <Text style={styles.saveButtonLabel}>Save</Text> </TouchableOpacity> diff --git a/src/components/moments/trimmer.tsx b/src/components/moments/TrimmerPlayer.tsx index c99eaa6f..b28df590 100644 --- a/src/components/moments/trimmer.tsx +++ b/src/components/moments/TrimmerPlayer.tsx @@ -1,16 +1,24 @@ -import React, {useEffect, useState} from 'react'; +import React, {useEffect, useRef, useState} from 'react'; +import {StyleSheet, View} from 'react-native'; import Video from 'react-native-video'; import {Trimmer} from 'react-native-video-processing'; -import {useRef} from 'react'; -import {SCREEN_WIDTH} from '../../utils'; +import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; -export const TrimmerPlayer: React.FC<{ +interface TrimmerPlayerProps { source: string; videoStyles: Object; hideTrimmer: boolean; handleLoad: Function; onChangedEndpoints: Function; -}> = ({source, videoStyles, hideTrimmer, handleLoad, onChangedEndpoints}) => { +} + +const TrimmerPlayer: React.FC<TrimmerPlayerProps> = ({ + source, + videoStyles, + hideTrimmer, + handleLoad, + onChangedEndpoints, +}) => { // Stores the reference to player for seeking const playerRef = useRef<Video>(); // Stores where the video is playing (seekTime) @@ -42,6 +50,10 @@ export const TrimmerPlayer: React.FC<{ // Callback so parent knows where the trimming endpts are useEffect(() => onChangedEndpoints({end, start}), [end, start]); + useEffect(() => { + playerRef.current?.seek(0); + }, [hideTrimmer]); + return ( <> <Video @@ -58,7 +70,6 @@ export const TrimmerPlayer: React.FC<{ resizeMode={'contain'} repeat={true} onLoad={(payload) => { - console.log(payload, source); setEnd(payload.duration); handleLoad(payload.naturalSize); }} @@ -72,27 +83,42 @@ export const TrimmerPlayer: React.FC<{ setPaused((state) => !state); }} /> - <Trimmer - // link to descr and use of props for trimmer -> - // https://github.com/shahen94/react-native-video-processing - source={source} - height={hideTrimmer ? 0 : 75} - width={hideTrimmer ? 0 : SCREEN_WIDTH} - borderWidth={hideTrimmer ? 0 : 100} - onTrackerMove={(e: {currentTime: number}) => { - setPaused(true); - setSeekTime(e.currentTime); - }} - currentTime={trackerTime} - themeColor={'white'} - thumbWidth={10} - trackerColor={'white'} - onChange={(e: {endTime: number; startTime: number}) => { - setPaused(true); - setEnd(e.endTime); - setStart(e.startTime); - }} - /> + {!hideTrimmer && ( + <View style={styles.trimmerContainer}> + <Trimmer + // link to descr and use of props for trimmer -> + // https://github.com/shahen94/react-native-video-processing + source={source} + height={75} + width={SCREEN_WIDTH} + onTrackerMove={(e: {currentTime: number}) => { + setPaused(true); + setSeekTime(e.currentTime); + }} + currentTime={trackerTime} + themeColor={'white'} + thumbWidth={10} + trackerColor={'white'} + onChange={(e: {endTime: number; startTime: number}) => { + setPaused(true); + setEnd(e.endTime); + setStart(e.startTime); + }} + /> + </View> + )} </> ); }; + +const styles = StyleSheet.create({ + trimmerContainer: { + position: 'absolute', + bottom: SCREEN_HEIGHT * 0.1, + alignItems: 'center', + borderWidth: 1, + borderColor: 'red', + }, +}); + +export default TrimmerPlayer; diff --git a/src/components/moments/index.ts b/src/components/moments/index.ts index cac2da2e..95e5c75a 100644 --- a/src/components/moments/index.ts +++ b/src/components/moments/index.ts @@ -3,3 +3,4 @@ export {default as CaptionScreenHeader} from './CaptionScreenHeader'; export {default as Moment} from './Moment'; export {default as TagFriendsFooter} from './TagFriendsFoooter'; export {default as MomentPost} from './MomentPost'; +export {default as TrimmerPlayer} from './TrimmerPlayer'; diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx index c569d2d6..11e9d08d 100644 --- a/src/routes/main/MainStackNavigator.tsx +++ b/src/routes/main/MainStackNavigator.tsx @@ -39,7 +39,7 @@ export type MainStackParams = { screenType: ScreenType; selectedCategory?: string; }; - ZoomInCropper: { + EditMedia: { media: {uri: string; isVideo: boolean}; screenType: ScreenType; selectedCategory?: string; diff --git a/src/routes/main/MainStackScreen.tsx b/src/routes/main/MainStackScreen.tsx index 15300c0d..064e9725 100644 --- a/src/routes/main/MainStackScreen.tsx +++ b/src/routes/main/MainStackScreen.tsx @@ -34,12 +34,12 @@ import { TagSelectionScreen, TagFriendsScreen, CameraScreen, + EditMedia, } from '../../screens'; import MutualBadgeHolders from '../../screens/suggestedPeople/MutualBadgeHolders'; import {ScreenType} from '../../types'; import {AvatarHeaderHeight, ChatHeaderHeight, SCREEN_WIDTH} from '../../utils'; import {MainStack, MainStackParams} from './MainStackNavigator'; -import {ZoomInCropper} from '../../components/comments/ZoomInCropper'; import ChoosingCategoryScreen from '../../screens/profile/ChoosingCategoryScreen'; /** @@ -336,8 +336,8 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => { }} /> <MainStack.Screen - name="ZoomInCropper" - component={ZoomInCropper} + name="EditMedia" + component={EditMedia} options={{ ...modalStyle, gestureEnabled: false, diff --git a/src/screens/index.ts b/src/screens/index.ts index 0c7d911f..5fa14d05 100644 --- a/src/screens/index.ts +++ b/src/screens/index.ts @@ -7,3 +7,4 @@ export * from './suggestedPeopleOnboarding'; export * from './badge'; export * from './chat'; export * from './moments'; +export * from './upload'; diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx index 18e04261..33ee2347 100644 --- a/src/screens/moments/CameraScreen.tsx +++ b/src/screens/moments/CameraScreen.tsx @@ -8,16 +8,10 @@ import {StyleSheet, TouchableOpacity, View} from 'react-native'; import {CameraType, FlashMode, RNCamera} from 'react-native-camera'; import {AnimatedCircularProgress} from 'react-native-circular-progress'; import CloseIcon from '../../assets/ionicons/close-outline.svg'; -import { - FlashButton, - FlipButton, - GalleryIcon, - SaveButton, - TaggSquareButton, -} from '../../components'; +import {FlashButton, FlipButton, GalleryIcon} from '../../components'; import {TAGG_PURPLE} from '../../constants'; import {MainStackParams} from '../../routes'; -import {HeaderHeight, normalize, SCREEN_WIDTH} from '../../utils'; +import {HeaderHeight, SCREEN_WIDTH} from '../../utils'; import {showGIFFailureAlert, takePicture, takeVideo} from '../../utils/camera'; type CameraScreenRouteProps = RouteProp<MainStackParams, 'CameraScreen'>; @@ -35,9 +29,7 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => { const tabBarHeight = useBottomTabBarHeight(); const [cameraType, setCameraType] = useState<keyof CameraType>('front'); const [flashMode, setFlashMode] = useState<keyof FlashMode>('off'); - const [mediaFromGallery, setMediaFromGallery] = useState<string>(''); const [mostRecentPhoto, setMostRecentPhoto] = useState<string>(''); - const [showSaveButton, setShowSaveButton] = useState<boolean>(false); const [isRecording, setIsRecording] = useState<boolean>(false); useFocusEffect( @@ -62,14 +54,21 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => { .catch((_err) => console.log('Unable to fetch preview photo for gallery'), ); - }, [mediaFromGallery]); + }, []); - const navigateToCropper = (uri: string) => { - navigation.navigate('ZoomInCropper', { + const navigateToEditMedia = (uri: string) => { + navigation.navigate('EditMedia', { screenType, media: { uri, - isVideo: false, + isVideo: !( + uri.endsWith('jpg') || + uri.endsWith('JPG') || + uri.endsWith('PNG') || + uri.endsWith('png') || + uri.endsWith('GIF') || + uri.endsWith('gif') + ), }, selectedCategory, }); @@ -85,21 +84,11 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => { }); }; - /* - * If picture is not taken yet, exists from camera screen to profile view - * If picture is taken, exists from captured image's preview to camera - * */ const handleClose = () => { - if (showSaveButton) { - cameraRef.current?.resumePreview(); - setShowSaveButton(false); - setMediaFromGallery(''); - } else { - navigation.dangerouslyGetParent()?.setOptions({ - tabBarVisible: true, - }); - navigation.goBack(); - } + navigation.dangerouslyGetParent()?.setOptions({ + tabBarVisible: true, + }); + navigation.goBack(); }; return ( @@ -118,42 +107,31 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => { }} /> <View style={[styles.bottomContainer, {bottom: tabBarHeight}]}> - {showSaveButton ? ( - <SaveButton capturedImageURI={mediaFromGallery} /> - ) : ( - <FlipButton cameraType={cameraType} setCameraType={setCameraType} /> - )} - {!showSaveButton ? ( - <TouchableOpacity - style={ - isRecording - ? styles.captureButtonVideoContainer - : styles.captureButtonContainer + <FlipButton cameraType={cameraType} setCameraType={setCameraType} /> + <TouchableOpacity + style={ + isRecording + ? styles.captureButtonVideoContainer + : styles.captureButtonContainer + } + activeOpacity={1} + onLongPress={() => { + takeVideo(cameraRef, (vid) => { + navigateToCaptionScreen(true, vid.uri); + }); + setIsRecording(true); + }} + onPressOut={async () => { + if (await cameraRef.current?.isRecording()) { + cameraRef.current?.stopRecording(); + setIsRecording(false); } - activeOpacity={1} - onLongPress={() => { - takeVideo(cameraRef, (vid) => { - navigateToCaptionScreen(true, vid.uri); - }); - setIsRecording(true); - }} - onPressOut={async () => { - if (await cameraRef.current?.isRecording()) { - cameraRef.current?.stopRecording(); - setIsRecording(false); - } - }} - onPress={() => { - takePicture(cameraRef, (pic) => { - setShowSaveButton(true); - setMediaFromGallery(pic.uri); - }); - }}> - <View style={styles.captureButton} /> - </TouchableOpacity> - ) : ( - <View style={styles.captureButtonPlaceholder} /> - )} + }} + onPress={() => { + takePicture(cameraRef, (pic) => navigateToEditMedia(pic.uri)); + }}> + <View style={styles.captureButton} /> + </TouchableOpacity> {isRecording && ( <AnimatedCircularProgress size={95} @@ -167,32 +145,20 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => { /> )} <View style={styles.bottomRightContainer}> - {mediaFromGallery ? ( - <TaggSquareButton - onPress={() => navigateToCaptionScreen(false, mediaFromGallery)} - title={'Next'} - buttonStyle={'large'} - buttonColor={'blue'} - labelColor={'white'} - style={styles.nextButton} - labelStyle={styles.nextButtonLabel} - /> - ) : ( - <GalleryIcon - mostRecentPhotoUri={mostRecentPhoto} - callback={(media) => { - const filename = media.filename; - if ( - filename && - (filename.endsWith('gif') || filename.endsWith('GIF')) - ) { - showGIFFailureAlert(() => navigateToCropper(media.path)); - } else { - navigateToCropper(media.path); - } - }} - /> - )} + <GalleryIcon + mostRecentPhotoUri={mostRecentPhoto} + callback={(media) => { + const filename = media.filename; + if ( + filename && + (filename.endsWith('gif') || filename.endsWith('GIF')) + ) { + showGIFFailureAlert(() => navigateToEditMedia(media.path)); + } else { + navigateToEditMedia(media.path); + } + }} + /> </View> </View> </View> @@ -221,10 +187,6 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, - captureButtonPlaceholder: { - width: 93, - height: 93, - }, captureButtonContainer: { alignSelf: 'center', backgroundColor: 'transparent', @@ -262,19 +224,6 @@ const styles = StyleSheet.create({ alignItems: 'center', width: (SCREEN_WIDTH - 100) / 2, }, - nextButton: { - zIndex: 1, - width: normalize(100), - height: normalize(37), - borderRadius: 10, - }, - nextButtonLabel: { - fontWeight: '700', - fontSize: normalize(15), - lineHeight: normalize(17.8), - letterSpacing: normalize(1.3), - textAlign: 'center', - }, }); export default CameraScreen; diff --git a/src/components/comments/ZoomInCropper.tsx b/src/screens/upload/EditMedia.tsx index b4333cbb..1dc408ee 100644 --- a/src/components/comments/ZoomInCropper.tsx +++ b/src/screens/upload/EditMedia.tsx @@ -1,43 +1,48 @@ +import ReactNativeZoomableView from '@dudigital/react-native-zoomable-view/src/ReactNativeZoomableView'; import {RouteProp} from '@react-navigation/core'; import {StackNavigationProp} from '@react-navigation/stack'; import React, {useEffect, useRef, useState} from 'react'; -import {Image, StyleSheet, TouchableOpacity, View} from 'react-native'; -import {normalize} from 'react-native-elements'; +import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native'; import ImageZoom, {IOnMove} from 'react-native-image-pan-zoom'; import PhotoManipulator from 'react-native-photo-manipulator'; +import TrimIcon from '../../assets/icons/trim.svg'; import CloseIcon from '../../assets/ionicons/close-outline.svg'; +import {SaveButton, TrimmerPlayer} from '../../components'; +import {TaggLoadingIndicator, TaggSquareButton} from '../../components/common'; import {MainStackParams} from '../../routes'; import { cropVideo, - trimVideo, HeaderHeight, + normalize, + saveImageToGallery, SCREEN_HEIGHT, SCREEN_WIDTH, + trimVideo, } from '../../utils'; -import {TaggSquareButton, TaggLoadingIndicator} from '../common'; -import ReactNativeZoomableView from '@dudigital/react-native-zoomable-view/src/ReactNativeZoomableView'; -import {TrimmerPlayer} from '../moments/trimmer'; -type ZoomInCropperRouteProps = RouteProp<MainStackParams, 'ZoomInCropper'>; -type ZoomInCropperNavigationProps = StackNavigationProp< +type EditMediaRouteProps = RouteProp<MainStackParams, 'EditMedia'>; +type EditMediaNavigationProps = StackNavigationProp< MainStackParams, - 'ZoomInCropper' + 'EditMedia' >; -interface ZoomInCropperProps { - route: ZoomInCropperRouteProps; - navigation: ZoomInCropperNavigationProps; +interface EditMediaProps { + route: EditMediaRouteProps; + navigation: EditMediaNavigationProps; } -export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ - route, - navigation, -}) => { - const {screenType, media, selectedCategory} = route.params; +export const EditMedia: React.FC<EditMediaProps> = ({route, navigation}) => { + const { + screenType, + selectedCategory, + media: {isVideo}, + } = route.params; const [aspectRatio, setAspectRatio] = useState<number>(1); // width and height of video, if video const [origDimensions, setOrigDimensions] = useState<number[]>([0, 0]); + const [mediaUri, setMediaUri] = useState<string>(route.params.media.uri); const vidRef = useRef<View>(null); const [cropLoading, setCropLoading] = useState<boolean>(false); + const [hideTrimmer, setHideTrimmer] = useState<boolean>(true); // Stores the coordinates of the cropped image const [x0, setX0] = useState<number>(); @@ -62,28 +67,17 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ start: 0, }); - const checkIfUriImage = (uri: string) => { - return ( - uri.endsWith('jpg') || - uri.endsWith('JPG') || - uri.endsWith('PNG') || - uri.endsWith('png') || - uri.endsWith('GIF') || - uri.endsWith('gif') - ); - }; - // Setting original aspect ratio of image useEffect(() => { - if (media.uri && checkIfUriImage(media.uri)) { + if (mediaUri && !isVideo) { Image.getSize( - media.uri, + mediaUri, (w, h) => { setAspectRatio(w / h); }, (err) => console.log(err), ); - } else if (media.uri && !checkIfUriImage(media.uri)) { + } else if (mediaUri && isVideo) { setVideoCrop((prevState) => ({ ...prevState, cropWidth: origDimensions[0], @@ -94,7 +88,7 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ // Possible need to delay setting aspect ratio of video until loaded useEffect(() => { - if (media.uri && !checkIfUriImage(media.uri)) { + if (mediaUri && isVideo) { setVideoCrop((prevState) => ({ ...prevState, cropWidth: origDimensions[0], @@ -104,28 +98,23 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ }, [origDimensions]); // Crops original image based of (x0, y0) and (x1, y1) coordinates - const handleNext = () => { - if (checkIfUriImage(media.uri)) { + const processVideo = (callback: (finalUri: string) => void) => { + if (!isVideo) { if ( x0 !== undefined && x1 !== undefined && y0 !== undefined && y1 !== undefined ) { - PhotoManipulator.crop(media.uri, { + PhotoManipulator.crop(mediaUri, { x: x0, y: y1, width: Math.abs(x0 - x1), height: Math.abs(y0 - y1), }) .then((croppedURL) => { - navigation.navigate('CaptionScreen', { - screenType, - media: { - uri: croppedURL, - isVideo: false, - }, - }); + // Pass the cropped image + callback(croppedURL); }) .catch((err) => console.log('err: ', err)); } else if ( @@ -134,10 +123,8 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ y0 === undefined && y1 === undefined ) { - navigation.navigate('CaptionScreen', { - screenType, - media, - }); + // If no crop coordinates are set, then we will just pass the original image + callback(mediaUri); } } else { if (!videoCrop.cropHeight || !videoCrop.cropWidth) { @@ -148,25 +135,14 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ })); } setCropLoading(true); - trimVideo( - media.uri, - (trimmedURL: string) => - cropVideo( - trimmedURL, - (croppedURL: string) => { - setCropLoading(false); - navigation.navigate('CaptionScreen', { - screenType, - media: { - uri: croppedURL, - isVideo: true, - }, - selectedCategory, - }); - }, - videoCrop, - ), - trimEnds, + cropVideo( + mediaUri, + (croppedURL: string) => { + setCropLoading(false); + // Pass the trimmed/cropped video + callback(croppedURL); + }, + videoCrop, ); } }; @@ -238,7 +214,7 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ */ const onMove = (position: IOnMove) => { Image.getSize( - media.uri, + mediaUri, (w, h) => { const x = position.positionX; const y = position.positionY; @@ -274,12 +250,38 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ return ( <View style={styles.container}> {cropLoading && <TaggLoadingIndicator fullscreen />} - <TouchableOpacity - style={styles.closeButton} - onPress={() => navigation.goBack()}> - <CloseIcon height={25} width={25} color={'white'} /> - </TouchableOpacity> - {checkIfUriImage(media.uri) ? ( + {hideTrimmer && ( + <TouchableOpacity + style={styles.closeButton} + onPress={() => navigation.goBack()}> + <CloseIcon height={25} width={25} color={'white'} /> + </TouchableOpacity> + )} + {!hideTrimmer && ( + <View style={styles.topContainer}> + <TouchableOpacity onPress={() => setHideTrimmer(true)}> + <Text style={styles.bigText}>Cancel</Text> + </TouchableOpacity> + <TouchableOpacity + onPress={() => + trimVideo( + mediaUri, + (trimmedUri: string) => { + setCropLoading(true); + setMediaUri(trimmedUri); + setTimeout(() => { + setHideTrimmer(true); + setCropLoading(false); + }, 500); + }, + trimEnds, + ) + }> + <Text style={styles.bigText}>Save</Text> + </TouchableOpacity> + </View> + )} + {!isVideo ? ( <ImageZoom style={styles.zoomView} cropWidth={SCREEN_WIDTH} @@ -290,7 +292,7 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ <Image style={{width: SCREEN_WIDTH, height: SCREEN_WIDTH / aspectRatio}} source={{ - uri: media.uri, + uri: mediaUri, }} /> </ImageZoom> @@ -301,7 +303,7 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ zoomStep={0.5} initialZoom={1} bindToBorders={true} - // onZoomAfter={this.logOutZoomState} + zoomEnabled={hideTrimmer} onDoubleTapAfter={( _1: any, _2: any, @@ -324,8 +326,8 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ style={styles.zoomView}> <View style={styles.videoParent} ref={vidRef}> <TrimmerPlayer - hideTrimmer={false} - source={media.uri} + hideTrimmer={hideTrimmer} + source={mediaUri} videoStyles={[ styles.media, { @@ -344,16 +346,58 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ </View> </ReactNativeZoomableView> )} - - <TaggSquareButton - onPress={handleNext} - title={'Next'} - buttonStyle={'normal'} - buttonColor={'blue'} - labelColor={'white'} - style={styles.button} - labelStyle={styles.buttonLabel} - /> + {isVideo && hideTrimmer && ( + <View style={styles.iconCarrier}> + <TouchableOpacity + style={styles.iconContainer} + onPress={() => setHideTrimmer((state) => !state)}> + <TrimIcon /> + <Text style={styles.iconText}>Trim</Text> + </TouchableOpacity> + <TouchableOpacity + style={styles.iconContainer} + // TODO: finish me + onPress={() => null}> + <Image + style={styles.volumnIcon} + source={require('../../assets/images/volume-on.png')} + /> + <Text style={styles.iconText}>Volume</Text> + </TouchableOpacity> + </View> + )} + {hideTrimmer && ( + <View style={styles.bottomContainer}> + <SaveButton + style={styles.saveButton} + onPress={() => + processVideo((uri) => + saveImageToGallery(uri, isVideo ? 'video' : 'photo'), + ) + } + /> + <TaggSquareButton + style={styles.button} + onPress={() => + processVideo((uri) => + navigation.navigate('CaptionScreen', { + screenType, + media: { + uri: uri, + isVideo: isVideo, + }, + selectedCategory, + }), + ) + } + title={'Next'} + buttonStyle={'large'} + buttonColor={'blue'} + labelColor={'white'} + labelStyle={styles.buttonLabel} + /> + </View> + )} </View> ); }; @@ -371,14 +415,36 @@ const styles = StyleSheet.create({ zIndex: 1, marginLeft: '5%', }, - button: { - zIndex: 1, + bottomContainer: { position: 'absolute', - bottom: normalize(20), - right: normalize(15), + bottom: SCREEN_HEIGHT * 0.1, + width: SCREEN_WIDTH * 0.8, + justifyContent: 'space-between', + alignItems: 'center', + alignSelf: 'center', + flexDirection: 'row', + }, + topContainer: { + position: 'absolute', + top: SCREEN_HEIGHT * 0.1, + width: SCREEN_WIDTH * 0.9, + justifyContent: 'space-between', + alignItems: 'center', + alignSelf: 'center', + flexDirection: 'row', + zIndex: 1, + }, + bigText: { + fontSize: normalize(15), + color: 'white', + fontWeight: 'bold', + }, + saveButton: { + width: 50, + }, + button: { width: normalize(108), - height: normalize(25), - borderRadius: 10, + height: normalize(36), }, buttonLabel: { fontWeight: '700', @@ -387,6 +453,28 @@ const styles = StyleSheet.create({ letterSpacing: normalize(1.3), textAlign: 'center', }, + iconCarrier: { + width: SCREEN_WIDTH * 0.15, + height: SCREEN_HEIGHT * 0.2, + borderRadius: SCREEN_WIDTH * 0.1, + backgroundColor: 'rgba(0, 0, 0, 0.3)', + position: 'absolute', + right: SCREEN_WIDTH * 0.025, + top: SCREEN_HEIGHT * 0.1, + flexDirection: 'column', + justifyContent: 'space-evenly', + alignItems: 'center', + }, + iconContainer: { + height: 50, + alignItems: 'center', + justifyContent: 'space-between', + }, + iconText: { + color: 'white', + fontSize: normalize(11), + fontWeight: 'bold', + }, media: { zIndex: 0, flex: 1, @@ -398,4 +486,10 @@ const styles = StyleSheet.create({ backgroundColor: 'black', flex: 1, }, + volumnIcon: { + width: 25, + height: 25, + }, }); + +export default EditMedia; diff --git a/src/screens/upload/index.ts b/src/screens/upload/index.ts new file mode 100644 index 00000000..0dadeede --- /dev/null +++ b/src/screens/upload/index.ts @@ -0,0 +1 @@ +export {default as EditMedia} from './EditMedia'; diff --git a/src/utils/camera.ts b/src/utils/camera.ts index 5485b1ca..9d7ff67f 100644 --- a/src/utils/camera.ts +++ b/src/utils/camera.ts @@ -48,8 +48,11 @@ export const takeVideo = ( } }; -export const saveImageToGallery = (capturedImageURI: string) => { - CameraRoll.save(capturedImageURI, {album: 'Recents', type: 'photo'}) +export const saveImageToGallery = ( + capturedImageURI: string, + type: 'photo' | 'video', +) => { + CameraRoll.save(capturedImageURI, {album: 'Recents', type: type}) .then((_res) => Alert.alert('Saved to device!')) .catch((_err) => Alert.alert('Failed to save to device!')); }; |