aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--ios/Frontend/Info.plist6
-rw-r--r--ios/Podfile.lock16
-rw-r--r--package.json1
-rw-r--r--patches/react-native-inappbrowser-reborn+3.5.1.patch24
-rw-r--r--src/assets/icons/camera/flash-off.svg1
-rw-r--r--src/assets/icons/camera/flash-on.svg1
-rw-r--r--src/assets/icons/camera/flip.svg1
-rw-r--r--src/assets/icons/camera/save.svg1
-rw-r--r--src/components/camera/FlashButton.tsx42
-rw-r--r--src/components/camera/FlipButton.tsx29
-rw-r--r--src/components/camera/GalleryIcon.tsx39
-rw-r--r--src/components/camera/SaveButton.tsx26
-rw-r--r--src/components/camera/index.ts4
-rw-r--r--src/components/camera/styles.tsx53
-rw-r--r--src/components/comments/ZoomInCropper.tsx4
-rw-r--r--src/components/index.ts9
-rw-r--r--src/components/moments/Moment.tsx116
-rw-r--r--src/routes/main/MainStackNavigator.tsx9
-rw-r--r--src/routes/main/MainStackScreen.tsx22
-rw-r--r--src/screens/moments/CameraScreen.tsx228
-rw-r--r--src/screens/moments/index.ts1
-rw-r--r--src/screens/profile/CaptionScreen.tsx26
-rw-r--r--src/services/MomentService.ts20
-rw-r--r--src/utils/camera.ts98
-rw-r--r--yarn.lock61
26 files changed, 630 insertions, 210 deletions
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
diff --git a/ios/Frontend/Info.plist b/ios/Frontend/Info.plist
index fa67d073..0b1f27bf 100644
--- a/ios/Frontend/Info.plist
+++ b/ios/Frontend/Info.plist
@@ -54,8 +54,6 @@
<string></string>
<key>NSMainNibFile</key>
<string>LaunchScreen</string>
- <key>NSPhotoLibraryUsageDescription</key>
- <string>This lets you share photos from your library and select profile displays</string>
<key>UIAppFonts</key>
<array>
<string>Feather.ttf</string>
@@ -76,6 +74,10 @@
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
+ <key>NSPhotoLibraryUsageDescription</key>
+ <string>This lets you share photos from your library and select profile displays</string>
+ <key>NSPhotoLibraryAddUsageDescription</key>
+ <string>This lets you save photos captured on Tagg, to your library</string>
<key>NSCameraUsageDescription</key>
<string>This lets you share photos/videos from your camera for a Moment post on your profile!</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 15ec026f..19df8c81 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):
@@ -447,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
@@ -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
@@ -789,7 +801,7 @@ SPEC CHECKSUMS:
RNFS: 3ab21fa6c56d65566d1fb26c2228e2b6132e5e32
RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211
RNImageCropPicker: 35a3ceb837446fa11547704709bb22b5fac6d584
- RNInAppBrowser: 48b95ba7a4eaff5cc223bca338d3e319561dbd1b
+ RNInAppBrowser: 0523b3c15501fb8b54b4f32905d2e71ca902d914
RNReactNativeHapticFeedback: 653a8c126a0f5e88ce15ffe280b3ff37e1fbb285
RNReanimated: b9c929bfff7dedc9c89ab1875f1c6151023358d9
RNScreens: f7ad633b2e0190b77b6a7aab7f914fad6f198d8d
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/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];
- }
-
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 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 720"><defs><style>.cls-1{fill:#fff;}</style></defs><path class="cls-1" d="M361.84,13.57,128.3,360c-3.94,2.81-6.8,11.94-7.74,16.16-4.5,29.23,19.23,41.69,31.66,44.27H289.39l-26,253c-2.11,10.3-1.13,33.3,19.69,42.86s38.69-1.18,45-7.73l260.27-373.1,9.85-16.16c8.44-32.61-14.78-46.38-27.44-49.19H400.53L427.26,38.16c1.13-17.42-11.26-29.28-17.59-33C386.6-7.23,368.17,5.61,361.84,13.57Z"/></svg> \ 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 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 720"><defs><style>.cls-1{fill:#fff;}</style></defs><path class="cls-1" d="M413.14,36.09,387.86,255.37h5.7L251.35,397.57H153c-11.76-2.43-34.2-14.21-29.95-41.86.89-4,3.6-12.62,7.32-15.28L351.28,12.83c6-7.53,23.41-19.67,45.23-8C402.5,8.4,414.2,19.61,413.14,36.09Z"/><path class="cls-1" d="M594.91,341l-9.32,15.28L339.45,709.1c-6,6.2-22.89,16.35-42.58,7.31s-20.62-30.78-18.63-40.53L297,493.2,495.79,294.46H569C580.94,297.12,602.89,310.14,594.91,341Z"/><path class="cls-1" d="M634.48,114.82a29,29,0,0,1-8.5,20.53L486.15,275.18,287.4,473.92,135.09,626.24A29,29,0,1,1,94,585.18l167.8-167.8L404,275.18,584.92,94.29a29,29,0,0,1,49.56,20.53Z"/></svg> \ 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 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 720"><defs><style>.cls-1{fill:none;stroke:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-width:49.69px;}</style></defs><path class="cls-1" d="M691.29,360c0,164.67-133.49,298.16-298.16,298.16-132.89,0-245.48-86.95-284-207.06m0-182.2c38.5-120.11,151.09-207.06,284-207.06A297.74,297.74,0,0,1,633.31,183.3"/><path class="cls-1" d="M28.71,525.64l74.54-99.38L227.48,476"/><path class="cls-1" d="M525.64,208.11l124.16-4.06L680.34,73.78"/></svg> \ 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 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 720"><defs><style>.cls-1{fill:none;stroke:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-width:49.92px;}</style></defs><path class="cls-1" d="M359.15,42v526.9"/><path class="cls-1" d="M192.76,424.7,359.85,577.23,539.41,424.7"/><path class="cls-1" d="M26.37,458V618.53c.63,20.44,15.92,61.3,72.08,61.3h527c21.53-1.29,65-15.37,66.46-61.3V458"/></svg> \ 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<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',
diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx
index c518d75e..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,10 +110,14 @@ export type MainStackParams = {
Chat: undefined;
NewChatModal: undefined;
ZoomInCropper: {
- media: {filename: string; uri: string; isVideo: boolean};
+ media: {uri: string; isVideo: boolean};
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..65a695f5 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';
@@ -331,6 +332,15 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => {
name="ZoomInCropper"
component={ZoomInCropper}
options={{
+ ...modalStyle,
+ gestureEnabled: false,
+ }}
+ />
+ <MainStack.Screen
+ name="CameraScreen"
+ component={CameraScreen}
+ options={{
+ ...modalStyle,
gestureEnabled: false,
}}
/>
@@ -399,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
new file mode 100644
index 00000000..d9278876
--- /dev/null
+++ b/src/screens/moments/CameraScreen.tsx
@@ -0,0 +1,228 @@
+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, 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 {showGIFFailureAlert, takePicture} from '../../utils/camera';
+
+type CameraScreenRouteProps = RouteProp<MainStackParams, 'CameraScreen'>;
+export 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>('');
+ const [showSaveButton, setShowSaveButton] = useState<boolean>(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
+ */
+ 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]);
+
+ 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: only support image for now
+ },
+ });
+ };
+
+ /*
+ * 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 (
+ <View style={styles.container}>
+ <TouchableOpacity style={styles.closeButton} onPress={handleClose}>
+ <CloseIcon height={25} width={25} color={'white'} />
+ </TouchableOpacity>
+ <FlashButton flashMode={flashMode} setFlashMode={setFlashMode} />
+ <RNCamera
+ ref={cameraRef}
+ style={styles.camera}
+ type={cameraType}
+ flashMode={flashMode}
+ />
+ <View style={[styles.bottomContainer, {bottom: tabBarHeight}]}>
+ {showSaveButton ? (
+ <SaveButton capturedImageURI={capturedImage} />
+ ) : (
+ <FlipButton cameraType={cameraType} setCameraType={setCameraType} />
+ )}
+ <TouchableOpacity
+ onPress={() =>
+ takePicture(cameraRef, (pic) => {
+ setShowSaveButton(true);
+ setCapturedImage(pic.uri);
+ })
+ }
+ style={styles.captureButtonContainer}>
+ <View style={styles.captureButton} />
+ </TouchableOpacity>
+ <View style={styles.bottomRightContainer}>
+ {capturedImage ? (
+ <TaggSquareButton
+ onPress={navigateToCaptionScreen}
+ title={'Next'}
+ buttonStyle={'large'}
+ buttonColor={'blue'}
+ labelColor={'white'}
+ style={styles.nextButton}
+ labelStyle={styles.nextButtonLabel}
+ />
+ ) : (
+ <GalleryIcon
+ mostRecentPhotoUri={mostRecentPhoto}
+ callback={(pic) => {
+ const filename = pic.filename;
+ if (
+ filename &&
+ (filename.endsWith('gif') || filename.endsWith('GIF'))
+ ) {
+ showGIFFailureAlert(() => navigateToCropper(pic.path));
+ } else {
+ navigateToCropper(pic.path);
+ }
+ }}
+ />
+ )}
+ </View>
+ </View>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ camera: {
+ flex: 1,
+ justifyContent: 'space-between',
+ },
+ container: {
+ flex: 1,
+ flexDirection: 'column',
+ backgroundColor: 'black',
+ },
+ 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/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx
index 364b81a3..05db8ed7 100644
--- a/src/screens/profile/CaptionScreen.tsx
+++ b/src/screens/profile/CaptionScreen.tsx
@@ -69,7 +69,6 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
selectedTags ? selectedTags : [],
);
const [taggedList, setTaggedList] = useState<string>('');
- 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<CaptionScreenProps> = ({route, navigation}) => {
const handleShare = async () => {
setLoading(true);
- if (moment || !mediaFilename || !title) {
+ if (moment || !title) {
handleFailed();
return;
}
@@ -146,22 +145,20 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
let momentId;
// separate upload logic for image/video
if (isMediaAVideo) {
- const presignedURL = await handlePresignedURL(mediaFilename, title);
- if (!presignedURL) {
+ const presignedURLResponse = await handlePresignedURL(title);
+ if (!presignedURLResponse) {
handleFailed();
return;
}
- momentId = presignedURL.moment_id;
- // TODO: assume success for now
- await handleVideoUpload(mediaFilename, 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(
- mediaFilename,
- mediaUri,
- caption,
- title,
- userId,
- );
+ const momentResponse = await postMoment(mediaUri, caption, title, userId);
if (!momentResponse) {
handleFailed();
return;
@@ -252,7 +249,6 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({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..60e6be3f 100644
--- a/src/services/MomentService.ts
+++ b/src/services/MomentService.ts
@@ -16,7 +16,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 +24,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 +214,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 +254,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 +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: filename,
+ name: urlObj.response_url.fields.key,
});
const response = await fetch(urlObj.response_url.url, {
method: 'POST',
diff --git a/src/utils/camera.ts b/src/utils/camera.ts
new file mode 100644
index 00000000..3937129a
--- /dev/null
+++ b/src/utils/camera.ts
@@ -0,0 +1,98 @@
+import CameraRoll from '@react-native-community/cameraroll';
+import {RefObject} from 'react';
+import {Alert} from 'react-native';
+import {
+ RNCamera,
+ TakePictureOptions,
+ TakePictureResponse,
+} from 'react-native-camera';
+import ImagePicker, {Image, Video} from 'react-native-image-crop-picker';
+import {ERROR_UPLOAD} from '../constants/strings';
+
+/*
+ * Captures a photo and pauses to show the preview of the picture taken
+ */
+export const takePicture = (
+ cameraRef: RefObject<RNCamera>,
+ callback: (pic: TakePictureResponse) => void,
+) => {
+ if (cameraRef !== null) {
+ cameraRef.current?.pausePreview();
+ const options: TakePictureOptions = {
+ forceUpOrientation: true,
+ orientation: 'portrait',
+ writeExif: false,
+ };
+ cameraRef.current?.takePictureAsync(options).then((pic) => {
+ callback(pic);
+ });
+ }
+};
+
+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!'));
+};
+
+export const navigateToImagePicker = (callback: (pic: Image) => void) => {
+ ImagePicker.openPicker({
+ smartAlbums: [
+ 'Favorites',
+ 'RecentlyAdded',
+ 'SelfPortraits',
+ 'Screenshots',
+ 'UserLibrary',
+ ],
+ mediaType: 'photo',
+ })
+ .then((pic) => {
+ callback(pic);
+ })
+ .catch((err) => {
+ if (err.code && err.code !== 'E_PICKER_CANCELLED') {
+ Alert.alert(ERROR_UPLOAD);
+ }
+ });
+};
+
+export const navigateToVideoPicker = (callback: (vid: Video) => void) => {
+ ImagePicker.openPicker({
+ mediaType: 'video',
+ })
+ .then(async (vid) => {
+ if (vid.path) {
+ callback(vid);
+ }
+ })
+ .catch((err) => {
+ if (err.code && err.code !== 'E_PICKER_CANCELLED') {
+ Alert.alert(ERROR_UPLOAD);
+ }
+ });
+};
+
+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.',
+ ),
+ },
+ );
diff --git a/yarn.lock b/yarn.lock
index 9b425078..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"
@@ -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"
@@ -6528,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"
@@ -8304,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"