aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/camera/buttons.tsx149
-rw-r--r--src/components/camera/index.ts1
-rw-r--r--src/components/index.ts9
-rw-r--r--src/screens/moments/CameraScreen.tsx194
-rw-r--r--src/utils/camera.ts67
5 files changed, 263 insertions, 157 deletions
diff --git a/src/components/camera/buttons.tsx b/src/components/camera/buttons.tsx
new file mode 100644
index 00000000..321be958
--- /dev/null
+++ b/src/components/camera/buttons.tsx
@@ -0,0 +1,149 @@
+import {useNavigation} from '@react-navigation/native';
+import React, {Dispatch, SetStateAction} from 'react';
+import {Image, StyleSheet, Text, TouchableOpacity} from 'react-native';
+import {CameraType, FlashMode} from 'react-native-camera';
+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 {ScreenType} from '../../types';
+import {downloadImage, navigateToImagePicker} from '../../utils/camera';
+import {normalize, SCREEN_WIDTH} from '../../utils/layouts';
+
+interface GalleryIconProps {
+ screenType: ScreenType;
+ title: string;
+ mostRecentPhoto: string;
+}
+
+/*
+ * Displays the most recent photo in the user's gallery
+ * On click, navigates to the image picker
+ */
+export const GalleryIcon: React.FC<GalleryIconProps> = ({
+ screenType,
+ title,
+ mostRecentPhoto,
+}) => {
+ return (
+ <TouchableOpacity
+ onPress={() => navigateToImagePicker(screenType, title)}
+ 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>
+ );
+};
+
+interface FlipButtonProps {
+ setCameraType: Dispatch<SetStateAction<keyof CameraType>>;
+ cameraType: keyof CameraType;
+}
+
+/*
+ * Toggles between back camera and front camera
+ * Appears only when user has not taken a picture yet
+ * Once user takes a picture, this button disappears to reveal the save button
+ */
+export const FlipButton: React.FC<FlipButtonProps> = ({
+ setCameraType,
+ cameraType,
+}) => (
+ <TouchableOpacity
+ onPress={() => setCameraType(cameraType === 'front' ? 'back' : 'front')}
+ style={styles.saveButton}>
+ <FlipIcon width={40} height={40} />
+ <Text style={styles.saveButtonLabel}>Flip</Text>
+ </TouchableOpacity>
+);
+
+interface FlashButtonProps {
+ flashMode: keyof FlashMode;
+ setFlashMode: Dispatch<SetStateAction<keyof FlashMode>>;
+}
+
+/*
+ * Toggles between flash on/off modes
+ */
+export const FlashButton: React.FC<FlashButtonProps> = ({
+ flashMode,
+ setFlashMode,
+}) => (
+ <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>
+);
+
+interface SaveButtonProps {
+ capturedImageURI: string;
+}
+
+/*
+ * 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={() => {
+ downloadImage(capturedImageURI);
+ }}
+ style={[styles.saveButton]}>
+ <SaveIcon width={40} height={40} />
+ <Text style={styles.saveButtonLabel}>Save</Text>
+ </TouchableOpacity>
+);
+
+const styles = StyleSheet.create({
+ 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,
+ },
+ 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,
+ },
+});
diff --git a/src/components/camera/index.ts b/src/components/camera/index.ts
new file mode 100644
index 00000000..428d0fe8
--- /dev/null
+++ b/src/components/camera/index.ts
@@ -0,0 +1 @@
+export * from './buttons';
diff --git a/src/components/index.ts b/src/components/index.ts
index 47dc583b..c2f50118 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -1,9 +1,10 @@
+export * from './camera';
+export * from './comments';
export * from './common';
+export * from './messages';
+export * from './moments';
export * from './onboarding';
export * from './profile';
export * from './search';
-export * from './taggs';
-export * from './comments';
-export * from './moments';
export * from './suggestedPeople';
-export * from './messages';
+export * from './taggs';
diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx
index 70411a83..4c2633a3 100644
--- a/src/screens/moments/CameraScreen.tsx
+++ b/src/screens/moments/CameraScreen.tsx
@@ -3,28 +3,22 @@ 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 {StyleSheet, 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 {
+ FlashButton,
+ FlipButton,
+ GalleryIcon,
+ SaveButton,
+ TaggSquareButton,
+} from '../../components';
import {MainStackParams} from '../../routes';
import {HeaderHeight, normalize, SCREEN_WIDTH} from '../../utils';
+import {takePicture} from '../../utils/camera';
type CameraScreenRouteProps = RouteProp<MainStackParams, 'CameraScreen'>;
-type CameraScreenNavigationProps = StackNavigationProp<
+export type CameraScreenNavigationProps = StackNavigationProp<
MainStackParams,
'CameraScreen'
>;
@@ -40,8 +34,11 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
const [flashMode, setFlashMode] = useState<keyof FlashMode>('off');
const [capturedImage, setCapturedImage] = useState<string>('');
const [mostRecentPhoto, setMostRecentPhoto] = useState<string>('');
+ const [showSaveButton, setShowSaveButton] = useState<boolean>(false);
- // Removes bottom navigation bar on current screen and add it back when navigating away
+ /*
+ * Removes bottom navigation bar on current screen and add it back when navigating away
+ */
useFocusEffect(
useCallback(() => {
navigation.dangerouslyGetParent()?.setOptions({
@@ -55,6 +52,9 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
}, [navigation]),
);
+ /*
+ * Chooses the last picture from gallery to display as the gallery button icon
+ */
useEffect(() => {
CameraRoll.getPhotos({first: 1})
.then((lastPhoto) => {
@@ -62,20 +62,14 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
setMostRecentPhoto(edge.node.image.uri),
);
})
- .catch((err) => console.log('Unable to fetch preview photo for gallery'));
+ .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);
- });
- }
- };
-
+ /*
+ * Appears once a picture has been captured to navigate to the caption screen
+ */
const handleNext = () => {
navigation.navigate('CaptionScreen', {
screenType,
@@ -87,94 +81,10 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
});
};
- 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>
- );
-
+ /*
+ * If picture is not taken yet, exists from camera screen to profile view
+ * If picture is taken, exists from captured image's preview to camera
+ * */
const handleClose = () => {
if (showSaveButton) {
cameraRef.current?.resumePreview();
@@ -185,14 +95,12 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
}
};
- 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 />
+ <FlashButton flashMode={flashMode} setFlashMode={setFlashMode} />
<RNCamera
ref={cameraRef}
style={{
@@ -203,9 +111,15 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
flashMode={flashMode}
/>
<View style={[styles.bottomContainer, {bottom: tabBarHeight}]}>
- {showSaveButton ? <SaveButton /> : <FlipButton />}
+ {showSaveButton ? (
+ <SaveButton capturedImageURI={capturedImage} />
+ ) : (
+ <FlipButton cameraType={cameraType} setCameraType={setCameraType} />
+ )}
<TouchableOpacity
- onPress={takePicture}
+ onPress={() =>
+ takePicture(cameraRef, setShowSaveButton, setCapturedImage)
+ }
style={styles.captureButtonContainer}>
<View style={styles.captureButton} />
</TouchableOpacity>
@@ -227,7 +141,11 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
labelStyle={styles.nextButtonLabel}
/>
) : (
- <GalleryIcon />
+ <GalleryIcon
+ mostRecentPhoto={mostRecentPhoto}
+ screenType={screenType}
+ title={title}
+ />
)}
</View>
</View>
@@ -277,21 +195,6 @@ const styles = StyleSheet.create({
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),
@@ -305,21 +208,6 @@ const styles = StyleSheet.create({
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/utils/camera.ts b/src/utils/camera.ts
new file mode 100644
index 00000000..fc2471e5
--- /dev/null
+++ b/src/utils/camera.ts
@@ -0,0 +1,67 @@
+import CameraRoll from '@react-native-community/cameraroll';
+import {useNavigation} from '@react-navigation/native';
+import {Dispatch, RefObject, SetStateAction} from 'react';
+import {Alert} from 'react-native';
+import {Orientation, RNCamera} from 'react-native-camera';
+import ImagePicker from 'react-native-image-crop-picker';
+import {ScreenType} from 'src/types';
+import {ERROR_UPLOAD} from '../constants/strings';
+
+/*
+ * Captures a photo and pauses to shoe the preview of the picture taken
+ */
+export const takePicture = (
+ cameraRef: RefObject<RNCamera>,
+ setShowSaveButton: Dispatch<SetStateAction<boolean>>,
+ setCapturedImage: Dispatch<SetStateAction<string>>,
+) => {
+ if (cameraRef !== null) {
+ cameraRef.current?.pausePreview();
+ const options = {
+ forceUpOrientation: true,
+ quality: 0.5,
+ base64: true,
+ };
+ cameraRef.current?.takePictureAsync(options).then((response) => {
+ setShowSaveButton(true);
+ setCapturedImage(response.uri);
+ });
+ }
+};
+
+export const downloadImage = (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 = (
+ screenType: ScreenType,
+ title: string,
+) => {
+ const navigation = useNavigation();
+ ImagePicker.openPicker({
+ smartAlbums: [
+ 'Favorites',
+ 'RecentlyAdded',
+ 'SelfPortraits',
+ 'Screenshots',
+ 'UserLibrary',
+ ],
+ mediaType: 'any',
+ })
+ .then((picture) => {
+ if ('path' in picture) {
+ navigation.navigate('ZoomInCropper', {
+ screenType,
+ title,
+ image: picture,
+ });
+ }
+ })
+ .catch((err) => {
+ if (err.code && err.code !== 'E_PICKER_CANCELLED') {
+ Alert.alert(ERROR_UPLOAD);
+ }
+ });
+};