From c3d418bc7ad3c702ed52fa522c1225dc4816c092 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 16 Jul 2021 17:46:15 -0400 Subject: Refactor ZoomInCropper to be EditMedia --- src/components/comments/ZoomInCropper.tsx | 432 ------------------------------ src/routes/main/MainStackNavigator.tsx | 2 +- src/routes/main/MainStackScreen.tsx | 6 +- src/screens/index.ts | 1 + src/screens/moments/CameraScreen.tsx | 2 +- src/screens/upload/EditMedia.tsx | 431 +++++++++++++++++++++++++++++ src/screens/upload/index.ts | 1 + 7 files changed, 438 insertions(+), 437 deletions(-) delete mode 100644 src/components/comments/ZoomInCropper.tsx create mode 100644 src/screens/upload/EditMedia.tsx create mode 100644 src/screens/upload/index.ts (limited to 'src') diff --git a/src/components/comments/ZoomInCropper.tsx b/src/components/comments/ZoomInCropper.tsx deleted file mode 100644 index 88f161e5..00000000 --- a/src/components/comments/ZoomInCropper.tsx +++ /dev/null @@ -1,432 +0,0 @@ -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, Text, TouchableOpacity, View} from 'react-native'; -import {normalize} from 'react-native-elements'; -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 {MainStackParams} from '../../routes'; -import { - cropVideo, - HeaderHeight, - SCREEN_HEIGHT, - SCREEN_WIDTH, - trimVideo, -} from '../../utils'; -import {TaggLoadingIndicator, TaggSquareButton} from '../common'; -import {TrimmerPlayer} from '../moments/trimmer'; - -type ZoomInCropperRouteProps = RouteProp; -type ZoomInCropperNavigationProps = StackNavigationProp< - MainStackParams, - 'ZoomInCropper' ->; -interface ZoomInCropperProps { - route: ZoomInCropperRouteProps; - navigation: ZoomInCropperNavigationProps; -} - -export const ZoomInCropper: React.FC = ({ - route, - navigation, -}) => { - const {screenType, media, selectedCategory} = route.params; - const [aspectRatio, setAspectRatio] = useState(1); - // width and height of video, if video - const [origDimensions, setOrigDimensions] = useState([0, 0]); - const vidRef = useRef(null); - const [cropLoading, setCropLoading] = useState(false); - - // Stores the coordinates of the cropped image - const [x0, setX0] = useState(); - const [x1, setX1] = useState(); - const [y0, setY0] = useState(); - const [y1, setY1] = useState(); - - // Stores crop information for video - const [videoCrop, setVideoCrop] = useState<{ - cropWidth?: number; - cropHeight?: number; - cropOffsetX?: number; - cropOffsetY?: number; - }>({}); - - // Stores the current trim endpoints - const [trimEnds, setTrimEnds] = useState<{ - end: number; - start: number; - }>({ - end: 60, - 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)) { - Image.getSize( - media.uri, - (w, h) => { - setAspectRatio(w / h); - }, - (err) => console.log(err), - ); - } else if (media.uri && !checkIfUriImage(media.uri)) { - setVideoCrop((prevState) => ({ - ...prevState, - cropWidth: origDimensions[0], - cropHeight: origDimensions[1], - })); - } - }, []); - - // Possible need to delay setting aspect ratio of video until loaded - useEffect(() => { - if (media.uri && !checkIfUriImage(media.uri)) { - setVideoCrop((prevState) => ({ - ...prevState, - cropWidth: origDimensions[0], - cropHeight: origDimensions[1], - })); - } - }, [origDimensions]); - - // Crops original image based of (x0, y0) and (x1, y1) coordinates - const handleNext = () => { - if (checkIfUriImage(media.uri)) { - if ( - x0 !== undefined && - x1 !== undefined && - y0 !== undefined && - y1 !== undefined - ) { - PhotoManipulator.crop(media.uri, { - 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, - }, - }); - }) - .catch((err) => console.log('err: ', err)); - } else if ( - x0 === undefined && - x1 === undefined && - y0 === undefined && - y1 === undefined - ) { - navigation.navigate('CaptionScreen', { - screenType, - media, - }); - } - } else { - if (!videoCrop.cropHeight || !videoCrop.cropWidth) { - setVideoCrop((prevState) => ({ - ...prevState, - cropWidth: origDimensions[0], - cropHeight: origDimensions[1], - })); - } - 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, - ); - } - }; - - // for whenever the video is altered by the user - const onVideoMove = (zoomableEvent: any) => { - const {originalHeight, originalWidth} = zoomableEvent; - - let cropWidth = 0; - let cropHeight = 0; - let cropOffsetX = 0; - let cropOffsetY = 0; - - if (vidRef !== null && vidRef.current !== null) { - vidRef.current.measure( - ( - _x: number, - _y: number, - width: number, - height: number, - pageX: number, - pageY: number, - ) => { - // width - cropWidth = origDimensions[0] * (originalWidth / width); - - // offsetX - cropOffsetX = -1 * origDimensions[0] * (pageX / width); - if (cropOffsetX < 0) { - cropOffsetX = 0; - } else if (cropOffsetX + cropWidth > origDimensions[0] - 1) { - cropOffsetX = origDimensions[0] - cropWidth - 1; - } - - // height - if ( - height * (SCREEN_WIDTH / aspectRatio / originalHeight) > - SCREEN_HEIGHT - ) { - const superHeight = width / aspectRatio; - cropHeight = origDimensions[1] * (originalHeight / superHeight); - - // offsetY - const topDeadZone = (height - superHeight) / 2; - const offsetY = topDeadZone + pageY; - cropOffsetY = -1 * origDimensions[1] * (offsetY / superHeight); - if (cropOffsetY < 0) { - cropOffsetY = 0; - } else if (cropOffsetY + cropHeight > origDimensions[1]) { - cropOffsetY = origDimensions[1] - cropHeight - 1; - } - } else { - cropHeight = origDimensions[1]; - } - setVideoCrop((prevState) => ({ - ...prevState, - cropWidth: cropWidth, - cropHeight: cropHeight, - cropOffsetX: cropOffsetX, - cropOffsetY: cropOffsetY, - })); - }, - ); - } - }; - - /* Records (x0, y0) and (x1, y1) coordinates used later for cropping, - * based on(x, y) - the center of the image and scale of zoom - */ - const onMove = (position: IOnMove) => { - Image.getSize( - media.uri, - (w, h) => { - const x = position.positionX; - const y = position.positionY; - const scale = position.scale; - const screen_ratio = SCREEN_HEIGHT / SCREEN_WIDTH; - let tempx0 = w / 2 - x * (w / SCREEN_WIDTH) - w / 2 / scale; - let tempx1 = w / 2 - x * (w / SCREEN_WIDTH) + w / 2 / scale; - if (tempx0 < 0) { - tempx0 = 0; - } - if (tempx1 > w) { - tempx1 = w; - } - const x_distance = Math.abs(tempx1 - tempx0); - const y_distance = screen_ratio * x_distance; - let tempy0 = h / 2 - y * (h / SCREEN_HEIGHT) + y_distance / 2; - let tempy1 = h / 2 - y * (h / SCREEN_HEIGHT) - y_distance / 2; - if (tempy0 > h) { - tempy0 = h; - } - if (tempy1 < 0) { - tempy1 = 0; - } - setX0(tempx0); - setX1(tempx1); - setY0(tempy0); - setY1(tempy1); - }, - (err) => console.log(err), - ); - }; - - return ( - - {cropLoading && } - navigation.goBack()}> - - - {checkIfUriImage(media.uri) ? ( - - - - ) : ( - { - onVideoMove(zoomableViewEventObject); - }} - onShiftingAfter={(_1: any, _2: any, zoomableViewEventObject: any) => { - onVideoMove(zoomableViewEventObject); - }} - onShiftingEnd={(_1: any, _2: any, zoomableViewEventObject: any) => { - onVideoMove(zoomableViewEventObject); - }} - onZoomAfter={(_1: any, _2: any, zoomableViewEventObject: any) => { - onVideoMove(zoomableViewEventObject); - }} - onZoomEnd={(_1: any, _2: any, zoomableViewEventObject: any) => { - onVideoMove(zoomableViewEventObject); - }} - style={styles.zoomView}> - - { - const {width, height} = response; - setOrigDimensions([width, height]); - setAspectRatio(width / height); - }} - onChangedEndpoints={(response: {start: number; end: number}) => - setTrimEnds(response) - } - /> - - - )} - {!checkIfUriImage(media.uri) && ( - - - Trim - - Volume - - )} - - - ); -}; - -const styles = StyleSheet.create({ - container: { - backgroundColor: 'black', - height: SCREEN_HEIGHT, - width: SCREEN_WIDTH, - }, - closeButton: { - position: 'absolute', - top: 0, - paddingTop: HeaderHeight, - zIndex: 1, - marginLeft: '5%', - }, - button: { - zIndex: 1, - position: 'absolute', - bottom: normalize(20), - right: normalize(15), - width: normalize(108), - height: normalize(25), - borderRadius: 10, - }, - buttonLabel: { - fontWeight: '700', - fontSize: normalize(15), - lineHeight: normalize(17.8), - 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.2, - flexDirection: 'column', - justifyContent: 'space-evenly', - alignItems: 'center', - }, - iconText: { - color: 'white', - fontSize: 10, - }, - media: { - zIndex: 0, - flex: 1, - }, - videoParent: { - flex: 1, - }, - zoomView: { - backgroundColor: 'black', - flex: 1, - }, - volumnIcon: { - width: 25, - height: 25, - }, -}); 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 = ({route}) => { }} /> = ({route, navigation}) => { }, [mediaFromGallery]); const navigateToCropper = (uri: string) => { - navigation.navigate('ZoomInCropper', { + navigation.navigate('EditMedia', { screenType, media: { uri, diff --git a/src/screens/upload/EditMedia.tsx b/src/screens/upload/EditMedia.tsx new file mode 100644 index 00000000..ed5cd0d7 --- /dev/null +++ b/src/screens/upload/EditMedia.tsx @@ -0,0 +1,431 @@ +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, Text, TouchableOpacity, View} from 'react-native'; +import {normalize} from 'react-native-elements'; +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 {TaggLoadingIndicator, TaggSquareButton} from '../../components/common'; +import {TrimmerPlayer} from '../../components/moments/trimmer'; +import {MainStackParams} from '../../routes'; +import { + cropVideo, + HeaderHeight, + SCREEN_HEIGHT, + SCREEN_WIDTH, + trimVideo, +} from '../../utils'; + +type EditMediaRouteProps = RouteProp; +type EditMediaNavigationProps = StackNavigationProp< + MainStackParams, + 'EditMedia' +>; +interface EditMediaProps { + route: EditMediaRouteProps; + navigation: EditMediaNavigationProps; +} + +export const EditMedia: React.FC = ({route, navigation}) => { + const {screenType, media, selectedCategory} = route.params; + const [aspectRatio, setAspectRatio] = useState(1); + // width and height of video, if video + const [origDimensions, setOrigDimensions] = useState([0, 0]); + const vidRef = useRef(null); + const [cropLoading, setCropLoading] = useState(false); + + // Stores the coordinates of the cropped image + const [x0, setX0] = useState(); + const [x1, setX1] = useState(); + const [y0, setY0] = useState(); + const [y1, setY1] = useState(); + + // Stores crop information for video + const [videoCrop, setVideoCrop] = useState<{ + cropWidth?: number; + cropHeight?: number; + cropOffsetX?: number; + cropOffsetY?: number; + }>({}); + + // Stores the current trim endpoints + const [trimEnds, setTrimEnds] = useState<{ + end: number; + start: number; + }>({ + end: 60, + 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)) { + Image.getSize( + media.uri, + (w, h) => { + setAspectRatio(w / h); + }, + (err) => console.log(err), + ); + } else if (media.uri && !checkIfUriImage(media.uri)) { + setVideoCrop((prevState) => ({ + ...prevState, + cropWidth: origDimensions[0], + cropHeight: origDimensions[1], + })); + } + }, []); + + // Possible need to delay setting aspect ratio of video until loaded + useEffect(() => { + if (media.uri && !checkIfUriImage(media.uri)) { + setVideoCrop((prevState) => ({ + ...prevState, + cropWidth: origDimensions[0], + cropHeight: origDimensions[1], + })); + } + }, [origDimensions]); + + // Crops original image based of (x0, y0) and (x1, y1) coordinates + const handleNext = () => { + if (checkIfUriImage(media.uri)) { + if ( + x0 !== undefined && + x1 !== undefined && + y0 !== undefined && + y1 !== undefined + ) { + PhotoManipulator.crop(media.uri, { + 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, + }, + }); + }) + .catch((err) => console.log('err: ', err)); + } else if ( + x0 === undefined && + x1 === undefined && + y0 === undefined && + y1 === undefined + ) { + navigation.navigate('CaptionScreen', { + screenType, + media, + }); + } + } else { + if (!videoCrop.cropHeight || !videoCrop.cropWidth) { + setVideoCrop((prevState) => ({ + ...prevState, + cropWidth: origDimensions[0], + cropHeight: origDimensions[1], + })); + } + 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, + ); + } + }; + + // for whenever the video is altered by the user + const onVideoMove = (zoomableEvent: any) => { + const {originalHeight, originalWidth} = zoomableEvent; + + let cropWidth = 0; + let cropHeight = 0; + let cropOffsetX = 0; + let cropOffsetY = 0; + + if (vidRef !== null && vidRef.current !== null) { + vidRef.current.measure( + ( + _x: number, + _y: number, + width: number, + height: number, + pageX: number, + pageY: number, + ) => { + // width + cropWidth = origDimensions[0] * (originalWidth / width); + + // offsetX + cropOffsetX = -1 * origDimensions[0] * (pageX / width); + if (cropOffsetX < 0) { + cropOffsetX = 0; + } else if (cropOffsetX + cropWidth > origDimensions[0] - 1) { + cropOffsetX = origDimensions[0] - cropWidth - 1; + } + + // height + if ( + height * (SCREEN_WIDTH / aspectRatio / originalHeight) > + SCREEN_HEIGHT + ) { + const superHeight = width / aspectRatio; + cropHeight = origDimensions[1] * (originalHeight / superHeight); + + // offsetY + const topDeadZone = (height - superHeight) / 2; + const offsetY = topDeadZone + pageY; + cropOffsetY = -1 * origDimensions[1] * (offsetY / superHeight); + if (cropOffsetY < 0) { + cropOffsetY = 0; + } else if (cropOffsetY + cropHeight > origDimensions[1]) { + cropOffsetY = origDimensions[1] - cropHeight - 1; + } + } else { + cropHeight = origDimensions[1]; + } + setVideoCrop((prevState) => ({ + ...prevState, + cropWidth: cropWidth, + cropHeight: cropHeight, + cropOffsetX: cropOffsetX, + cropOffsetY: cropOffsetY, + })); + }, + ); + } + }; + + /* Records (x0, y0) and (x1, y1) coordinates used later for cropping, + * based on(x, y) - the center of the image and scale of zoom + */ + const onMove = (position: IOnMove) => { + Image.getSize( + media.uri, + (w, h) => { + const x = position.positionX; + const y = position.positionY; + const scale = position.scale; + const screen_ratio = SCREEN_HEIGHT / SCREEN_WIDTH; + let tempx0 = w / 2 - x * (w / SCREEN_WIDTH) - w / 2 / scale; + let tempx1 = w / 2 - x * (w / SCREEN_WIDTH) + w / 2 / scale; + if (tempx0 < 0) { + tempx0 = 0; + } + if (tempx1 > w) { + tempx1 = w; + } + const x_distance = Math.abs(tempx1 - tempx0); + const y_distance = screen_ratio * x_distance; + let tempy0 = h / 2 - y * (h / SCREEN_HEIGHT) + y_distance / 2; + let tempy1 = h / 2 - y * (h / SCREEN_HEIGHT) - y_distance / 2; + if (tempy0 > h) { + tempy0 = h; + } + if (tempy1 < 0) { + tempy1 = 0; + } + setX0(tempx0); + setX1(tempx1); + setY0(tempy0); + setY1(tempy1); + }, + (err) => console.log(err), + ); + }; + + return ( + + {cropLoading && } + navigation.goBack()}> + + + {checkIfUriImage(media.uri) ? ( + + + + ) : ( + { + onVideoMove(zoomableViewEventObject); + }} + onShiftingAfter={(_1: any, _2: any, zoomableViewEventObject: any) => { + onVideoMove(zoomableViewEventObject); + }} + onShiftingEnd={(_1: any, _2: any, zoomableViewEventObject: any) => { + onVideoMove(zoomableViewEventObject); + }} + onZoomAfter={(_1: any, _2: any, zoomableViewEventObject: any) => { + onVideoMove(zoomableViewEventObject); + }} + onZoomEnd={(_1: any, _2: any, zoomableViewEventObject: any) => { + onVideoMove(zoomableViewEventObject); + }} + style={styles.zoomView}> + + { + const {width, height} = response; + setOrigDimensions([width, height]); + setAspectRatio(width / height); + }} + onChangedEndpoints={(response: {start: number; end: number}) => + setTrimEnds(response) + } + /> + + + )} + {!checkIfUriImage(media.uri) && ( + + + Trim + + Volume + + )} + + + ); +}; + +const styles = StyleSheet.create({ + container: { + backgroundColor: 'black', + height: SCREEN_HEIGHT, + width: SCREEN_WIDTH, + }, + closeButton: { + position: 'absolute', + top: 0, + paddingTop: HeaderHeight, + zIndex: 1, + marginLeft: '5%', + }, + button: { + zIndex: 1, + position: 'absolute', + bottom: normalize(20), + right: normalize(15), + width: normalize(108), + height: normalize(25), + borderRadius: 10, + }, + buttonLabel: { + fontWeight: '700', + fontSize: normalize(15), + lineHeight: normalize(17.8), + 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.2, + flexDirection: 'column', + justifyContent: 'space-evenly', + alignItems: 'center', + }, + iconText: { + color: 'white', + fontSize: 10, + }, + media: { + zIndex: 0, + flex: 1, + }, + videoParent: { + flex: 1, + }, + zoomView: { + 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'; -- cgit v1.2.3-70-g09d2