diff options
author | Shravya Ramesh <shravs1208@gmail.com> | 2021-06-25 03:26:00 -0700 |
---|---|---|
committer | Shravya Ramesh <shravs1208@gmail.com> | 2021-06-25 03:26:00 -0700 |
commit | f596a0246a9b9453df3a93c8c3fc5c9137bb50fc (patch) | |
tree | 54d1a7c04955b10c281a9db4249e64275f8156f0 /src | |
parent | 0646d38547319200f7f725cdd76b1ed9b531a188 (diff) |
Create camera screen, Add to Navigator
Diffstat (limited to 'src')
-rw-r--r-- | src/routes/main/MainStackNavigator.tsx | 4 | ||||
-rw-r--r-- | src/routes/main/MainStackScreen.tsx | 8 | ||||
-rw-r--r-- | src/screens/moments/CameraScreen.tsx | 325 | ||||
-rw-r--r-- | src/screens/moments/index.ts | 1 |
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'; |