aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Chen <ivan@tagg.id>2021-07-02 19:26:28 -0400
committerGitHub <noreply@github.com>2021-07-02 19:26:28 -0400
commitf88193340ef5110080732d37c6a6ab69013f7b36 (patch)
tree3de0e277727ef27f23386043c26f1d3f46ba064c /src
parentcef9d8e9908936a10a9aa0069e364ebf5970da4c (diff)
parentc772d0491529b795cc2cb3d87f6dd2ecd1775116 (diff)
Merge pull request #489 from IvanIFChen/tma953-camera-screen-video
[TMA-953] Camera Screen Video
Diffstat (limited to 'src')
-rw-r--r--src/components/camera/GalleryIcon.tsx4
-rw-r--r--src/components/moments/Moment.tsx45
-rw-r--r--src/screens/moments/CameraScreen.tsx87
-rw-r--r--src/utils/camera.ts32
4 files changed, 97 insertions, 71 deletions
diff --git a/src/components/camera/GalleryIcon.tsx b/src/components/camera/GalleryIcon.tsx
index 8d396550..ca2d2559 100644
--- a/src/components/camera/GalleryIcon.tsx
+++ b/src/components/camera/GalleryIcon.tsx
@@ -1,12 +1,12 @@
import React from 'react';
import {Image, Text, TouchableOpacity, View} from 'react-native';
import {navigateToImagePicker} from '../../utils/camera';
-import {Image as ImageType} from 'react-native-image-crop-picker';
+import {ImageOrVideo} from 'react-native-image-crop-picker';
import {styles} from './styles';
interface GalleryIconProps {
mostRecentPhotoUri: string;
- callback: (pic: ImageType) => void;
+ callback: (media: ImageOrVideo) => void;
}
/*
diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx
index 1e1cadce..73503c5e 100644
--- a/src/components/moments/Moment.tsx
+++ b/src/components/moments/Moment.tsx
@@ -1,6 +1,6 @@
import {useNavigation} from '@react-navigation/native';
import React from 'react';
-import {Alert, StyleProp, StyleSheet, View, ViewStyle} from 'react-native';
+import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native';
import {Text} from 'react-native-animatable';
import {ScrollView, TouchableOpacity} from 'react-native-gesture-handler';
import LinearGradient from 'react-native-linear-gradient';
@@ -12,8 +12,6 @@ import UpIcon from '../../assets/icons/up_icon.svg';
import {TAGG_LIGHT_BLUE} from '../../constants';
import {MomentType, ScreenType} from '../../types';
import {normalize, SCREEN_WIDTH} from '../../utils';
-import {navigateToVideoPicker} from '../../utils/camera';
-import ImagePicker from 'react-native-image-crop-picker';
import MomentTile from './MomentTile';
interface MomentProps {
@@ -43,17 +41,6 @@ const Moment: React.FC<MomentProps> = ({
}) => {
const navigation = useNavigation();
- const navigateToCaptionScreenForVideo = (uri: string) => {
- navigation.navigate('CaptionScreen', {
- screenType,
- title,
- media: {
- uri,
- isVideo: true,
- },
- });
- };
-
const navigateToCameraScreen = () => {
navigation.navigate('CameraScreen', {
title,
@@ -97,36 +84,6 @@ const Moment: React.FC<MomentProps> = ({
<PlusIcon
width={23}
height={23}
- onPress={() =>
- Alert.alert('Video Upload', 'pick one', [
- {
- text: 'gallery',
- onPress: () =>
- navigateToVideoPicker((vid) =>
- navigateToCaptionScreenForVideo(vid.path),
- ),
- },
- {
- text: 'camera (simulator will not work)',
- onPress: () =>
- ImagePicker.openCamera({
- mediaType: 'video',
- })
- .then((vid) => {
- if (vid.path) {
- navigateToCaptionScreenForVideo(vid.path);
- }
- })
- .catch((err) => console.error(err)),
- },
- ])
- }
- color={'black'}
- style={styles.horizontalMargin}
- />
- <PlusIcon
- width={23}
- height={23}
onPress={navigateToCameraScreen}
color={TAGG_LIGHT_BLUE}
style={styles.horizontalMargin}
diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx
index 37b37264..75638c40 100644
--- a/src/screens/moments/CameraScreen.tsx
+++ b/src/screens/moments/CameraScreen.tsx
@@ -6,6 +6,7 @@ import {StackNavigationProp} from '@react-navigation/stack';
import React, {createRef, useCallback, useEffect, useState} from 'react';
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,
@@ -14,9 +15,10 @@ import {
SaveButton,
TaggSquareButton,
} from '../../components';
+import {TAGG_PURPLE} from '../../constants';
import {MainStackParams} from '../../routes';
import {HeaderHeight, normalize, SCREEN_WIDTH} from '../../utils';
-import {showGIFFailureAlert, takePicture} from '../../utils/camera';
+import {showGIFFailureAlert, takePicture, takeVideo} from '../../utils/camera';
type CameraScreenRouteProps = RouteProp<MainStackParams, 'CameraScreen'>;
export type CameraScreenNavigationProps = StackNavigationProp<
@@ -33,9 +35,10 @@ 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 [capturedImage, setCapturedImage] = useState<string>('');
+ const [mediaFromGallery, setMediaFromGallery] = useState<string>('');
const [mostRecentPhoto, setMostRecentPhoto] = useState<string>('');
const [showSaveButton, setShowSaveButton] = useState<boolean>(false);
+ const [isRecording, setIsRecording] = useState<boolean>(false);
useFocusEffect(
useCallback(() => {
@@ -64,7 +67,7 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
.catch((_err) =>
console.log('Unable to fetch preview photo for gallery'),
);
- }, [capturedImage]);
+ }, [mediaFromGallery]);
const navigateToCropper = (uri: string) => {
navigation.navigate('ZoomInCropper', {
@@ -77,13 +80,13 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
});
};
- const navigateToCaptionScreen = () => {
+ const navigateToCaptionScreen = (isVideo: boolean, uri: string) => {
navigation.navigate('CaptionScreen', {
screenType,
title,
media: {
- uri: capturedImage,
- isVideo: false,
+ uri,
+ isVideo,
},
});
};
@@ -96,7 +99,7 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
if (showSaveButton) {
cameraRef.current?.resumePreview();
setShowSaveButton(false);
- setCapturedImage('');
+ setMediaFromGallery('');
} else {
navigation.goBack();
}
@@ -116,24 +119,53 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
/>
<View style={[styles.bottomContainer, {bottom: tabBarHeight}]}>
{showSaveButton ? (
- <SaveButton capturedImageURI={capturedImage} />
+ <SaveButton capturedImageURI={mediaFromGallery} />
) : (
<FlipButton cameraType={cameraType} setCameraType={setCameraType} />
)}
<TouchableOpacity
- onPress={() =>
+ 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);
- setCapturedImage(pic.uri);
- })
- }
- style={styles.captureButtonContainer}>
+ setMediaFromGallery(pic.uri);
+ });
+ }}
+ style={
+ isRecording
+ ? styles.captureButtonVideoContainer
+ : styles.captureButtonContainer
+ }>
<View style={styles.captureButton} />
</TouchableOpacity>
+ {isRecording && (
+ <AnimatedCircularProgress
+ size={95}
+ width={6}
+ fill={100}
+ rotation={0}
+ duration={60000 + 1000} // an extra second for UI to load
+ tintColor={TAGG_PURPLE}
+ style={styles.bottomContainer}
+ lineCap={'round'}
+ />
+ )}
<View style={styles.bottomRightContainer}>
- {capturedImage ? (
+ {mediaFromGallery ? (
<TaggSquareButton
- onPress={navigateToCaptionScreen}
+ onPress={() => navigateToCaptionScreen(false, mediaFromGallery)}
title={'Next'}
buttonStyle={'large'}
buttonColor={'blue'}
@@ -144,15 +176,20 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
) : (
<GalleryIcon
mostRecentPhotoUri={mostRecentPhoto}
- callback={(pic) => {
- const filename = pic.filename;
+ callback={(media) => {
+ const filename = media.filename;
if (
filename &&
(filename.endsWith('gif') || filename.endsWith('GIF'))
) {
- showGIFFailureAlert(() => navigateToCropper(pic.path));
+ showGIFFailureAlert(() => navigateToCropper(media.path));
} else {
- navigateToCropper(pic.path);
+ // is this a video?
+ if (media.duration !== null) {
+ navigateToCaptionScreen(true, media.path);
+ } else {
+ navigateToCropper(media.path);
+ }
}
}}
/>
@@ -173,6 +210,18 @@ const styles = StyleSheet.create({
flexDirection: 'column',
backgroundColor: 'black',
},
+ captureButtonVideoContainer: {
+ alignSelf: 'center',
+ backgroundColor: 'transparent',
+ borderRadius: 100,
+ borderWidth: 15,
+ borderColor: 'rgba(255,255,255,0.3)',
+ width: 93,
+ height: 93,
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
captureButtonContainer: {
alignSelf: 'center',
backgroundColor: 'transparent',
diff --git a/src/utils/camera.ts b/src/utils/camera.ts
index 3937129a..4f933b0c 100644
--- a/src/utils/camera.ts
+++ b/src/utils/camera.ts
@@ -2,11 +2,13 @@ import CameraRoll from '@react-native-community/cameraroll';
import {RefObject} from 'react';
import {Alert} from 'react-native';
import {
+ RecordOptions,
+ RecordResponse,
RNCamera,
TakePictureOptions,
TakePictureResponse,
} from 'react-native-camera';
-import ImagePicker, {Image, Video} from 'react-native-image-crop-picker';
+import ImagePicker, {ImageOrVideo, Video} from 'react-native-image-crop-picker';
import {ERROR_UPLOAD} from '../constants/strings';
/*
@@ -17,11 +19,11 @@ export const takePicture = (
callback: (pic: TakePictureResponse) => void,
) => {
if (cameraRef !== null) {
- cameraRef.current?.pausePreview();
const options: TakePictureOptions = {
forceUpOrientation: true,
orientation: 'portrait',
writeExif: false,
+ pauseAfterCapture: true,
};
cameraRef.current?.takePictureAsync(options).then((pic) => {
callback(pic);
@@ -29,13 +31,31 @@ export const takePicture = (
}
};
+export const takeVideo = (
+ cameraRef: RefObject<RNCamera>,
+ callback: (vid: RecordResponse) => void,
+) => {
+ if (cameraRef !== null) {
+ const options: RecordOptions = {
+ orientation: 'portrait',
+ maxDuration: 60,
+ quality: '1080p',
+ };
+ cameraRef.current?.recordAsync(options).then((vid) => {
+ callback(vid);
+ });
+ }
+};
+
export const saveImageToGallery = (capturedImageURI: string) => {
CameraRoll.save(capturedImageURI, {album: 'Recents', type: 'photo'})
.then((_res) => Alert.alert('Saved to device!'))
.catch((_err) => Alert.alert('Failed to save to device!'));
};
-export const navigateToImagePicker = (callback: (pic: Image) => void) => {
+export const navigateToImagePicker = (
+ callback: (media: ImageOrVideo) => void,
+) => {
ImagePicker.openPicker({
smartAlbums: [
'Favorites',
@@ -44,10 +64,10 @@ export const navigateToImagePicker = (callback: (pic: Image) => void) => {
'Screenshots',
'UserLibrary',
],
- mediaType: 'photo',
+ mediaType: 'any',
})
- .then((pic) => {
- callback(pic);
+ .then((media) => {
+ callback(media);
})
.catch((err) => {
if (err.code && err.code !== 'E_PICKER_CANCELLED') {