diff options
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/camera/FlashButton.tsx | 42 | ||||
-rw-r--r-- | src/components/camera/FlipButton.tsx | 29 | ||||
-rw-r--r-- | src/components/camera/GalleryIcon.tsx | 39 | ||||
-rw-r--r-- | src/components/camera/SaveButton.tsx | 26 | ||||
-rw-r--r-- | src/components/camera/index.ts | 4 | ||||
-rw-r--r-- | src/components/camera/styles.tsx | 53 | ||||
-rw-r--r-- | src/components/comments/ZoomInCropper.tsx | 4 | ||||
-rw-r--r-- | src/components/index.ts | 9 | ||||
-rw-r--r-- | src/components/moments/Moment.tsx | 116 |
9 files changed, 212 insertions, 110 deletions
diff --git a/src/components/camera/FlashButton.tsx b/src/components/camera/FlashButton.tsx new file mode 100644 index 00000000..06a4e44e --- /dev/null +++ b/src/components/camera/FlashButton.tsx @@ -0,0 +1,42 @@ +import React, {Dispatch, SetStateAction} from 'react'; +import {Text, TouchableOpacity} from 'react-native'; +import {FlashMode} from 'react-native-camera'; +import FlashOffIcon from '../../assets/icons/camera/flash-off.svg'; +import FlashOnIcon from '../../assets/icons/camera/flash-on.svg'; +import {styles} from './styles'; + +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={styles.flashIcon} + /> + ) : ( + <FlashOffIcon + height={30} + width={20} + color={'white'} + style={styles.flashIcon} + /> + )} + <Text style={styles.saveButtonLabel}>Flash</Text> + </TouchableOpacity> +); + +export default FlashButton; diff --git a/src/components/camera/FlipButton.tsx b/src/components/camera/FlipButton.tsx new file mode 100644 index 00000000..c6f710a9 --- /dev/null +++ b/src/components/camera/FlipButton.tsx @@ -0,0 +1,29 @@ +import React, {Dispatch, SetStateAction} from 'react'; +import {Text, TouchableOpacity} from 'react-native'; +import {CameraType} from 'react-native-camera'; +import FlipIcon from '../../assets/icons/camera/flip.svg'; +import {styles} from './styles'; + +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> +); + +export default FlipButton; diff --git a/src/components/camera/GalleryIcon.tsx b/src/components/camera/GalleryIcon.tsx new file mode 100644 index 00000000..8d396550 --- /dev/null +++ b/src/components/camera/GalleryIcon.tsx @@ -0,0 +1,39 @@ +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 {styles} from './styles'; + +interface GalleryIconProps { + mostRecentPhotoUri: string; + callback: (pic: ImageType) => void; +} + +/* + * Displays the most recent photo in the user's gallery + * On click, navigates to the image picker + */ +export const GalleryIcon: React.FC<GalleryIconProps> = ({ + mostRecentPhotoUri, + callback, +}) => { + return ( + <TouchableOpacity + onPress={() => navigateToImagePicker(callback)} + style={styles.saveButton}> + {mostRecentPhotoUri !== '' ? ( + <Image + source={{uri: mostRecentPhotoUri}} + width={40} + height={40} + style={styles.galleryIcon} + /> + ) : ( + <View style={styles.galleryIconEmpty} /> + )} + <Text style={styles.saveButtonLabel}>Gallery</Text> + </TouchableOpacity> + ); +}; + +export default GalleryIcon; diff --git a/src/components/camera/SaveButton.tsx b/src/components/camera/SaveButton.tsx new file mode 100644 index 00000000..0e220497 --- /dev/null +++ b/src/components/camera/SaveButton.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import {Text, TouchableOpacity} from 'react-native'; +import SaveIcon from '../../assets/icons/camera/save.svg'; +import {saveImageToGallery} from '../../utils/camera'; +import {styles} from './styles'; + +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={() => { + saveImageToGallery(capturedImageURI); + }} + style={styles.saveButton}> + <SaveIcon width={40} height={40} /> + <Text style={styles.saveButtonLabel}>Save</Text> + </TouchableOpacity> +); + +export default SaveButton; diff --git a/src/components/camera/index.ts b/src/components/camera/index.ts new file mode 100644 index 00000000..d33d1e4a --- /dev/null +++ b/src/components/camera/index.ts @@ -0,0 +1,4 @@ +export {default as GalleryIcon} from './GalleryIcon'; +export {default as FlashButton} from './FlashButton'; +export {default as FlipButton} from './FlipButton'; +export {default as SaveButton} from './SaveButton'; diff --git a/src/components/camera/styles.tsx b/src/components/camera/styles.tsx new file mode 100644 index 00000000..33b47cc4 --- /dev/null +++ b/src/components/camera/styles.tsx @@ -0,0 +1,53 @@ +import {StyleSheet} from 'react-native'; +import {normalize, SCREEN_WIDTH} from '../../utils/layouts'; + +export 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, + }, + galleryIcon: { + borderWidth: 2, + borderColor: 'white', + borderRadius: 5, + width: 40, + height: 40, + }, + galleryIconEmpty: { + borderWidth: 2, + borderColor: 'white', + borderRadius: 5, + width: 40, + height: 40, + backgroundColor: 'grey', + }, + flashIcon: { + zIndex: 2, + }, +}); diff --git a/src/components/comments/ZoomInCropper.tsx b/src/components/comments/ZoomInCropper.tsx index bca4e599..7fa88f6e 100644 --- a/src/components/comments/ZoomInCropper.tsx +++ b/src/components/comments/ZoomInCropper.tsx @@ -1,7 +1,7 @@ import {RouteProp} from '@react-navigation/core'; import {useFocusEffect} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; -import {default as React, useCallback, useEffect, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {Image, StyleSheet, TouchableOpacity} from 'react-native'; import {normalize} from 'react-native-elements'; import ImageZoom, {IOnMove} from 'react-native-image-pan-zoom'; @@ -34,7 +34,6 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ const [y0, setY0] = useState<number>(); const [y1, setY1] = useState<number>(); - // Removes bottom navigation bar on current screen and add it back when navigating away useFocusEffect( useCallback(() => { navigation.dangerouslyGetParent()?.setOptions({ @@ -80,7 +79,6 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ screenType, title: title, media: { - filename: media.filename, uri: croppedURL, isVideo: false, }, 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/components/moments/Moment.tsx b/src/components/moments/Moment.tsx index ec9129c5..108ea100 100644 --- a/src/components/moments/Moment.tsx +++ b/src/components/moments/Moment.tsx @@ -11,9 +11,9 @@ import BigPlusIcon from '../../assets/icons/plus-icon-white.svg'; import PlusIcon from '../../assets/icons/plus-icon.svg'; import UpIcon from '../../assets/icons/up_icon.svg'; import {TAGG_LIGHT_BLUE} from '../../constants'; -import {ERROR_UPLOAD} from '../../constants/strings'; import {MomentType, ScreenType} from '../../types'; import {normalize, SCREEN_WIDTH} from '../../utils'; +import {navigateToVideoPicker} from '../../utils/camera'; import MomentTile from './MomentTile'; interface MomentProps { @@ -43,115 +43,25 @@ const Moment: React.FC<MomentProps> = ({ }) => { const navigation = useNavigation(); + // TODO: remove this later, tmp solution for handling videos const navigateToCaptionScreenForVideo = (uri: string) => { - const randHash = Math.random().toString(36).substring(7); navigation.navigate('CaptionScreen', { screenType, title, media: { - filename: `poc_${randHash}.mov`, uri, isVideo: true, }, }); }; - /** - * This function opens the ImagePicker, only lets you select video files, - * formats the file extension, then makes a call to the server to get the presigned URL, - * after which it makes a POST request to the returned URL to upload the file directly to S3. - * params: none - * @returns: none - */ - const navigateToVideoPicker = () => { - ImagePicker.openPicker({ - mediaType: 'video', - }) - .then(async (vid) => { - console.log(vid); - if (vid.path) { - navigateToCaptionScreenForVideo(vid.path); - } - }) - .catch((err) => { - if (err.code && err.code !== 'E_PICKER_CANCELLED') { - Alert.alert(ERROR_UPLOAD); - } - }); - }; - const navigateToImagePicker = () => { - ImagePicker.openPicker({ - smartAlbums: [ - 'Favorites', - 'RecentlyAdded', - 'SelfPortraits', - 'Screenshots', - 'UserLibrary', - ], - mediaType: 'any', - }) - .then((picture) => { - if ( - picture.path && - picture.filename && - (picture.filename.endsWith('gif') || picture.filename.endsWith('GIF')) - ) { - showGIFFailureAlert(picture); - } else if (picture.path && picture.filename) { - navigation.navigate('ZoomInCropper', { - screenType, - title, - media: { - filename: picture.filename, - uri: picture.path, - isVideo: false, - }, - }); - } - }) - .catch((err) => { - if (err.code && err.code !== 'E_PICKER_CANCELLED') { - Alert.alert(ERROR_UPLOAD); - } - }); + const navigateToCameraScreen = () => { + navigation.navigate('CameraScreen', { + title, + screenType, + }); }; - /* Handles GIF files */ - const showGIFFailureAlert = (picture) => - Alert.alert( - 'Warning', - 'The app currently cannot handle GIFs, and will only save a static image.', - [ - { - text: 'Cancel', - onPress: () => {}, - style: 'cancel', - }, - { - text: 'Post', - onPress: () => { - navigation.navigate('ZoomInCropper', { - screenType, - title, - media: { - filename: picture.filename, - uri: picture.path, - isVideo: false, - }, - }); - }, - style: 'default', - }, - ], - { - cancelable: true, - onDismiss: () => - Alert.alert( - 'This alert was dismissed by tapping outside of the alert dialog.', - ), - }, - ); - return ( <View style={[styles.container, externalStyles?.container]}> <View style={[styles.header, externalStyles?.header]}> @@ -192,7 +102,10 @@ const Moment: React.FC<MomentProps> = ({ Alert.alert('Video Upload', 'pick one', [ { text: 'gallery', - onPress: navigateToVideoPicker, + onPress: () => + navigateToVideoPicker((vid) => + navigateToCaptionScreenForVideo(vid.path), + ), }, { text: 'camera (simulator will not work)', @@ -216,7 +129,7 @@ const Moment: React.FC<MomentProps> = ({ <PlusIcon width={23} height={23} - onPress={() => navigateToImagePicker()} + onPress={navigateToCameraScreen} color={TAGG_LIGHT_BLUE} style={styles.horizontalMargin} /> @@ -246,7 +159,7 @@ const Moment: React.FC<MomentProps> = ({ /> ))} {(images === undefined || images.length === 0) && !userXId && ( - <TouchableOpacity onPress={() => navigateToImagePicker()}> + <TouchableOpacity onPress={navigateToCameraScreen}> <LinearGradient colors={['rgba(105, 141, 211, 1)', 'rgba(105, 141, 211, 0.3)']}> <View style={styles.defaultImage}> @@ -282,9 +195,6 @@ const styles = StyleSheet.create({ color: TAGG_LIGHT_BLUE, maxWidth: '70%', }, - flexer: { - flex: 1, - }, scrollContainer: { height: SCREEN_WIDTH / 3.25, backgroundColor: '#eee', |