aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/routes/main/MainStackNavigator.tsx4
-rw-r--r--src/routes/main/MainStackScreen.tsx8
-rw-r--r--src/screens/moments/CameraScreen.tsx325
-rw-r--r--src/screens/moments/index.ts1
4 files changed, 338 insertions, 0 deletions
diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx
index b58e03cc..ef3fc7fd 100644
--- a/src/routes/main/MainStackNavigator.tsx
+++ b/src/routes/main/MainStackNavigator.tsx
@@ -115,6 +115,10 @@ export type MainStackParams = {
screenType: ScreenType;
title: string;
};
+ CameraScreen: {
+ title: string;
+ screenType: ScreenType;
+ };
};
export const MainStack = createStackNavigator<MainStackParams>();
diff --git a/src/routes/main/MainStackScreen.tsx b/src/routes/main/MainStackScreen.tsx
index 9e3747f9..f6adeab1 100644
--- a/src/routes/main/MainStackScreen.tsx
+++ b/src/routes/main/MainStackScreen.tsx
@@ -34,6 +34,7 @@ import {
SuggestedPeopleWelcomeScreen,
TagSelectionScreen,
TagFriendsScreen,
+ CameraScreen,
} from '../../screens';
import MutualBadgeHolders from '../../screens/suggestedPeople/MutualBadgeHolders';
import {ScreenType} from '../../types';
@@ -334,6 +335,13 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => {
gestureEnabled: false,
}}
/>
+ <MainStack.Screen
+ name="CameraScreen"
+ component={CameraScreen}
+ options={{
+ gestureEnabled: false,
+ }}
+ />
</MainStack.Navigator>
);
};
diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx
new file mode 100644
index 00000000..70411a83
--- /dev/null
+++ b/src/screens/moments/CameraScreen.tsx
@@ -0,0 +1,325 @@
+import CameraRoll from '@react-native-community/cameraroll';
+import {useBottomTabBarHeight} from '@react-navigation/bottom-tabs';
+import {RouteProp, useFocusEffect} from '@react-navigation/core';
+import {StackNavigationProp} from '@react-navigation/stack';
+import React, {createRef, useCallback, useEffect, useState} from 'react';
+import {
+ Alert,
+ Image,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+} from 'react-native';
+import {CameraType, FlashMode, RNCamera} from 'react-native-camera';
+import ImagePicker from 'react-native-image-crop-picker';
+import FlashOffIcon from '../../assets/icons/camera/flash-off.svg';
+import FlashOnIcon from '../../assets/icons/camera/flash-on.svg';
+import FlipIcon from '../../assets/icons/camera/flip.svg';
+import SaveIcon from '../../assets/icons/camera/save.svg';
+import CloseIcon from '../../assets/ionicons/close-outline.svg';
+import {TaggSquareButton} from '../../components';
+import {ERROR_UPLOAD} from '../../constants/strings';
+import {MainStackParams} from '../../routes';
+import {HeaderHeight, normalize, SCREEN_WIDTH} from '../../utils';
+
+type CameraScreenRouteProps = RouteProp<MainStackParams, 'CameraScreen'>;
+type CameraScreenNavigationProps = StackNavigationProp<
+ MainStackParams,
+ 'CameraScreen'
+>;
+interface CameraScreenProps {
+ route: CameraScreenRouteProps;
+ navigation: CameraScreenNavigationProps;
+}
+const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
+ const {title, screenType} = route.params;
+ const cameraRef = createRef<RNCamera>();
+ const tabBarHeight = useBottomTabBarHeight();
+ const [cameraType, setCameraType] = useState<keyof CameraType>('front');
+ const [flashMode, setFlashMode] = useState<keyof FlashMode>('off');
+ const [capturedImage, setCapturedImage] = useState<string>('');
+ const [mostRecentPhoto, setMostRecentPhoto] = useState<string>('');
+
+ // Removes bottom navigation bar on current screen and add it back when navigating away
+ useFocusEffect(
+ useCallback(() => {
+ navigation.dangerouslyGetParent()?.setOptions({
+ tabBarVisible: false,
+ });
+ return () => {
+ navigation.dangerouslyGetParent()?.setOptions({
+ tabBarVisible: true,
+ });
+ };
+ }, [navigation]),
+ );
+
+ useEffect(() => {
+ CameraRoll.getPhotos({first: 1})
+ .then((lastPhoto) => {
+ lastPhoto.edges.forEach((edge) =>
+ setMostRecentPhoto(edge.node.image.uri),
+ );
+ })
+ .catch((err) => console.log('Unable to fetch preview photo for gallery'));
+ }, [capturedImage]);
+
+ const takePicture = () => {
+ if (cameraRef !== null) {
+ cameraRef.current?.pausePreview();
+ const options = {quality: 0.5, base64: true};
+ cameraRef.current?.takePictureAsync(options).then((response) => {
+ setShowSaveButton(true);
+ setCapturedImage(response.uri);
+ });
+ }
+ };
+
+ const handleNext = () => {
+ navigation.navigate('CaptionScreen', {
+ screenType,
+ title,
+ image: {
+ filename: capturedImage,
+ path: capturedImage,
+ },
+ });
+ };
+
+ const downloadImage = () => {
+ CameraRoll.save(capturedImage, {album: 'Recents', type: 'photo'})
+ .then((_res) => Alert.alert('Saved to device!'))
+ .catch((_err) => Alert.alert('Failed to save to device!'));
+ };
+
+ const navigateToImagePicker = () => {
+ ImagePicker.openPicker({
+ smartAlbums: [
+ 'Favorites',
+ 'RecentlyAdded',
+ 'SelfPortraits',
+ 'Screenshots',
+ 'UserLibrary',
+ ],
+ mediaType: 'any',
+ })
+ .then((picture) => {
+ if ('path' in picture) {
+ navigation.navigate('ZoomInCropper', {
+ screenType,
+ title: title,
+ image: picture,
+ });
+ }
+ })
+ .catch((err) => {
+ if (err.code && err.code !== 'E_PICKER_CANCELLED') {
+ Alert.alert(ERROR_UPLOAD);
+ }
+ });
+ };
+
+ const GalleryIcon = () => (
+ <TouchableOpacity onPress={navigateToImagePicker} style={styles.saveButton}>
+ <Image
+ source={{uri: mostRecentPhoto}}
+ width={40}
+ height={40}
+ style={{borderWidth: 2, borderColor: 'white', borderRadius: 5}}
+ />
+ <Text style={styles.saveButtonLabel}>Gallery</Text>
+ </TouchableOpacity>
+ );
+
+ const FlipButton = () => (
+ <TouchableOpacity
+ onPress={() => setCameraType(cameraType === 'front' ? 'back' : 'front')}
+ style={styles.saveButton}>
+ <FlipIcon width={40} height={40} />
+ <Text style={styles.saveButtonLabel}>Flip</Text>
+ </TouchableOpacity>
+ );
+
+ const SaveButton = () => (
+ <TouchableOpacity
+ onPress={() => {
+ downloadImage();
+ }}
+ style={[styles.saveButton]}>
+ <SaveIcon width={40} height={40} />
+ <Text style={styles.saveButtonLabel}>Save</Text>
+ </TouchableOpacity>
+ );
+
+ const FlashButton = () => (
+ <TouchableOpacity
+ onPress={() => setFlashMode(flashMode === 'on' ? 'off' : 'on')}
+ style={styles.flashButtonContainer}>
+ {flashMode === 'on' ? (
+ <FlashOnIcon
+ height={30}
+ width={20}
+ color={'white'}
+ style={{zIndex: 999}}
+ />
+ ) : (
+ <FlashOffIcon
+ height={30}
+ width={20}
+ color={'white'}
+ style={{zIndex: 999}}
+ />
+ )}
+ <Text style={styles.saveButtonLabel}>Flash</Text>
+ </TouchableOpacity>
+ );
+
+ const handleClose = () => {
+ if (showSaveButton) {
+ cameraRef.current?.resumePreview();
+ setShowSaveButton(false);
+ setCapturedImage('');
+ } else {
+ navigation.goBack();
+ }
+ };
+
+ const [showSaveButton, setShowSaveButton] = useState<boolean>(false);
+
+ return (
+ <View style={styles.container}>
+ <TouchableOpacity style={styles.closeButton} onPress={handleClose}>
+ <CloseIcon height={25} width={25} color={'white'} />
+ </TouchableOpacity>
+ <FlashButton />
+ <RNCamera
+ ref={cameraRef}
+ style={{
+ flex: 1,
+ justifyContent: 'space-between',
+ }}
+ type={cameraType}
+ flashMode={flashMode}
+ />
+ <View style={[styles.bottomContainer, {bottom: tabBarHeight}]}>
+ {showSaveButton ? <SaveButton /> : <FlipButton />}
+ <TouchableOpacity
+ onPress={takePicture}
+ style={styles.captureButtonContainer}>
+ <View style={styles.captureButton} />
+ </TouchableOpacity>
+ <View
+ style={{
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: (SCREEN_WIDTH - 100) / 2,
+ }}>
+ {capturedImage ? (
+ <TaggSquareButton
+ onPress={handleNext}
+ title={'Next'}
+ buttonStyle={'large'}
+ buttonColor={'blue'}
+ labelColor={'white'}
+ style={styles.nextButton}
+ labelStyle={styles.nextButtonLabel}
+ />
+ ) : (
+ <GalleryIcon />
+ )}
+ </View>
+ </View>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ flexDirection: 'column',
+ backgroundColor: 'black',
+ },
+ preview: {
+ flex: 1,
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ },
+ captureButtonContainer: {
+ alignSelf: 'center',
+ backgroundColor: 'transparent',
+ borderRadius: 100,
+ borderWidth: 4,
+ borderColor: '#fff',
+ width: 93,
+ height: 93,
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ captureButton: {
+ backgroundColor: '#fff',
+ width: 68,
+ height: 68,
+ borderRadius: 74,
+ },
+ closeButton: {
+ position: 'absolute',
+ top: 0,
+ paddingTop: HeaderHeight,
+ zIndex: 1,
+ marginLeft: '5%',
+ },
+ bottomContainer: {
+ position: 'absolute',
+ width: SCREEN_WIDTH,
+ flexDirection: 'row',
+ justifyContent: 'center',
+ },
+ saveButton: {
+ zIndex: 1,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: (SCREEN_WIDTH - 100) / 2,
+ },
+ saveButtonLabel: {
+ color: 'white',
+ fontWeight: '700',
+ fontSize: normalize(12),
+ lineHeight: normalize(14.32),
+ marginTop: 5,
+ zIndex: 999,
+ },
+ 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',
+ },
+ flashButtonContainer: {
+ position: 'absolute',
+ backgroundColor: '#808080',
+ // opacity: 0.25,
+ zIndex: 1,
+ top: normalize(50),
+ right: 0,
+ marginRight: normalize(18),
+ height: 86,
+ width: 49,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderRadius: 30,
+ },
+});
+
+export default CameraScreen;
diff --git a/src/screens/moments/index.ts b/src/screens/moments/index.ts
index aac2ddeb..07d55192 100644
--- a/src/screens/moments/index.ts
+++ b/src/screens/moments/index.ts
@@ -1,2 +1,3 @@
export {default as TagSelectionScreen} from './TagSelectionScreen';
export {default as TagFriendsScreen} from './TagFriendsScreen';
+export {default as CameraScreen} from './CameraScreen';