aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorIvan Chen <ivan@tagg.id>2021-07-16 21:13:39 -0400
committerGitHub <noreply@github.com>2021-07-16 21:13:39 -0400
commitc1b4e35862172b2268a3a53ece0acc807260652e (patch)
tree40703c8d2dd5abf0a24c07ab8932559ebc2f9cd5 /src/components
parentc22c19c9eeb28641d36cb9df38fe95301e0f768c (diff)
parent4ebb552aef8c5f6136c9359c54f2e4e1aa921241 (diff)
Merge branch 'master' into tma988-remove-positioned-tags-for-video-moments
Diffstat (limited to 'src/components')
-rw-r--r--src/components/camera/SaveButton.tsx14
-rw-r--r--src/components/comments/ZoomInCropper.tsx381
-rw-r--r--src/components/moments/TrimmerPlayer.tsx124
-rw-r--r--src/components/moments/index.ts1
4 files changed, 130 insertions, 390 deletions
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/comments/ZoomInCropper.tsx b/src/components/comments/ZoomInCropper.tsx
deleted file mode 100644
index 6f8ff97c..00000000
--- a/src/components/comments/ZoomInCropper.tsx
+++ /dev/null
@@ -1,381 +0,0 @@
-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 ImageZoom, {IOnMove} from 'react-native-image-pan-zoom';
-import PhotoManipulator from 'react-native-photo-manipulator';
-import CloseIcon from '../../assets/ionicons/close-outline.svg';
-import {MainStackParams} from '../../routes';
-import {
- cropVideo,
- HeaderHeight,
- SCREEN_HEIGHT,
- SCREEN_WIDTH,
-} from '../../utils';
-import {TaggSquareButton} from '../common';
-import ReactNativeZoomableView from '@dudigital/react-native-zoomable-view/src/ReactNativeZoomableView';
-import Video from 'react-native-video';
-
-type ZoomInCropperRouteProps = RouteProp<MainStackParams, 'ZoomInCropper'>;
-type ZoomInCropperNavigationProps = StackNavigationProp<
- MainStackParams,
- 'ZoomInCropper'
->;
-interface ZoomInCropperProps {
- route: ZoomInCropperRouteProps;
- navigation: ZoomInCropperNavigationProps;
-}
-
-export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
- route,
- navigation,
-}) => {
- const {screenType, media, selectedCategory} = route.params;
- const [aspectRatio, setAspectRatio] = useState<number>(1);
- // width and height of video, if video
- const [origDimensions, setOrigDimensions] = useState<number[]>([0, 0]);
- const vidRef = useRef<View>(null);
-
- // Stores the coordinates of the cropped image
- const [x0, setX0] = useState<number>();
- const [x1, setX1] = useState<number>();
- const [y0, setY0] = useState<number>();
- const [y1, setY1] = useState<number>();
-
- // Stores crop information for video
- const [videoCrop, setVideoCrop] = useState<{
- cropWidth?: number;
- cropHeight?: number;
- cropOffsetX?: number;
- cropOffsetY?: number;
- }>({});
-
- 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],
- }));
- }
- cropVideo(
- media.uri,
- (croppedURL: string) => {
- navigation.navigate('CaptionScreen', {
- screenType,
- media: {
- uri: croppedURL,
- isVideo: true,
- },
- selectedCategory,
- });
- },
- videoCrop,
- );
- }
- };
-
- // 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 (
- <View style={styles.container}>
- <TouchableOpacity
- style={styles.closeButton}
- onPress={() => navigation.goBack()}>
- <CloseIcon height={25} width={25} color={'white'} />
- </TouchableOpacity>
- {checkIfUriImage(media.uri) ? (
- <ImageZoom
- style={styles.zoomView}
- cropWidth={SCREEN_WIDTH}
- cropHeight={SCREEN_HEIGHT}
- imageWidth={SCREEN_WIDTH}
- imageHeight={SCREEN_WIDTH / aspectRatio}
- onMove={onMove}>
- <Image
- style={{width: SCREEN_WIDTH, height: SCREEN_WIDTH / aspectRatio}}
- source={{
- uri: media.uri,
- }}
- />
- </ImageZoom>
- ) : (
- <ReactNativeZoomableView
- maxZoom={10}
- minZoom={1}
- zoomStep={0.5}
- initialZoom={1}
- bindToBorders={true}
- // onZoomAfter={this.logOutZoomState}
- onDoubleTapAfter={(
- _1: any,
- _2: any,
- zoomableViewEventObject: any,
- ) => {
- 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}>
- <View style={styles.videoParent} ref={vidRef}>
- <Video
- source={{uri: media.uri}}
- volume={1}
- style={[
- styles.media,
- {
- height: SCREEN_WIDTH / aspectRatio,
- },
- ]}
- repeat={true}
- resizeMode={'contain'}
- onLoad={(response) => {
- const {width, height} = response.naturalSize;
- setOrigDimensions([width, height]);
- setAspectRatio(width / height);
- }}
- />
- </View>
- </ReactNativeZoomableView>
- )}
-
- <TaggSquareButton
- onPress={handleNext}
- title={'Next'}
- buttonStyle={'normal'}
- buttonColor={'blue'}
- labelColor={'white'}
- style={styles.button}
- labelStyle={styles.buttonLabel}
- />
- </View>
- );
-};
-
-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',
- },
- media: {
- zIndex: 0,
- flex: 1,
- },
- videoParent: {
- flex: 1,
- },
- zoomView: {
- backgroundColor: 'black',
- flex: 1,
- },
-});
diff --git a/src/components/moments/TrimmerPlayer.tsx b/src/components/moments/TrimmerPlayer.tsx
new file mode 100644
index 00000000..b28df590
--- /dev/null
+++ b/src/components/moments/TrimmerPlayer.tsx
@@ -0,0 +1,124 @@
+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 {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+
+interface TrimmerPlayerProps {
+ source: string;
+ videoStyles: Object;
+ hideTrimmer: boolean;
+ handleLoad: Function;
+ onChangedEndpoints: Function;
+}
+
+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)
+ const [seekTime, setSeekTime] = useState<number>(0);
+ const [paused, setPaused] = useState<boolean>(false);
+ // Stores where the tracker is
+ const [trackerTime, setTrackerTime] = useState<number>(0);
+ // Stores start/end of desired trimmed video
+ const [end, setEnd] = useState<number>(60);
+ const [start, setStart] = useState<number>(0);
+
+ useEffect(() => {
+ playerRef.current?.seek(seekTime);
+ }, [seekTime]);
+ useEffect(() => {
+ if (!paused && (trackerTime >= end || trackerTime < start)) {
+ setTrackerTime(start);
+ playerRef.current?.seek(start);
+ }
+ }, [trackerTime]);
+ useEffect(() => {
+ setSeekTime(start);
+ setTrackerTime(start);
+ }, [start]);
+ useEffect(() => {
+ setSeekTime(end);
+ setTrackerTime(start);
+ }, [end]);
+ // Callback so parent knows where the trimming endpts are
+ useEffect(() => onChangedEndpoints({end, start}), [end, start]);
+
+ useEffect(() => {
+ playerRef.current?.seek(0);
+ }, [hideTrimmer]);
+
+ return (
+ <>
+ <Video
+ // link to descr and use of props of video player ->
+ // https://github.com/react-native-video/react-native-video
+ ref={(ref) => {
+ playerRef.current = ref || undefined;
+ }}
+ source={{uri: source}}
+ rate={1.0}
+ volume={1.0}
+ muted={false}
+ paused={paused}
+ resizeMode={'contain'}
+ repeat={true}
+ onLoad={(payload) => {
+ setEnd(payload.duration);
+ handleLoad(payload.naturalSize);
+ }}
+ onProgress={(e) => {
+ if (!paused) {
+ setTrackerTime(e.currentTime);
+ }
+ }} // Callback every ~250ms with currentTime
+ style={videoStyles}
+ onTouchEnd={() => {
+ setPaused((state) => !state);
+ }}
+ />
+ {!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 2cc4c9dd..16c9aed2 100644
--- a/src/components/moments/index.ts
+++ b/src/components/moments/index.ts
@@ -4,3 +4,4 @@ export {default as Moment} from './Moment';
export {default as TagFriendsFooter} from './TagFriendsFoooter';
export {default as MomentPost} from './MomentPost';
export {default as TaggedUsersDrawer} from './TaggedUsersDrawer';
+export {default as TrimmerPlayer} from './TrimmerPlayer';