aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/components/comments/ZoomInCropper.tsx62
-rw-r--r--src/components/moments/trimmer.tsx98
-rw-r--r--src/screens/moments/CameraScreen.tsx60
-rw-r--r--src/screens/profile/CaptionScreen.tsx2
-rw-r--r--src/utils/camera.ts34
-rw-r--r--src/utils/users.ts1
6 files changed, 192 insertions, 65 deletions
diff --git a/src/components/comments/ZoomInCropper.tsx b/src/components/comments/ZoomInCropper.tsx
index 6f8ff97c..b4333cbb 100644
--- a/src/components/comments/ZoomInCropper.tsx
+++ b/src/components/comments/ZoomInCropper.tsx
@@ -9,13 +9,14 @@ import CloseIcon from '../../assets/ionicons/close-outline.svg';
import {MainStackParams} from '../../routes';
import {
cropVideo,
+ trimVideo,
HeaderHeight,
SCREEN_HEIGHT,
SCREEN_WIDTH,
} from '../../utils';
-import {TaggSquareButton} from '../common';
+import {TaggSquareButton, TaggLoadingIndicator} from '../common';
import ReactNativeZoomableView from '@dudigital/react-native-zoomable-view/src/ReactNativeZoomableView';
-import Video from 'react-native-video';
+import {TrimmerPlayer} from '../moments/trimmer';
type ZoomInCropperRouteProps = RouteProp<MainStackParams, 'ZoomInCropper'>;
type ZoomInCropperNavigationProps = StackNavigationProp<
@@ -36,6 +37,7 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
// width and height of video, if video
const [origDimensions, setOrigDimensions] = useState<number[]>([0, 0]);
const vidRef = useRef<View>(null);
+ const [cropLoading, setCropLoading] = useState<boolean>(false);
// Stores the coordinates of the cropped image
const [x0, setX0] = useState<number>();
@@ -51,6 +53,15 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
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') ||
@@ -136,19 +147,26 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
cropHeight: origDimensions[1],
}));
}
- cropVideo(
+ setCropLoading(true);
+ trimVideo(
media.uri,
- (croppedURL: string) => {
- navigation.navigate('CaptionScreen', {
- screenType,
- media: {
- uri: croppedURL,
- isVideo: true,
+ (trimmedURL: string) =>
+ cropVideo(
+ trimmedURL,
+ (croppedURL: string) => {
+ setCropLoading(false);
+ navigation.navigate('CaptionScreen', {
+ screenType,
+ media: {
+ uri: croppedURL,
+ isVideo: true,
+ },
+ selectedCategory,
+ });
},
- selectedCategory,
- });
- },
- videoCrop,
+ videoCrop,
+ ),
+ trimEnds,
);
}
};
@@ -255,6 +273,7 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
return (
<View style={styles.container}>
+ {cropLoading && <TaggLoadingIndicator fullscreen />}
<TouchableOpacity
style={styles.closeButton}
onPress={() => navigation.goBack()}>
@@ -304,22 +323,23 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
}}
style={styles.zoomView}>
<View style={styles.videoParent} ref={vidRef}>
- <Video
- source={{uri: media.uri}}
- volume={1}
- style={[
+ <TrimmerPlayer
+ hideTrimmer={false}
+ source={media.uri}
+ videoStyles={[
styles.media,
{
height: SCREEN_WIDTH / aspectRatio,
},
]}
- repeat={true}
- resizeMode={'contain'}
- onLoad={(response) => {
- const {width, height} = response.naturalSize;
+ handleLoad={(response: {width: number; height: number}) => {
+ const {width, height} = response;
setOrigDimensions([width, height]);
setAspectRatio(width / height);
}}
+ onChangedEndpoints={(response: {start: number; end: number}) =>
+ setTrimEnds(response)
+ }
/>
</View>
</ReactNativeZoomableView>
diff --git a/src/components/moments/trimmer.tsx b/src/components/moments/trimmer.tsx
new file mode 100644
index 00000000..c99eaa6f
--- /dev/null
+++ b/src/components/moments/trimmer.tsx
@@ -0,0 +1,98 @@
+import React, {useEffect, useState} from 'react';
+import Video from 'react-native-video';
+import {Trimmer} from 'react-native-video-processing';
+import {useRef} from 'react';
+import {SCREEN_WIDTH} from '../../utils';
+
+export const TrimmerPlayer: React.FC<{
+ source: string;
+ videoStyles: Object;
+ hideTrimmer: boolean;
+ handleLoad: Function;
+ onChangedEndpoints: Function;
+}> = ({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]);
+
+ 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) => {
+ console.log(payload, source);
+ setEnd(payload.duration);
+ handleLoad(payload.naturalSize);
+ }}
+ onProgress={(e) => {
+ if (!paused) {
+ setTrackerTime(e.currentTime);
+ }
+ }} // Callback every ~250ms with currentTime
+ style={videoStyles}
+ onTouchEnd={() => {
+ 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);
+ }}
+ />
+ </>
+ );
+};
diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx
index ee5834cb..18e04261 100644
--- a/src/screens/moments/CameraScreen.tsx
+++ b/src/screens/moments/CameraScreen.tsx
@@ -123,33 +123,37 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
) : (
<FlipButton cameraType={cameraType} setCameraType={setCameraType} />
)}
- <TouchableOpacity
- activeOpacity={1}
- onLongPress={() => {
- takeVideo(cameraRef, (vid) => {
- navigateToCaptionScreen(true, vid.uri);
- });
- setIsRecording(true);
- }}
- onPressOut={async () => {
- if (await cameraRef.current?.isRecording()) {
- cameraRef.current?.stopRecording();
- setIsRecording(false);
+ {!showSaveButton ? (
+ <TouchableOpacity
+ style={
+ isRecording
+ ? styles.captureButtonVideoContainer
+ : styles.captureButtonContainer
}
- }}
- onPress={() => {
- takePicture(cameraRef, (pic) => {
- setShowSaveButton(true);
- setMediaFromGallery(pic.uri);
- });
- }}
- style={
- isRecording
- ? styles.captureButtonVideoContainer
- : styles.captureButtonContainer
- }>
- <View style={styles.captureButton} />
- </TouchableOpacity>
+ 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} />
+ )}
{isRecording && (
<AnimatedCircularProgress
size={95}
@@ -217,6 +221,10 @@ const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
},
+ captureButtonPlaceholder: {
+ width: 93,
+ height: 93,
+ },
captureButtonContainer: {
alignSelf: 'center',
backgroundColor: 'transparent',
diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx
index 6bcf0e24..eba3e4bf 100644
--- a/src/screens/profile/CaptionScreen.tsx
+++ b/src/screens/profile/CaptionScreen.tsx
@@ -49,7 +49,6 @@ import {RootState} from '../../store/rootReducer';
import {MomentTagType} from '../../types';
import {isIPhoneX, normalize, SCREEN_WIDTH, StatusBarHeight} from '../../utils';
import {mentionPartTypes} from '../../utils/comments';
-
/**
* Upload Screen to allow users to upload posts to Tagg
*/
@@ -362,6 +361,7 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
</SearchBackground>
);
};
+
const styles = StyleSheet.create({
contentContainer: {
paddingTop: StatusBarHeight,
diff --git a/src/utils/camera.ts b/src/utils/camera.ts
index 9e37d62e..5485b1ca 100644
--- a/src/utils/camera.ts
+++ b/src/utils/camera.ts
@@ -9,7 +9,7 @@ import {
TakePictureResponse,
} from 'react-native-camera';
import {ProcessingManager} from 'react-native-video-processing';
-import ImagePicker, {ImageOrVideo, Video} from 'react-native-image-crop-picker';
+import ImagePicker, {ImageOrVideo} from 'react-native-image-crop-picker';
import {ERROR_UPLOAD} from '../constants/strings';
/*
@@ -66,6 +66,7 @@ export const navigateToImagePicker = (
'UserLibrary',
],
mediaType: 'any',
+ compressVideoPreset: 'Passthrough',
})
.then((media) => {
callback(media);
@@ -77,22 +78,6 @@ export const navigateToImagePicker = (
});
};
-export const navigateToVideoPicker = (callback: (vid: Video) => void) => {
- ImagePicker.openPicker({
- mediaType: 'video',
- })
- .then(async (vid) => {
- if (vid.path) {
- callback(vid);
- }
- })
- .catch((err) => {
- if (err.code && err.code !== 'E_PICKER_CANCELLED') {
- Alert.alert(ERROR_UPLOAD);
- }
- });
-};
-
export const showGIFFailureAlert = (onSuccess: () => void) =>
Alert.alert(
'Warning',
@@ -118,6 +103,21 @@ export const showGIFFailureAlert = (onSuccess: () => void) =>
},
);
+export const trimVideo = (
+ sourceUri: string,
+ handleData: (data: any) => any,
+ ends: {
+ start: number;
+ end: number;
+ },
+) => {
+ ProcessingManager.trim(sourceUri, {
+ startTime: ends.start / 2, //needed divide by 2 for bug in module
+ endTime: ends.end,
+ quality: 'passthrough',
+ }).then((data: any) => handleData(data));
+};
+
export const cropVideo = (
sourceUri: string,
handleData: (data: any) => any,
diff --git a/src/utils/users.ts b/src/utils/users.ts
index c1c3b8bc..992d7721 100644
--- a/src/utils/users.ts
+++ b/src/utils/users.ts
@@ -280,6 +280,7 @@ export const patchProfile = async (
screenTitle = '';
requestTitle = '';
fileName = '';
+ imageSettings = {};
}
return await ImagePicker.openPicker(imageSettings)