From 2f3244dfa11cc23b804930ad448222bbff4f022a Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 13:55:28 -0400 Subject: Squashed commit of the following: commit 66c974161b59f1e3570e2a4a42334fabc16c2129 Merge: 53bdc94c d4b21051 Author: Ivan Chen Date: Tue Jun 29 17:06:19 2021 -0400 Merge pull request #476 from shravyaramesh/tma937-tagg-camera [TMA-937] Tagg Camera commit d4b210518eaffd3bf1320ca7ce7fa4a6d611528f Author: Ivan Chen Date: Tue Jun 29 17:04:10 2021 -0400 Cleanup code, Update camera options commit 3f826ec0741d3f0d0c85a17e5d0a09eef402dbf2 Author: Ivan Chen Date: Tue Jun 29 16:45:45 2021 -0400 Set to only allow photos commit 9d30c0c211e6b0b1b87e5de93a043e6e9f06beb3 Author: Ivan Chen Date: Tue Jun 29 16:44:41 2021 -0400 Cleanup code, Fix gallery icon bug commit f6fdd5d913c29855644f226d09d6cba60faf6e21 Author: Ivan Chen Date: Tue Jun 29 16:32:19 2021 -0400 Add error handling commit 5fcffd40746b2074d523f53dc82c824d147444e5 Author: Ivan Chen Date: Tue Jun 29 16:29:06 2021 -0400 Refactor buttons commit f273a7aa1c2e27692c2a03ae1e2fc9b81360558d Author: Shravya Ramesh Date: Fri Jun 25 17:18:47 2021 -0700 Fix lint errors commit 448e91ed0b6c7519c02bbe1ac32a9d51989679db Author: Shravya Ramesh Date: Fri Jun 25 16:58:05 2021 -0700 Fix lint errors commit 6f94f0bb6dbe12e23f4222a0d0e3ffb09af965d7 Author: Shravya Ramesh Date: Fri Jun 25 16:50:13 2021 -0700 Add missing description for permissions commit 727c6384a2a07c42cd132d02da8c7dbb5757ea4f Author: Shravya Ramesh Date: Fri Jun 25 16:50:00 2021 -0700 Refactor code, Fix orientation bug commit f596a0246a9b9453df3a93c8c3fc5c9137bb50fc Author: Shravya Ramesh Date: Fri Jun 25 03:26:00 2021 -0700 Create camera screen, Add to Navigator commit 0646d38547319200f7f725cdd76b1ed9b531a188 Author: Shravya Ramesh Date: Fri Jun 25 03:24:54 2021 -0700 Navigate to camera screen, Move image picker funct commit f0762b7a3171f99833eb3c3f5e723c472dbc4879 Author: Shravya Ramesh Date: Fri Jun 25 03:23:56 2021 -0700 Add assets for camera screen commit 3c4676b7646fbddc43bf5d9796b7cbac185b6664 Author: Shravya Ramesh Date: Fri Jun 25 03:23:33 2021 -0700 Add package, install for camera lib --- ios/Frontend/Info.plist | 6 + ios/Podfile.lock | 12 ++ package.json | 1 + src/assets/icons/camera/flash-off.svg | 1 + src/assets/icons/camera/flash-on.svg | 1 + src/assets/icons/camera/flip.svg | 1 + src/assets/icons/camera/save.svg | 1 + src/components/camera/FlashButton.tsx | 42 +++++++ src/components/camera/FlipButton.tsx | 29 +++++ src/components/camera/GalleryIcon.tsx | 43 +++++++ src/components/camera/SaveButton.tsx | 26 ++++ src/components/camera/index.ts | 4 + src/components/camera/styles.tsx | 53 ++++++++ src/components/index.ts | 9 +- src/components/moments/Moment.tsx | 13 +- src/routes/main/MainStackNavigator.tsx | 4 + src/routes/main/MainStackScreen.tsx | 8 ++ src/screens/moments/CameraScreen.tsx | 215 +++++++++++++++++++++++++++++++++ src/screens/moments/index.ts | 1 + src/utils/camera.ts | 65 ++++++++++ yarn.lock | 7 ++ 21 files changed, 533 insertions(+), 9 deletions(-) create mode 100644 src/assets/icons/camera/flash-off.svg create mode 100644 src/assets/icons/camera/flash-on.svg create mode 100644 src/assets/icons/camera/flip.svg create mode 100644 src/assets/icons/camera/save.svg create mode 100644 src/components/camera/FlashButton.tsx create mode 100644 src/components/camera/FlipButton.tsx create mode 100644 src/components/camera/GalleryIcon.tsx create mode 100644 src/components/camera/SaveButton.tsx create mode 100644 src/components/camera/index.ts create mode 100644 src/components/camera/styles.tsx create mode 100644 src/screens/moments/CameraScreen.tsx create mode 100644 src/utils/camera.ts diff --git a/ios/Frontend/Info.plist b/ios/Frontend/Info.plist index fa67d073..94734c40 100644 --- a/ios/Frontend/Info.plist +++ b/ios/Frontend/Info.plist @@ -56,6 +56,12 @@ LaunchScreen NSPhotoLibraryUsageDescription This lets you share photos from your library and select profile displays + NSPhotoLibraryAddUsageDescription + This lets you save photos captured on Tagg, to your library + NSCameraUsageDescription + Enable camera access to capture and share moment with your friends + NSMicrophoneUsageDescription + Enable microphone access to record and listen to videos UIAppFonts Feather.ttf diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 15ec026f..8eed7569 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -329,6 +329,14 @@ PODS: - React-jsinspector (0.63.3) - react-native-blur (0.8.0): - React + - react-native-camera (3.44.1): + - React-Core + - react-native-camera/RCT (= 3.44.1) + - react-native-camera/RN (= 3.44.1) + - react-native-camera/RCT (3.44.1): + - React-Core + - react-native-camera/RN (3.44.1): + - React-Core - react-native-cameraroll (4.0.4): - React-Core - react-native-contacts (6.0.5): @@ -534,6 +542,7 @@ DEPENDENCIES: - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - "react-native-blur (from `../node_modules/@react-native-community/blur`)" + - react-native-camera (from `../node_modules/react-native-camera`) - "react-native-cameraroll (from `../node_modules/@react-native-community/cameraroll`)" - react-native-contacts (from `../node_modules/react-native-contacts`) - react-native-date-picker (from `../node_modules/react-native-date-picker`) @@ -638,6 +647,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/jsinspector" react-native-blur: :path: "../node_modules/@react-native-community/blur" + react-native-camera: + :path: "../node_modules/react-native-camera" react-native-cameraroll: :path: "../node_modules/@react-native-community/cameraroll" react-native-contacts: @@ -758,6 +769,7 @@ SPEC CHECKSUMS: React-jsiexecutor: b56c03e61c0dd5f5801255f2160a815f4a53d451 React-jsinspector: 8e68ffbfe23880d3ee9bafa8be2777f60b25cbe2 react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c + react-native-camera: 7bf59f2728ffb019fa25e60a225d2c57e1a8f0f6 react-native-cameraroll: 88f4e62d9ecd0e1f253abe4f685474f2ea14bfa2 react-native-contacts: 931baebf460125c5a7bbce1c4521a96c69795123 react-native-date-picker: 96a07ca27a6225da8a3935324d85046028456b0f diff --git a/package.json b/package.json index 09ed6fc5..63203775 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "react-moment": "^1.0.0", "react-native": "0.63.3", "react-native-animatable": "^1.3.3", + "react-native-camera": "^3.44.1", "react-native-confirmation-code-field": "^6.5.0", "react-native-contacts": "^6.0.4", "react-native-controlled-mentions": "^2.2.5", diff --git a/src/assets/icons/camera/flash-off.svg b/src/assets/icons/camera/flash-off.svg new file mode 100644 index 00000000..fb04efd2 --- /dev/null +++ b/src/assets/icons/camera/flash-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/camera/flash-on.svg b/src/assets/icons/camera/flash-on.svg new file mode 100644 index 00000000..b4608b75 --- /dev/null +++ b/src/assets/icons/camera/flash-on.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/camera/flip.svg b/src/assets/icons/camera/flip.svg new file mode 100644 index 00000000..e2ef1a0c --- /dev/null +++ b/src/assets/icons/camera/flip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/camera/save.svg b/src/assets/icons/camera/save.svg new file mode 100644 index 00000000..6a28fb55 --- /dev/null +++ b/src/assets/icons/camera/save.svg @@ -0,0 +1 @@ + \ No newline at end of file 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>; +} + +/* + * Toggles between flash on/off modes + */ +export const FlashButton: React.FC = ({ + flashMode, + setFlashMode, +}) => ( + setFlashMode(flashMode === 'on' ? 'off' : 'on')} + style={styles.flashButtonContainer}> + {flashMode === 'on' ? ( + + ) : ( + + )} + Flash + +); + +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>; + 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 = ({ + setCameraType, + cameraType, +}) => ( + setCameraType(cameraType === 'front' ? 'back' : 'front')} + style={styles.saveButton}> + + Flip + +); + +export default FlipButton; diff --git a/src/components/camera/GalleryIcon.tsx b/src/components/camera/GalleryIcon.tsx new file mode 100644 index 00000000..c49ace7d --- /dev/null +++ b/src/components/camera/GalleryIcon.tsx @@ -0,0 +1,43 @@ +import {useNavigation} from '@react-navigation/native'; +import React from 'react'; +import {Image, Text, TouchableOpacity, View} from 'react-native'; +import {ScreenType} from '../../types'; +import {navigateToImagePicker} from '../../utils/camera'; +import {styles} from './styles'; + +interface GalleryIconProps { + screenType: ScreenType; + title: string; + mostRecentPhotoUri: string; +} + +/* + * Displays the most recent photo in the user's gallery + * On click, navigates to the image picker + */ +export const GalleryIcon: React.FC = ({ + screenType, + title, + mostRecentPhotoUri, +}) => { + const navigation = useNavigation(); + return ( + navigateToImagePicker(navigation, screenType, title)} + style={styles.saveButton}> + {mostRecentPhotoUri !== '' ? ( + + ) : ( + + )} + Gallery + + ); +}; + +export default GalleryIcon; diff --git a/src/components/camera/SaveButton.tsx b/src/components/camera/SaveButton.tsx new file mode 100644 index 00000000..840cc804 --- /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 {downloadImage} 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 = ({capturedImageURI}) => ( + { + downloadImage(capturedImageURI); + }} + style={styles.saveButton}> + + Save + +); + +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/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 a43a2830..b080ca4a 100644 --- a/src/components/moments/Moment.tsx +++ b/src/components/moments/Moment.tsx @@ -1,9 +1,8 @@ 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 ImagePicker from 'react-native-image-crop-picker'; import LinearGradient from 'react-native-linear-gradient'; import DeleteIcon from '../../assets/icons/delete-logo.svg'; import DownIcon from '../../assets/icons/down_icon.svg'; @@ -11,7 +10,6 @@ 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 MomentTile from './MomentTile'; @@ -108,6 +106,11 @@ const Moment: React.FC = ({ Alert.alert(ERROR_UPLOAD); } }); + const navigateToCameraScreen = () => { + navigation.navigate('CameraScreen', { + title, + screenType, + }); }; return ( @@ -174,7 +177,7 @@ const Moment: React.FC = ({ navigateToImagePicker()} + onPress={() => navigateToCameraScreen()} color={TAGG_LIGHT_BLUE} style={styles.horizontalMargin} /> @@ -204,7 +207,7 @@ const Moment: React.FC = ({ /> ))} {(images === undefined || images.length === 0) && !userXId && ( - navigateToImagePicker()}> + navigateToCameraScreen()}> diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx index c518d75e..21430d7a 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(); 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 = ({route}) => { gestureEnabled: false, }} /> + ); }; diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx new file mode 100644 index 00000000..c6ed1116 --- /dev/null +++ b/src/screens/moments/CameraScreen.tsx @@ -0,0 +1,215 @@ +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 {StyleSheet, TouchableOpacity, View} from 'react-native'; +import {CameraType, FlashMode, RNCamera} from 'react-native-camera'; +import CloseIcon from '../../assets/ionicons/close-outline.svg'; +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; +export type CameraScreenNavigationProps = StackNavigationProp< + MainStackParams, + 'CameraScreen' +>; +interface CameraScreenProps { + route: CameraScreenRouteProps; + navigation: CameraScreenNavigationProps; +} +const CameraScreen: React.FC = ({route, navigation}) => { + const {title, screenType} = route.params; + const cameraRef = createRef(); + const tabBarHeight = useBottomTabBarHeight(); + const [cameraType, setCameraType] = useState('front'); + const [flashMode, setFlashMode] = useState('off'); + const [capturedImage, setCapturedImage] = useState(''); + const [mostRecentPhoto, setMostRecentPhoto] = useState(''); + const [showSaveButton, setShowSaveButton] = useState(false); + + /* + * 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]), + ); + + /* + * Chooses the last picture from gallery to display as the gallery button icon + */ + useEffect(() => { + CameraRoll.getPhotos({first: 1}) + .then((lastPhoto) => { + if (lastPhoto.edges.length > 0) { + const image = lastPhoto.edges[0].node.image; + setMostRecentPhoto(image.uri); + } + }) + .catch((_err) => + console.log('Unable to fetch preview photo for gallery'), + ); + }, [capturedImage]); + + /* + * Appears once a picture has been captured to navigate to the caption screen + */ + const handleNext = () => { + navigation.navigate('CaptionScreen', { + screenType, + title, + image: { + filename: capturedImage, + path: capturedImage, + }, + }); + }; + + /* + * 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(); + setShowSaveButton(false); + setCapturedImage(''); + } else { + navigation.goBack(); + } + }; + + return ( + + + + + + + + {showSaveButton ? ( + + ) : ( + + )} + + takePicture(cameraRef, setShowSaveButton, setCapturedImage) + } + style={styles.captureButtonContainer}> + + + + {capturedImage ? ( + + ) : ( + + )} + + + + ); +}; + +const styles = StyleSheet.create({ + camera: { + flex: 1, + justifyContent: 'space-between', + }, + 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', + }, + bottomRightContainer: { + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + width: (SCREEN_WIDTH - 100) / 2, + }, + 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', + }, +}); + +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'; diff --git a/src/utils/camera.ts b/src/utils/camera.ts new file mode 100644 index 00000000..73461ad7 --- /dev/null +++ b/src/utils/camera.ts @@ -0,0 +1,65 @@ +import CameraRoll from '@react-native-community/cameraroll'; +import {Dispatch, RefObject, SetStateAction} from 'react'; +import {Alert} from 'react-native'; +import {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, + setShowSaveButton: Dispatch>, + setCapturedImage: Dispatch>, +) => { + if (cameraRef !== null) { + cameraRef.current?.pausePreview(); + const options = { + forceUpOrientation: true, + writeExif: false, + }; + 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 = ( + navigation: any, + screenType: ScreenType, + title: string, +) => { + ImagePicker.openPicker({ + smartAlbums: [ + 'Favorites', + 'RecentlyAdded', + 'SelfPortraits', + 'Screenshots', + 'UserLibrary', + ], + mediaType: 'photo', + }) + .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); + } + }); +}; diff --git a/yarn.lock b/yarn.lock index 9b425078..716b345a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6427,6 +6427,13 @@ react-native-animatable@^1.3.3: dependencies: prop-types "^15.7.2" +react-native-camera@^3.44.1: + version "3.44.1" + resolved "https://registry.yarnpkg.com/react-native-camera/-/react-native-camera-3.44.1.tgz#60e7d60fe778c1fc59d0579b64c63c3c1a59865a" + integrity sha512-B95RL3laK2v8R7L/37v28MYcEcwsM/mS94h6EZuRMLH5HFolkAwh7zJo+UAn7FG9eFtAdBwIM6s9OqYudTVO4Q== + dependencies: + prop-types "^15.6.2" + react-native-confirmation-code-field@^6.5.0: version "6.7.0" resolved "https://registry.yarnpkg.com/react-native-confirmation-code-field/-/react-native-confirmation-code-field-6.7.0.tgz#81f5e646898addb3243cf89d41d884b0762ae962" -- cgit v1.2.3-70-g09d2 From 3b7297189633cda8b886fa06f4b9d4787b6aa7c7 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 14:27:25 -0400 Subject: Clean up code, Remove tab bar logic to use modal style instead --- src/components/camera/GalleryIcon.tsx | 14 ++++++- src/components/comments/ZoomInCropper.tsx | 17 +-------- src/components/moments/Moment.tsx | 62 ++++--------------------------- src/routes/main/MainStackScreen.tsx | 14 +------ src/screens/moments/CameraScreen.tsx | 37 +++++------------- src/utils/camera.ts | 54 ++++++++++++++++----------- 6 files changed, 66 insertions(+), 132 deletions(-) diff --git a/src/components/camera/GalleryIcon.tsx b/src/components/camera/GalleryIcon.tsx index c49ace7d..bc8b1d41 100644 --- a/src/components/camera/GalleryIcon.tsx +++ b/src/components/camera/GalleryIcon.tsx @@ -23,7 +23,19 @@ export const GalleryIcon: React.FC = ({ const navigation = useNavigation(); return ( navigateToImagePicker(navigation, screenType, title)} + onPress={() => + navigateToImagePicker((pic) => + navigation.navigate('ZoomInCropper', { + screenType, + title, + media: { + filename: pic.filename, + uri: pic.path, + isVideo: false, + }, + }), + ) + } style={styles.saveButton}> {mostRecentPhotoUri !== '' ? ( = ({ const [y0, setY0] = useState(); const [y1, setY1] = useState(); - // 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]), - ); - // Setting original aspect ratio of image useEffect(() => { if (media.uri) { diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx index b080ca4a..25d69fba 100644 --- a/src/components/moments/Moment.tsx +++ b/src/components/moments/Moment.tsx @@ -1,8 +1,9 @@ import {useNavigation} from '@react-navigation/native'; import React from 'react'; -import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'; +import {Alert, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'; import {Text} from 'react-native-animatable'; import {ScrollView, TouchableOpacity} from 'react-native-gesture-handler'; +import ImagePicker from 'react-native-image-crop-picker'; import LinearGradient from 'react-native-linear-gradient'; import DeleteIcon from '../../assets/icons/delete-logo.svg'; import DownIcon from '../../assets/icons/down_icon.svg'; @@ -12,6 +13,7 @@ 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 MomentTile from './MomentTile'; interface MomentProps { @@ -53,59 +55,6 @@ const Moment: React.FC = ({ }, }); }; - /** - * 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) { - 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, @@ -153,7 +102,10 @@ const Moment: React.FC = ({ Alert.alert('Video Upload', 'pick one', [ { text: 'gallery', - onPress: navigateToVideoPicker, + onPress: () => + navigateToVideoPicker((vid) => + navigateToCaptionScreenForVideo(vid.path), + ), }, { text: 'camera (simulator will not work)', diff --git a/src/routes/main/MainStackScreen.tsx b/src/routes/main/MainStackScreen.tsx index f6adeab1..65a695f5 100644 --- a/src/routes/main/MainStackScreen.tsx +++ b/src/routes/main/MainStackScreen.tsx @@ -332,6 +332,7 @@ const MainStackScreen: React.FC = ({route}) => { name="ZoomInCropper" component={ZoomInCropper} options={{ + ...modalStyle, gestureEnabled: false, }} /> @@ -339,6 +340,7 @@ const MainStackScreen: React.FC = ({route}) => { name="CameraScreen" component={CameraScreen} options={{ + ...modalStyle, gestureEnabled: false, }} /> @@ -407,18 +409,6 @@ const styles = StyleSheet.create({ letterSpacing: normalize(1.3), fontWeight: '700', }, - whiteHeaderTitle: { - fontSize: normalize(16), - letterSpacing: normalize(1.3), - fontWeight: '700', - color: 'white', - }, - blackHeaderTitle: { - fontSize: normalize(16), - letterSpacing: normalize(1.3), - fontWeight: '700', - color: 'black', - }, }); export default MainStackScreen; diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx index c6ed1116..1826f9d7 100644 --- a/src/screens/moments/CameraScreen.tsx +++ b/src/screens/moments/CameraScreen.tsx @@ -1,8 +1,8 @@ import CameraRoll from '@react-native-community/cameraroll'; import {useBottomTabBarHeight} from '@react-navigation/bottom-tabs'; -import {RouteProp, useFocusEffect} from '@react-navigation/core'; +import {RouteProp} from '@react-navigation/core'; import {StackNavigationProp} from '@react-navigation/stack'; -import React, {createRef, useCallback, useEffect, useState} from 'react'; +import React, {createRef, useEffect, useState} from 'react'; import {StyleSheet, TouchableOpacity, View} from 'react-native'; import {CameraType, FlashMode, RNCamera} from 'react-native-camera'; import CloseIcon from '../../assets/ionicons/close-outline.svg'; @@ -36,22 +36,6 @@ const CameraScreen: React.FC = ({route, navigation}) => { const [mostRecentPhoto, setMostRecentPhoto] = useState(''); const [showSaveButton, setShowSaveButton] = useState(false); - /* - * 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]), - ); - /* * Chooses the last picture from gallery to display as the gallery button icon */ @@ -75,9 +59,10 @@ const CameraScreen: React.FC = ({route, navigation}) => { navigation.navigate('CaptionScreen', { screenType, title, - image: { - filename: capturedImage, - path: capturedImage, + media: { + filename: 'dont have that info', + uri: capturedImage, + isVideo: false, // TODO: false for now }, }); }; @@ -116,7 +101,10 @@ const CameraScreen: React.FC = ({route, navigation}) => { )} - takePicture(cameraRef, setShowSaveButton, setCapturedImage) + takePicture(cameraRef, (pic) => { + setShowSaveButton(true); + setCapturedImage(pic.uri); + }) } style={styles.captureButtonContainer}> @@ -155,11 +143,6 @@ const styles = StyleSheet.create({ flexDirection: 'column', backgroundColor: 'black', }, - preview: { - flex: 1, - justifyContent: 'flex-end', - alignItems: 'center', - }, captureButtonContainer: { alignSelf: 'center', backgroundColor: 'transparent', diff --git a/src/utils/camera.ts b/src/utils/camera.ts index 73461ad7..1ee5dbf4 100644 --- a/src/utils/camera.ts +++ b/src/utils/camera.ts @@ -1,9 +1,8 @@ import CameraRoll from '@react-native-community/cameraroll'; -import {Dispatch, RefObject, SetStateAction} from 'react'; +import {RefObject} from 'react'; import {Alert} from 'react-native'; -import {RNCamera} from 'react-native-camera'; -import ImagePicker from 'react-native-image-crop-picker'; -import {ScreenType} from 'src/types'; +import {RNCamera, TakePictureResponse} from 'react-native-camera'; +import ImagePicker, {Image, Video} from 'react-native-image-crop-picker'; import {ERROR_UPLOAD} from '../constants/strings'; /* @@ -11,8 +10,7 @@ import {ERROR_UPLOAD} from '../constants/strings'; */ export const takePicture = ( cameraRef: RefObject, - setShowSaveButton: Dispatch>, - setCapturedImage: Dispatch>, + callback: (pic: TakePictureResponse) => void, ) => { if (cameraRef !== null) { cameraRef.current?.pausePreview(); @@ -20,9 +18,8 @@ export const takePicture = ( forceUpOrientation: true, writeExif: false, }; - cameraRef.current?.takePictureAsync(options).then((response) => { - setShowSaveButton(true); - setCapturedImage(response.uri); + cameraRef.current?.takePictureAsync(options).then((pic) => { + callback(pic); }); } }; @@ -33,11 +30,7 @@ export const downloadImage = (capturedImageURI: string) => { .catch((_err) => Alert.alert('Failed to save to device!')); }; -export const navigateToImagePicker = ( - navigation: any, - screenType: ScreenType, - title: string, -) => { +export const navigateToImagePicker = (callback: (pic: Image) => void) => { ImagePicker.openPicker({ smartAlbums: [ 'Favorites', @@ -48,13 +41,32 @@ export const navigateToImagePicker = ( ], mediaType: 'photo', }) - .then((picture) => { - if ('path' in picture) { - navigation.navigate('ZoomInCropper', { - screenType, - title, - image: picture, - }); + .then((pic) => { + if (pic.path && pic.filename) { + callback(pic); + } + }) + .catch((err) => { + if (err.code && err.code !== 'E_PICKER_CANCELLED') { + Alert.alert(ERROR_UPLOAD); + } + }); +}; + +/** + * 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 + */ +export const navigateToVideoPicker = (callback: (vid: Video) => void) => { + ImagePicker.openPicker({ + mediaType: 'video', + }) + .then(async (vid) => { + if (vid.path) { + callback(vid); } }) .catch((err) => { -- cgit v1.2.3-70-g09d2 From ba0ea15092e64929f2a3f9f88f3245e476dfcbe1 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 14:28:38 -0400 Subject: Clean up import --- src/components/comments/ZoomInCropper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/comments/ZoomInCropper.tsx b/src/components/comments/ZoomInCropper.tsx index 2fc0fd44..cfd3cbbc 100644 --- a/src/components/comments/ZoomInCropper.tsx +++ b/src/components/comments/ZoomInCropper.tsx @@ -1,6 +1,6 @@ import {RouteProp} from '@react-navigation/core'; import {StackNavigationProp} from '@react-navigation/stack'; -import {default as React, useEffect, useState} from 'react'; +import React, {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'; -- cgit v1.2.3-70-g09d2 From 4b90c999d3ee45368108851eeb1cdc634e5ec499 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 14:32:55 -0400 Subject: Organize plist key values --- ios/Frontend/Info.plist | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/ios/Frontend/Info.plist b/ios/Frontend/Info.plist index 94734c40..0b1f27bf 100644 --- a/ios/Frontend/Info.plist +++ b/ios/Frontend/Info.plist @@ -54,14 +54,6 @@ NSMainNibFile LaunchScreen - NSPhotoLibraryUsageDescription - This lets you share photos from your library and select profile displays - NSPhotoLibraryAddUsageDescription - This lets you save photos captured on Tagg, to your library - NSCameraUsageDescription - Enable camera access to capture and share moment with your friends - NSMicrophoneUsageDescription - Enable microphone access to record and listen to videos UIAppFonts Feather.ttf @@ -82,6 +74,10 @@ UIInterfaceOrientationPortrait + NSPhotoLibraryUsageDescription + This lets you share photos from your library and select profile displays + NSPhotoLibraryAddUsageDescription + This lets you save photos captured on Tagg, to your library NSCameraUsageDescription This lets you share photos/videos from your camera for a Moment post on your profile! UIViewControllerBasedStatusBarAppearance -- cgit v1.2.3-70-g09d2 From d22e3508a4122575c064bbcb2df94a6e07bce7eb Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 14:34:28 -0400 Subject: Update lock files --- ios/Podfile.lock | 4 ++-- yarn.lock | 54 +++++++++++++++++++++++++++--------------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 8eed7569..19df8c81 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -455,7 +455,7 @@ PODS: - React-Core - React-RCTImage - TOCropViewController - - RNInAppBrowser (3.5.1): + - RNInAppBrowser (3.6.1): - React-Core - RNReactNativeHapticFeedback (1.11.0): - React-Core @@ -801,7 +801,7 @@ SPEC CHECKSUMS: RNFS: 3ab21fa6c56d65566d1fb26c2228e2b6132e5e32 RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211 RNImageCropPicker: 35a3ceb837446fa11547704709bb22b5fac6d584 - RNInAppBrowser: 48b95ba7a4eaff5cc223bca338d3e319561dbd1b + RNInAppBrowser: 0523b3c15501fb8b54b4f32905d2e71ca902d914 RNReactNativeHapticFeedback: 653a8c126a0f5e88ce15ffe280b3ff37e1fbb285 RNReanimated: b9c929bfff7dedc9c89ab1875f1c6151023358d9 RNScreens: f7ad633b2e0190b77b6a7aab7f914fad6f198d8d diff --git a/yarn.lock b/yarn.lock index 716b345a..8dff89cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1278,9 +1278,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.11.1" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.1.tgz#654f6c4f67568e24c23b367e947098c6206fa639" - integrity sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw== + version "7.14.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.0.tgz#a34277cf8acbd3185ea74129e1f100491eb1da7f" + integrity sha512-IilJZ1hJBUZwMOVDNTdflOOLzJB/ZtljYVa7k3gEZN/jqIJIPkWHC6dvbX+DD2CwZDHB9wAKzZPzzqMIkW37/w== dependencies: "@babel/types" "^7.3.0" @@ -1342,9 +1342,9 @@ "@types/node" "*" "@types/node@*": - version "15.12.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.4.tgz#e1cf817d70a1e118e81922c4ff6683ce9d422e26" - integrity sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA== + version "15.12.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.5.tgz#9a78318a45d75c9523d2396131bd3cca54b2d185" + integrity sha512-se3yX7UHv5Bscf8f1ERKvQOD6sTyycH3hdaoozvaLxgUiY5lIGEeH37AD0G0Qi9kPqihPn0HOfd2yaIEN9VwEg== "@types/prop-types@*": version "15.7.3" @@ -2164,9 +2164,9 @@ camelcase@^5.0.0, camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== caniuse-lite@^1.0.30001219: - version "1.0.30001239" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001239.tgz#66e8669985bb2cb84ccb10f68c25ce6dd3e4d2b8" - integrity sha512-cyBkXJDMeI4wthy8xJ2FvDU6+0dtcZSJW3voUF8+e9f1bBeuvyZfc3PNbkOETyhbR+dGCPzn9E7MA3iwzusOhQ== + version "1.0.30001241" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001241.tgz#cd3fae47eb3d7691692b406568d7a3e5b23c7598" + integrity sha512-1uoSZ1Pq1VpH0WerIMqwptXHNNGfdl7d1cJUFs80CwQ/lVzdhTvsFZCeNFslze7AjsQnb4C85tzclPa1VShbeQ== capture-exit@^2.0.0: version "2.0.0" @@ -2479,17 +2479,17 @@ copy-descriptor@^0.1.0: integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-js-compat@^3.14.0: - version "3.15.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.15.1.tgz#1afe233716d37ee021956ef097594071b2b585a7" - integrity sha512-xGhzYMX6y7oEGQGAJmP2TmtBLvR4nZmRGEcFa3ubHOq5YEp51gGN9AovVa0AoujGZIq+Wm6dISiYyGNfdflYww== + version "3.15.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.15.2.tgz#47272fbb479880de14b4e6081f71f3492f5bd3cb" + integrity sha512-Wp+BJVvwopjI+A1EFqm2dwUmWYXrvucmtIB2LgXn/Rb+gWPKYxtmb4GKHGKG/KGF1eK9jfjzT38DITbTOCX/SQ== dependencies: browserslist "^4.16.6" semver "7.0.0" core-js-pure@^3.15.0: - version "3.15.1" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.15.1.tgz#8356f6576fa2aa8e54ca6fe02620968ff010eed7" - integrity sha512-OZuWHDlYcIda8sJLY4Ec6nWq2hRjlyCqCZ+jCflyleMkVt3tPedDVErvHslyS2nbO+SlBFMSBJYvtLMwxnrzjA== + version "3.15.2" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.15.2.tgz#c8e0874822705f3385d3197af9348f7c9ae2e3ce" + integrity sha512-D42L7RYh1J2grW8ttxoY1+17Y4wXZeKe7uyplAI3FkNQyI5OgBIAjUfFiTPfL1rs0qLpxaabITNbjKl1Sp82tA== core-js@^1.0.0: version "1.2.7" @@ -2830,9 +2830,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.723: - version "1.3.756" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.756.tgz#942cee59cd64d19f576d8d5804eef09cb423740c" - integrity sha512-WsmJym1TMeHVndjPjczTFbnRR/c4sbzg8fBFtuhlb2Sru3i/S1VGpzDSrv/It8ctMU2bj8G7g7/O3FzYMGw6eA== + version "1.3.762" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.762.tgz#3fa4e3bcbda539b50e3aa23041627063a5cffe61" + integrity sha512-LehWjRpfPcK8F1Lf/NZoAwWLWnjJVo0SZeQ9j/tvnBWYcT99qDqgo4raAfS2oTKZjPrR/jxruh85DGgDUmywEA== eme-encryption-scheme-polyfill@^2.0.1: version "2.0.3" @@ -6265,9 +6265,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.0.2, prettier@^2.0.4: - version "2.3.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" - integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA== + version "2.3.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" + integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== pretty-format@^24.9.0: version "24.9.0" @@ -6535,9 +6535,9 @@ react-native-image-resizer@^1.4.4: integrity sha512-33EgL3C9pyvjKpullAB6fWyD5QhoYEpNNB9rxNvUsrpAnL2mHBW7PTrUCCZudJeB6Weg7nbweKrSw1nnto5aqg== react-native-inappbrowser-reborn@^3.5.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/react-native-inappbrowser-reborn/-/react-native-inappbrowser-reborn-3.5.1.tgz#a17e6548ac7864f074f5df6171781601d179d638" - integrity sha512-pEM9DSvW3LYoI7x1sfr4GtTA1/BSzSoQvHeNRtbJwOpujgRBqnEQrapBwC/24l2pIRhKDWmXGfqc12478IXY3Q== + version "3.6.1" + resolved "https://registry.yarnpkg.com/react-native-inappbrowser-reborn/-/react-native-inappbrowser-reborn-3.6.1.tgz#9f2bfab73901f1e6c6ef6958e994749b008fa19b" + integrity sha512-4hoBh15ppCl1XxHbrZfvwq86s3Q+65jdUZwKZX3dCALWrSN1Rj47gxPEqlT6dSUltsgGBgWJ4Raq2JyIW2ZiYw== dependencies: invariant "^2.2.4" opencollective-postinstall "^2.0.2" @@ -8311,9 +8311,9 @@ ws@^5.2.0: async-limiter "~1.0.0" ws@^7, ws@^7.4.4: - version "7.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.0.tgz#0033bafea031fb9df041b2026fc72a571ca44691" - integrity sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw== + version "7.5.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.1.tgz#44fc000d87edb1d9c53e51fbc69a0ac1f6871d66" + integrity sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow== xcode@^2.0.0: version "2.1.0" -- cgit v1.2.3-70-g09d2 From c548f8df62c3775058ffa18e201ca230a641e6c1 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 15:18:57 -0400 Subject: Cleanup code --- src/components/camera/GalleryIcon.tsx | 24 ++++-------------------- src/components/camera/SaveButton.tsx | 4 ++-- src/components/moments/Moment.tsx | 4 +--- src/screens/moments/CameraScreen.tsx | 20 +++++++++++++++++--- src/utils/camera.ts | 4 ++-- 5 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/components/camera/GalleryIcon.tsx b/src/components/camera/GalleryIcon.tsx index bc8b1d41..8d396550 100644 --- a/src/components/camera/GalleryIcon.tsx +++ b/src/components/camera/GalleryIcon.tsx @@ -1,14 +1,12 @@ -import {useNavigation} from '@react-navigation/native'; import React from 'react'; import {Image, Text, TouchableOpacity, View} from 'react-native'; -import {ScreenType} from '../../types'; import {navigateToImagePicker} from '../../utils/camera'; +import {Image as ImageType} from 'react-native-image-crop-picker'; import {styles} from './styles'; interface GalleryIconProps { - screenType: ScreenType; - title: string; mostRecentPhotoUri: string; + callback: (pic: ImageType) => void; } /* @@ -16,26 +14,12 @@ interface GalleryIconProps { * On click, navigates to the image picker */ export const GalleryIcon: React.FC = ({ - screenType, - title, mostRecentPhotoUri, + callback, }) => { - const navigation = useNavigation(); return ( - navigateToImagePicker((pic) => - navigation.navigate('ZoomInCropper', { - screenType, - title, - media: { - filename: pic.filename, - uri: pic.path, - isVideo: false, - }, - }), - ) - } + onPress={() => navigateToImagePicker(callback)} style={styles.saveButton}> {mostRecentPhotoUri !== '' ? ( = ({capturedImageURI}) => ( { - downloadImage(capturedImageURI); + saveImageToGallery(capturedImageURI); }} style={styles.saveButton}> diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx index 25d69fba..f057b329 100644 --- a/src/components/moments/Moment.tsx +++ b/src/components/moments/Moment.tsx @@ -55,6 +55,7 @@ const Moment: React.FC = ({ }, }); }; + const navigateToCameraScreen = () => { navigation.navigate('CameraScreen', { title, @@ -195,9 +196,6 @@ const styles = StyleSheet.create({ color: TAGG_LIGHT_BLUE, maxWidth: '70%', }, - flexer: { - flex: 1, - }, scrollContainer: { height: SCREEN_WIDTH / 3.25, backgroundColor: '#eee', diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx index 1826f9d7..b3275764 100644 --- a/src/screens/moments/CameraScreen.tsx +++ b/src/screens/moments/CameraScreen.tsx @@ -15,7 +15,7 @@ import { } from '../../components'; import {MainStackParams} from '../../routes'; import {HeaderHeight, normalize, SCREEN_WIDTH} from '../../utils'; -import {takePicture} from '../../utils/camera'; +import {navigateToImagePicker, takePicture} from '../../utils/camera'; type CameraScreenRouteProps = RouteProp; export type CameraScreenNavigationProps = StackNavigationProp< @@ -52,6 +52,7 @@ const CameraScreen: React.FC = ({route, navigation}) => { ); }, [capturedImage]); + console.log(capturedImage); /* * Appears once a picture has been captured to navigate to the caption screen */ @@ -123,8 +124,21 @@ const CameraScreen: React.FC = ({route, navigation}) => { ) : ( + navigateToImagePicker((pic) => { + if (pic.filename) { + navigation.navigate('ZoomInCropper', { + screenType, + title, + media: { + filename: pic.filename, + uri: pic.path, + isVideo: false, + }, + }); + } + }) + } /> )} diff --git a/src/utils/camera.ts b/src/utils/camera.ts index 1ee5dbf4..877c8c2f 100644 --- a/src/utils/camera.ts +++ b/src/utils/camera.ts @@ -6,7 +6,7 @@ import ImagePicker, {Image, Video} from 'react-native-image-crop-picker'; import {ERROR_UPLOAD} from '../constants/strings'; /* - * Captures a photo and pauses to shoe the preview of the picture taken + * Captures a photo and pauses to show the preview of the picture taken */ export const takePicture = ( cameraRef: RefObject, @@ -24,7 +24,7 @@ export const takePicture = ( } }; -export const downloadImage = (capturedImageURI: string) => { +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!')); -- cgit v1.2.3-70-g09d2 From a8c210165938cfa4da7ed6bc185af297d528d2aa Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 15:32:52 -0400 Subject: Remove filename requirement for all moment upload --- src/components/comments/ZoomInCropper.tsx | 17 +++++++++++++++-- src/components/moments/Moment.tsx | 2 -- src/routes/main/MainStackNavigator.tsx | 5 ++--- src/screens/moments/CameraScreen.tsx | 18 +++++++++++++++--- src/screens/profile/CaptionScreen.tsx | 16 ++++------------ src/services/MomentService.ts | 21 +++++++-------------- 6 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/components/comments/ZoomInCropper.tsx b/src/components/comments/ZoomInCropper.tsx index cfd3cbbc..7fa88f6e 100644 --- a/src/components/comments/ZoomInCropper.tsx +++ b/src/components/comments/ZoomInCropper.tsx @@ -1,6 +1,7 @@ import {RouteProp} from '@react-navigation/core'; +import {useFocusEffect} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; -import React, {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'; @@ -33,6 +34,19 @@ export const ZoomInCropper: React.FC = ({ const [y0, setY0] = useState(); const [y1, setY1] = useState(); + useFocusEffect( + useCallback(() => { + navigation.dangerouslyGetParent()?.setOptions({ + tabBarVisible: false, + }); + return () => { + navigation.dangerouslyGetParent()?.setOptions({ + tabBarVisible: true, + }); + }; + }, [navigation]), + ); + // Setting original aspect ratio of image useEffect(() => { if (media.uri) { @@ -65,7 +79,6 @@ export const ZoomInCropper: React.FC = ({ screenType, title: title, media: { - filename: media.filename, uri: croppedURL, isVideo: false, }, diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx index f057b329..50c60fd9 100644 --- a/src/components/moments/Moment.tsx +++ b/src/components/moments/Moment.tsx @@ -44,12 +44,10 @@ const Moment: React.FC = ({ const navigation = useNavigation(); 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, }, diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx index 21430d7a..a5d73988 100644 --- a/src/routes/main/MainStackNavigator.tsx +++ b/src/routes/main/MainStackNavigator.tsx @@ -37,14 +37,13 @@ export type MainStackParams = { }; CaptionScreen: { title?: string; - media?: {filename: string; uri: string; isVideo: boolean}; + media?: {uri: string; isVideo: boolean}; screenType: ScreenType; selectedTags?: MomentTagType[]; moment?: MomentType; }; TagFriendsScreen: { media: { - filename: string; uri: string; isVideo: boolean; }; @@ -111,7 +110,7 @@ export type MainStackParams = { Chat: undefined; NewChatModal: undefined; ZoomInCropper: { - media: {filename: string; uri: string; isVideo: boolean}; + media: {uri: string; isVideo: boolean}; screenType: ScreenType; title: string; }; diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx index b3275764..104774c0 100644 --- a/src/screens/moments/CameraScreen.tsx +++ b/src/screens/moments/CameraScreen.tsx @@ -1,8 +1,9 @@ import CameraRoll from '@react-native-community/cameraroll'; import {useBottomTabBarHeight} from '@react-navigation/bottom-tabs'; import {RouteProp} from '@react-navigation/core'; +import {useFocusEffect} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; -import React, {createRef, useEffect, useState} from 'react'; +import React, {createRef, useCallback, useEffect, useState} from 'react'; import {StyleSheet, TouchableOpacity, View} from 'react-native'; import {CameraType, FlashMode, RNCamera} from 'react-native-camera'; import CloseIcon from '../../assets/ionicons/close-outline.svg'; @@ -36,6 +37,19 @@ const CameraScreen: React.FC = ({route, navigation}) => { const [mostRecentPhoto, setMostRecentPhoto] = useState(''); const [showSaveButton, setShowSaveButton] = useState(false); + useFocusEffect( + useCallback(() => { + navigation.dangerouslyGetParent()?.setOptions({ + tabBarVisible: false, + }); + return () => { + navigation.dangerouslyGetParent()?.setOptions({ + tabBarVisible: true, + }); + }; + }, [navigation]), + ); + /* * Chooses the last picture from gallery to display as the gallery button icon */ @@ -61,7 +75,6 @@ const CameraScreen: React.FC = ({route, navigation}) => { screenType, title, media: { - filename: 'dont have that info', uri: capturedImage, isVideo: false, // TODO: false for now }, @@ -131,7 +144,6 @@ const CameraScreen: React.FC = ({route, navigation}) => { screenType, title, media: { - filename: pic.filename, uri: pic.path, isVideo: false, }, diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 364b81a3..da3efb06 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -69,7 +69,6 @@ const CaptionScreen: React.FC = ({route, navigation}) => { selectedTags ? selectedTags : [], ); const [taggedList, setTaggedList] = useState(''); - const mediaFilename = moment ? undefined : route.params.media!.filename; const mediaUri = moment ? moment.moment_url : route.params.media!.uri; // TODO: change this once moment refactor is done const isMediaAVideo = moment @@ -138,7 +137,7 @@ const CaptionScreen: React.FC = ({route, navigation}) => { const handleShare = async () => { setLoading(true); - if (moment || !mediaFilename || !title) { + if (moment || !title) { handleFailed(); return; } @@ -146,22 +145,16 @@ const CaptionScreen: React.FC = ({route, navigation}) => { let momentId; // separate upload logic for image/video if (isMediaAVideo) { - const presignedURL = await handlePresignedURL(mediaFilename, title); + const presignedURL = await handlePresignedURL(title); if (!presignedURL) { handleFailed(); return; } momentId = presignedURL.moment_id; // TODO: assume success for now - await handleVideoUpload(mediaFilename, mediaUri, presignedURL); + await handleVideoUpload(mediaUri, presignedURL); } else { - const momentResponse = await postMoment( - mediaFilename, - mediaUri, - caption, - title, - userId, - ); + const momentResponse = await postMoment(mediaUri, caption, title, userId); if (!momentResponse) { handleFailed(); return; @@ -252,7 +245,6 @@ const CaptionScreen: React.FC = ({route, navigation}) => { onPress={() => navigation.navigate('TagFriendsScreen', { media: { - filename: mediaFilename ?? '', uri: mediaUri, isVideo: isMediaAVideo, }, diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index b274ef04..ad0b0042 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -6,6 +6,7 @@ import { MOMENT_TAGS_ENDPOINT, MOMENT_THUMBNAIL_ENDPOINT, PRESIGNED_URL_ENDPOINT, + RADIO_BUTTON_GREY, TAGG_CUSTOMER_SUPPORT, } from '../constants'; import { @@ -16,7 +17,6 @@ import {MomentPostType, MomentTagType, PresignedURLResponse} from '../types'; import {checkImageUploadStatus} from '../utils'; export const postMoment = async ( - fileName: string, uri: string, caption: string, category: string, @@ -25,13 +25,9 @@ export const postMoment = async ( try { const request = new FormData(); - //Manipulating filename to end with .jpg instead of .heic - if (fileName.endsWith('.heic') || fileName.endsWith('.HEIC')) { - fileName = fileName.split('.')[0] + '.jpg'; - } request.append('image', { uri: uri, - name: fileName, + name: 'moment.jpg', // we don't care about filename, anything works type: 'image/jpg', }); request.append('moment', category); @@ -219,14 +215,13 @@ export const deleteMomentTag = async (moment_tag_id: string) => { * This function makes a request to the server in order to provide the client with a presigned URL. * This is called first, in order for the client to directly upload a file to S3 * @param value: string | undefined - * @param filename: string | undefined * @returns a PresignedURLResponse object */ -export const handlePresignedURL = async ( - filename: string | undefined, - momentCategory: string, -) => { +export const handlePresignedURL = async (momentCategory: string) => { try { + // TODO: just a random filename for video poc, we should not need to once complete + const randHash = Math.random().toString(36).substring(7); + const filename = `[pc_${randHash}].mov`; const token = await AsyncStorage.getItem('token'); const response = await fetch(PRESIGNED_URL_ENDPOINT, { method: 'POST', @@ -260,13 +255,11 @@ export const handlePresignedURL = async ( /** * This util function takes in the file object and the PresignedURLResponse object, creates form data from the latter, * and makes a post request to the presigned URL, sending the file object inside of the form data. - * @param filename: the filename * @param filePath: the path to the file, including filename * @param urlObj PresignedURLResponse | undefined * @returns responseURL or boolean */ export const handleVideoUpload = async ( - filename: string, filePath: string, urlObj: PresignedURLResponse | undefined, ) => { @@ -297,7 +290,7 @@ export const handleVideoUpload = async ( uri: filePath, // other types such as 'quicktime' 'image' etc exist, and we can programmatically type this, but for now sticking with simple 'video' type: 'video', - name: filename, + name: 'moment.mov', // we don't care about filename, anything works }); const response = await fetch(urlObj.response_url.url, { method: 'POST', -- cgit v1.2.3-70-g09d2 From 159ae12d6679441d16791bbf23d05240e3779101 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 15:33:02 -0400 Subject: Fix camera orientation --- src/utils/camera.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/utils/camera.ts b/src/utils/camera.ts index 877c8c2f..7e4fc14e 100644 --- a/src/utils/camera.ts +++ b/src/utils/camera.ts @@ -1,7 +1,11 @@ import CameraRoll from '@react-native-community/cameraroll'; import {RefObject} from 'react'; import {Alert} from 'react-native'; -import {RNCamera, TakePictureResponse} from 'react-native-camera'; +import { + RNCamera, + TakePictureOptions, + TakePictureResponse, +} from 'react-native-camera'; import ImagePicker, {Image, Video} from 'react-native-image-crop-picker'; import {ERROR_UPLOAD} from '../constants/strings'; @@ -14,8 +18,9 @@ export const takePicture = ( ) => { if (cameraRef !== null) { cameraRef.current?.pausePreview(); - const options = { + const options: TakePictureOptions = { forceUpOrientation: true, + orientation: 'portrait', writeExif: false, }; cameraRef.current?.takePictureAsync(options).then((pic) => { -- cgit v1.2.3-70-g09d2 From 56ec3a62ebbade90f3d0fb3ff2ccbcbfc091672b Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 15:47:24 -0400 Subject: Cleanup code --- src/components/moments/Moment.tsx | 5 +++-- src/screens/moments/CameraScreen.tsx | 32 +++++++++++++++----------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx index 50c60fd9..108ea100 100644 --- a/src/components/moments/Moment.tsx +++ b/src/components/moments/Moment.tsx @@ -43,6 +43,7 @@ const Moment: React.FC = ({ }) => { const navigation = useNavigation(); + // TODO: remove this later, tmp solution for handling videos const navigateToCaptionScreenForVideo = (uri: string) => { navigation.navigate('CaptionScreen', { screenType, @@ -128,7 +129,7 @@ const Moment: React.FC = ({ navigateToCameraScreen()} + onPress={navigateToCameraScreen} color={TAGG_LIGHT_BLUE} style={styles.horizontalMargin} /> @@ -158,7 +159,7 @@ const Moment: React.FC = ({ /> ))} {(images === undefined || images.length === 0) && !userXId && ( - navigateToCameraScreen()}> + diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx index 104774c0..7b71f9e5 100644 --- a/src/screens/moments/CameraScreen.tsx +++ b/src/screens/moments/CameraScreen.tsx @@ -66,17 +66,24 @@ const CameraScreen: React.FC = ({route, navigation}) => { ); }, [capturedImage]); - console.log(capturedImage); - /* - * Appears once a picture has been captured to navigate to the caption screen - */ - const handleNext = () => { + const navigateToCropper = (uri: string) => { + navigation.navigate('ZoomInCropper', { + screenType, + title, + media: { + uri, + isVideo: false, // TODO: only support image for now + }, + }); + }; + + const navigateToCaptionScreen = () => { navigation.navigate('CaptionScreen', { screenType, title, media: { uri: capturedImage, - isVideo: false, // TODO: false for now + isVideo: false, // TODO: only support image for now }, }); }; @@ -126,7 +133,7 @@ const CameraScreen: React.FC = ({route, navigation}) => { {capturedImage ? ( = ({route, navigation}) => { mostRecentPhotoUri={mostRecentPhoto} callback={() => navigateToImagePicker((pic) => { - if (pic.filename) { - navigation.navigate('ZoomInCropper', { - screenType, - title, - media: { - uri: pic.path, - isVideo: false, - }, - }); - } + navigateToCropper(pic.path); }) } /> -- cgit v1.2.3-70-g09d2 From 919a7c14633f0a381613cebf73e8f29d84d3ebe2 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 15:49:18 -0400 Subject: Add option to mirror image --- src/utils/camera.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/camera.ts b/src/utils/camera.ts index 7e4fc14e..e5eba5f8 100644 --- a/src/utils/camera.ts +++ b/src/utils/camera.ts @@ -21,6 +21,7 @@ export const takePicture = ( const options: TakePictureOptions = { forceUpOrientation: true, orientation: 'portrait', + mirrorImage: true, writeExif: false, }; cameraRef.current?.takePictureAsync(options).then((pic) => { -- cgit v1.2.3-70-g09d2 From 9eb246e92aad427ac4d12840960f0be531f43d19 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 15:57:55 -0400 Subject: Squashed commit of the following: commit 7046c2673c54ecf2f418a85fbcfc2c4872858697 Merge: 5480267b c6900bc6 Author: Ivan Chen Date: Wed Jun 30 15:16:02 2021 -0400 Merge pull request #479 from brian-tagg/tma959-gif-handle [TMA-959] GIF handle commit c6900bc62b2ea9a1eb07088ac02556c0dd1d5ebc Author: Brian Kim Date: Thu Jul 1 04:00:53 2021 +0900 Redo extraneous file commit 4eda8db632ca996ff9712872b2076a8675a63523 Author: Brian Kim Date: Thu Jul 1 03:52:50 2021 +0900 Updates from product commit 1c1975265fb6013c34c8e766a7f11b0c5b8d1e11 Author: Brian Kim Date: Thu Jul 1 01:23:19 2021 +0900 Respond to comments commit 50344b4236e235fdd42997c1fe85f2c1bd014e47 Author: Brian Kim Date: Tue Jun 29 23:04:22 2021 +0900 Fixed up some merge issues commit 014af4ac61bae6e38a4965ceb274b147ac3395c6 Author: Brian Kim Date: Tue Jun 29 23:01:24 2021 +0900 Construct and merge with master --- src/components/moments/MomentPost.tsx | 4 +++- src/screens/moments/CameraScreen.tsx | 16 ++++++++++++++-- src/utils/camera.ts | 29 ++++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/components/moments/MomentPost.tsx b/src/components/moments/MomentPost.tsx index cb3a138b..f5a256d6 100644 --- a/src/components/moments/MomentPost.tsx +++ b/src/components/moments/MomentPost.tsx @@ -78,7 +78,9 @@ const MomentPost: React.FC = ({ moment.moment_url.endsWith('jpg') || moment.moment_url.endsWith('JPG') || moment.moment_url.endsWith('PNG') || - moment.moment_url.endsWith('png') + moment.moment_url.endsWith('png') || + moment.moment_url.endsWith('GIF') || + moment.moment_url.endsWith('gif') ); /* diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx index 7b71f9e5..4ca79c4f 100644 --- a/src/screens/moments/CameraScreen.tsx +++ b/src/screens/moments/CameraScreen.tsx @@ -16,7 +16,11 @@ import { } from '../../components'; import {MainStackParams} from '../../routes'; import {HeaderHeight, normalize, SCREEN_WIDTH} from '../../utils'; -import {navigateToImagePicker, takePicture} from '../../utils/camera'; +import { + navigateToImagePicker, + showGIFFailureAlert, + takePicture, +} from '../../utils/camera'; type CameraScreenRouteProps = RouteProp; export type CameraScreenNavigationProps = StackNavigationProp< @@ -146,7 +150,15 @@ const CameraScreen: React.FC = ({route, navigation}) => { mostRecentPhotoUri={mostRecentPhoto} callback={() => navigateToImagePicker((pic) => { - navigateToCropper(pic.path); + const filename = pic.filename; + if ( + filename && + (filename.endsWith('gif') || filename.endsWith('GIF')) + ) { + showGIFFailureAlert(() => navigateToCropper(pic.path)); + } else { + navigateToCropper(pic.path); + } }) } /> diff --git a/src/utils/camera.ts b/src/utils/camera.ts index e5eba5f8..0be4d27c 100644 --- a/src/utils/camera.ts +++ b/src/utils/camera.ts @@ -48,9 +48,7 @@ export const navigateToImagePicker = (callback: (pic: Image) => void) => { mediaType: 'photo', }) .then((pic) => { - if (pic.path && pic.filename) { - callback(pic); - } + callback(pic); }) .catch((err) => { if (err.code && err.code !== 'E_PICKER_CANCELLED') { @@ -81,3 +79,28 @@ export const navigateToVideoPicker = (callback: (vid: Video) => void) => { } }); }; + +export const showGIFFailureAlert = (onSuccess: () => void) => + Alert.alert( + 'Warning', + 'The app currently cannot handle GIFs, and will only save a static image.', + [ + { + text: 'Cancel', + onPress: () => {}, + style: 'cancel', + }, + { + text: 'Post', + onPress: onSuccess, + style: 'default', + }, + ], + { + cancelable: true, + onDismiss: () => + Alert.alert( + 'This alert was dismissed by tapping outside of the alert dialog.', + ), + }, + ); -- cgit v1.2.3-70-g09d2 From 3b73e88ac85b4c45ba34f9ddbc859b21edcc0f6b Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 15:58:41 -0400 Subject: Remove unused comment --- src/utils/camera.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/utils/camera.ts b/src/utils/camera.ts index 0be4d27c..9433ccc7 100644 --- a/src/utils/camera.ts +++ b/src/utils/camera.ts @@ -57,13 +57,6 @@ export const navigateToImagePicker = (callback: (pic: Image) => void) => { }); }; -/** - * 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 - */ export const navigateToVideoPicker = (callback: (vid: Video) => void) => { ImagePicker.openPicker({ mediaType: 'video', -- cgit v1.2.3-70-g09d2 From 926325faca1a7b664e0e1f4dd6d32f9631e6bdb9 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 16:33:47 -0400 Subject: Remove patch --- .../react-native-inappbrowser-reborn+3.5.1.patch | 24 ---------------------- 1 file changed, 24 deletions(-) delete mode 100644 patches/react-native-inappbrowser-reborn+3.5.1.patch diff --git a/patches/react-native-inappbrowser-reborn+3.5.1.patch b/patches/react-native-inappbrowser-reborn+3.5.1.patch deleted file mode 100644 index 12a49b85..00000000 --- a/patches/react-native-inappbrowser-reborn+3.5.1.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/node_modules/react-native-inappbrowser-reborn/ios/RNInAppBrowser.m b/node_modules/react-native-inappbrowser-reborn/ios/RNInAppBrowser.m -index 674e8f1..81f069e 100644 ---- a/node_modules/react-native-inappbrowser-reborn/ios/RNInAppBrowser.m -+++ b/node_modules/react-native-inappbrowser-reborn/ios/RNInAppBrowser.m -@@ -90,15 +90,17 @@ RCT_EXPORT_METHOD(openAuth:(NSString *)authURL - } - }; - -+ NSString *escapedRedirectURL = [redirectURL stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]]; -+ - if (@available(iOS 12.0, *)) { - webAuthSession = [[ASWebAuthenticationSession alloc] - initWithURL:url -- callbackURLScheme:redirectURL -+ callbackURLScheme:escapedRedirectURL - completionHandler:completionHandler]; - } else { - authSession = [[SFAuthenticationSession alloc] - initWithURL:url -- callbackURLScheme:redirectURL -+ callbackURLScheme:escapedRedirectURL - completionHandler:completionHandler]; - } - -- cgit v1.2.3-70-g09d2 From 7dc3ed67d4008ad4dd171dcae846639d9349c4d4 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 17:25:15 -0400 Subject: Lint --- src/services/MomentService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index ad0b0042..9e853a49 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -6,7 +6,6 @@ import { MOMENT_TAGS_ENDPOINT, MOMENT_THUMBNAIL_ENDPOINT, PRESIGNED_URL_ENDPOINT, - RADIO_BUTTON_GREY, TAGG_CUSTOMER_SUPPORT, } from '../constants'; import { -- cgit v1.2.3-70-g09d2 From b77a716e52fcc1e0c669ce59df1f063fb008a5ca Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 30 Jun 2021 17:32:56 -0400 Subject: Fix gallery repicking image bug --- src/screens/moments/CameraScreen.tsx | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx index 4ca79c4f..d9278876 100644 --- a/src/screens/moments/CameraScreen.tsx +++ b/src/screens/moments/CameraScreen.tsx @@ -16,11 +16,7 @@ import { } from '../../components'; import {MainStackParams} from '../../routes'; import {HeaderHeight, normalize, SCREEN_WIDTH} from '../../utils'; -import { - navigateToImagePicker, - showGIFFailureAlert, - takePicture, -} from '../../utils/camera'; +import {showGIFFailureAlert, takePicture} from '../../utils/camera'; type CameraScreenRouteProps = RouteProp; export type CameraScreenNavigationProps = StackNavigationProp< @@ -148,19 +144,17 @@ const CameraScreen: React.FC = ({route, navigation}) => { ) : ( - navigateToImagePicker((pic) => { - const filename = pic.filename; - if ( - filename && - (filename.endsWith('gif') || filename.endsWith('GIF')) - ) { - showGIFFailureAlert(() => navigateToCropper(pic.path)); - } else { - navigateToCropper(pic.path); - } - }) - } + callback={(pic) => { + const filename = pic.filename; + if ( + filename && + (filename.endsWith('gif') || filename.endsWith('GIF')) + ) { + showGIFFailureAlert(() => navigateToCropper(pic.path)); + } else { + navigateToCropper(pic.path); + } + }} /> )} -- cgit v1.2.3-70-g09d2 From 29f64921943fb7016ab0db79e4c06377d617a3bf Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 1 Jul 2021 15:53:25 -0400 Subject: Add logic for handling new video upload handshake --- src/screens/profile/CaptionScreen.tsx | 14 +++++++++----- src/services/MomentService.ts | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index da3efb06..05db8ed7 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -145,14 +145,18 @@ const CaptionScreen: React.FC = ({route, navigation}) => { let momentId; // separate upload logic for image/video if (isMediaAVideo) { - const presignedURL = await handlePresignedURL(title); - if (!presignedURL) { + const presignedURLResponse = await handlePresignedURL(title); + if (!presignedURLResponse) { handleFailed(); return; } - momentId = presignedURL.moment_id; - // TODO: assume success for now - await handleVideoUpload(mediaUri, presignedURL); + momentId = presignedURLResponse.moment_id; + const fileHash = presignedURLResponse.response_url.fields.key; + if (fileHash !== null && fileHash !== '' && fileHash !== undefined) { + await handleVideoUpload(mediaUri, presignedURLResponse); + } else { + handleFailed(); + } } else { const momentResponse = await postMoment(mediaUri, caption, title, userId); if (!momentResponse) { diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index 9e853a49..87bdfa40 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -289,7 +289,7 @@ export const handleVideoUpload = async ( uri: filePath, // other types such as 'quicktime' 'image' etc exist, and we can programmatically type this, but for now sticking with simple 'video' type: 'video', - name: 'moment.mov', // we don't care about filename, anything works + name: urlObj.response_url.fields.key, }); const response = await fetch(urlObj.response_url.url, { method: 'POST', -- cgit v1.2.3-70-g09d2 From ae3b50ee47c615b5080082e9fc4808ef85807607 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 1 Jul 2021 15:59:44 -0400 Subject: Update home directory in Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b0022598..44f637e2 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,6 @@ clean: deep_clean: -rm *.lock -rm ios/*.lock - -rm -rf /Users/ivan/Library/Developer/Xcode/DerivedData + -rm -rf ~/Library/Developer/Xcode/DerivedData -rm -rf node_modules yarn cache clean -- cgit v1.2.3-70-g09d2 From 04dd962e8113dda15c54883d092e58d96da92edd Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 1 Jul 2021 16:09:03 -0400 Subject: Undo mirror image option --- src/utils/camera.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/camera.ts b/src/utils/camera.ts index 9433ccc7..3937129a 100644 --- a/src/utils/camera.ts +++ b/src/utils/camera.ts @@ -21,7 +21,6 @@ export const takePicture = ( const options: TakePictureOptions = { forceUpOrientation: true, orientation: 'portrait', - mirrorImage: true, writeExif: false, }; cameraRef.current?.takePictureAsync(options).then((pic) => { -- cgit v1.2.3-70-g09d2 From c08a67f5cc1d622e91828d0557c6716a40ee5bad Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 1 Jul 2021 16:23:19 -0400 Subject: Fix filename bug --- src/services/MomentService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index 87bdfa40..60e6be3f 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -220,7 +220,7 @@ export const handlePresignedURL = async (momentCategory: string) => { try { // TODO: just a random filename for video poc, we should not need to once complete const randHash = Math.random().toString(36).substring(7); - const filename = `[pc_${randHash}].mov`; + const filename = `pc_${randHash}.mov`; const token = await AsyncStorage.getItem('token'); const response = await fetch(PRESIGNED_URL_ENDPOINT, { method: 'POST', -- cgit v1.2.3-70-g09d2