From 0c029f2f801330da227d62858975823cff16978e Mon Sep 17 00:00:00 2001 From: George Rusu Date: Mon, 21 Jun 2021 19:18:25 -0700 Subject: Add new type --- src/services/MomentService.ts | 116 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) (limited to 'src/services') diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index b837585a..c5b2c530 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -1,12 +1,20 @@ import AsyncStorage from '@react-native-community/async-storage'; +import {Image, Video} from 'react-native-image-crop-picker'; import RNFetchBlob from 'rn-fetch-blob'; +import {ProfileBody} from '../components'; import { MOMENTS_ENDPOINT, MOMENTTAG_ENDPOINT, MOMENT_TAGS_ENDPOINT, MOMENT_THUMBNAIL_ENDPOINT, + PRESIGNED_URL_ENDPOINT, + TAGG_CUSTOMER_SUPPORT, } from '../constants'; -import {MomentPostType, MomentTagType} from '../types'; +import { + ERROR_SOMETHING_WENT_WRONG, + ERROR_SOMETHING_WENT_WRONG_REFRESH, +} from '../constants/strings'; +import {MomentPostType, MomentTagType, ResponseURL} from '../types'; import {checkImageUploadStatus} from '../utils'; export const postMoment = async ( @@ -18,6 +26,7 @@ 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'; @@ -208,3 +217,108 @@ export const deleteMomentTag = async (moment_tag_id: string) => { return false; } }; + +export const handlePresignedURL = async ( + value: string | undefined, + filename: string | undefined, +) => { + try { + const response = await fetch(PRESIGNED_URL_ENDPOINT, { + method: 'POST', + body: JSON.stringify({ + value, + filename, + }), + }); + const status = response.status; + let data: ResponseURL = await response.json(); + if (status === 200) { + console.log(data.response_msg); + return data; + } else { + if (status === 404) { + console.log( + `Please make sure that the email / username entered is registered with us. You may contact our customer support at ${TAGG_CUSTOMER_SUPPORT}`, + ); + } else { + console.log(ERROR_SOMETHING_WENT_WRONG_REFRESH); + } + console.log(response); + } + } catch (error) { + console.log(error); + console.log(ERROR_SOMETHING_WENT_WRONG); + } +}; +export const handleVideoUpload = async ( + file: Video | Image | undefined, + urlObj: ResponseURL | undefined, +) => { + try { + console.log('inside of second handler: \n' + JSON.stringify(urlObj)); + console.log(urlObj?.response_url.url); + let fileName = file?.filename || ''; // check if null + if (fileName.endsWith('.heic') || fileName.endsWith('.HEIC')) { + fileName = fileName.split('.')[0] + '.jpg'; + } + //build payload packet + console.log('LOG'); + const form = new FormData(); + form.append('key', JSON.stringify(urlObj.response_url.fields.key)); + form.append( + 'x-amz-algorithm', + JSON.stringify(urlObj.response_url.fields['x-amz-algorithm']), + ); + form.append( + 'x-amz-credential', + JSON.stringify(urlObj.response_url.fields['x-amz-credential']), + ); + form.append( + 'x-amz-date', + JSON.stringify(urlObj.response_url.fields['x-amz-date']), + ); + form.append('policy', JSON.stringify(urlObj.response_url.fields.policy)); + form.append( + 'x-amz-signature', + JSON.stringify(urlObj.response_url.fields['x-amz-signature']), + ); + + form.append('file', { + uri: file?.sourceURL, + type: 'video', + name: fileName, + }); + console.log('FORM: \n' + JSON.stringify(form)); + + const response = await fetch(urlObj.response_url.url, { + method: 'POST', + headers: { + 'Content-Type': 'multipart/form-data', + }, + body: form, + }); + console.log('response' + JSON.stringify(response)); + const status = response.status; + console.log('responmse text' + response.text()); + let data = await response.json(); + if (status === 200) { + console.log(data.response_msg); + return data.response_url; + } else { + if (status === 404) { + console.log( + `Please make sure that the email / username entered is registered with us. You may contact our customer support at ${TAGG_CUSTOMER_SUPPORT}`, + ); + } else { + console.log('bad'); + console.log(ERROR_SOMETHING_WENT_WRONG_REFRESH); + } + console.log(response); + return false; + } + } catch (error) { + console.log(error); + console.log(ERROR_SOMETHING_WENT_WRONG); + return false; + } +}; -- cgit v1.2.3-70-g09d2 From 3065e921b858f68eb1c1746bba116c01cbfb01dd Mon Sep 17 00:00:00 2001 From: George Rusu Date: Mon, 21 Jun 2021 19:34:11 -0700 Subject: Functional now --- src/services/MomentService.ts | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'src/services') diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index c5b2c530..28191421 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -258,31 +258,25 @@ export const handleVideoUpload = async ( console.log('inside of second handler: \n' + JSON.stringify(urlObj)); console.log(urlObj?.response_url.url); let fileName = file?.filename || ''; // check if null - if (fileName.endsWith('.heic') || fileName.endsWith('.HEIC')) { - fileName = fileName.split('.')[0] + '.jpg'; - } //build payload packet console.log('LOG'); const form = new FormData(); - form.append('key', JSON.stringify(urlObj.response_url.fields.key)); + form.append('key', urlObj.response_url.fields.key); form.append( 'x-amz-algorithm', - JSON.stringify(urlObj.response_url.fields['x-amz-algorithm']), + urlObj.response_url.fields['x-amz-algorithm'], ); form.append( 'x-amz-credential', - JSON.stringify(urlObj.response_url.fields['x-amz-credential']), + urlObj.response_url.fields['x-amz-credential'], ); - form.append( - 'x-amz-date', - JSON.stringify(urlObj.response_url.fields['x-amz-date']), - ); - form.append('policy', JSON.stringify(urlObj.response_url.fields.policy)); + form.append('x-amz-date', urlObj.response_url.fields['x-amz-date']); + form.append('policy', urlObj.response_url.fields.policy); form.append( 'x-amz-signature', - JSON.stringify(urlObj.response_url.fields['x-amz-signature']), + urlObj.response_url.fields['x-amz-signature'], ); - + console.log('date: \n' + urlObj.response_url.fields['x-amz-date']); form.append('file', { uri: file?.sourceURL, type: 'video', -- cgit v1.2.3-70-g09d2 From ae970faf6f560cc2fb55488acd49a04cfcb942c5 Mon Sep 17 00:00:00 2001 From: George Rusu Date: Tue, 22 Jun 2021 04:32:39 -0700 Subject: Add documentation and clean up code: improvements in code quality can be made, POC functionality ready --- src/components/moments/Moment.tsx | 91 ++++++++++++++++++++++----------------- src/services/MomentService.ts | 45 ++++++++++++------- src/types/types.ts | 4 +- 3 files changed, 83 insertions(+), 57 deletions(-) (limited to 'src/services') diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx index 11640b5b..d2f7be2e 100644 --- a/src/components/moments/Moment.tsx +++ b/src/components/moments/Moment.tsx @@ -1,25 +1,25 @@ +import AsyncStorage from '@react-native-community/async-storage'; import {useNavigation} from '@react-navigation/native'; import React from 'react'; import {Alert, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'; -import AsyncStorage from '@react-native-community/async-storage'; 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'; -import PlusIcon from '../../assets/icons/plus-icon.svg'; 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'; import { handlePresignedURL, handleVideoUpload, } from '../../services/MomentService'; +import {MomentType, ScreenType} from '../../types'; +import {normalize, SCREEN_WIDTH} from '../../utils'; +import MomentTile from './MomentTile'; interface MomentProps { title: string; @@ -46,8 +46,14 @@ const Moment: React.FC = ({ move, externalStyles, }) => { - // const navigation = useNavigation(); - + const navigation = useNavigation(); + /** + * 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({ smartAlbums: [ @@ -61,7 +67,7 @@ const Moment: React.FC = ({ height: 580, cropping: false, cropperToolbarTitle: 'select a video', - mediaType: 'any', + mediaType: 'video', }) .then(async (vid) => { if ('path' in vid) { @@ -82,36 +88,36 @@ const Moment: React.FC = ({ }); }; - // const navigateToImagePicker = () => { - // ImagePicker.openPicker({ - // smartAlbums: [ - // 'Favorites', - // 'RecentlyAdded', - // 'SelfPortraits', - // 'Screenshots', - // 'UserLibrary', - // ], - // width: 580, - // height: 580, - // cropping: true, - // cropperToolbarTitle: 'Upload a moment', - // mediaType: 'photo', - // }) - // .then((picture) => { - // if ('path' in picture) { - // navigation.navigate('CaptionScreen', { - // screenType, - // title: title, - // image: picture, - // }); - // } - // }) - // .catch((err) => { - // if (err.code && err.code !== 'E_PICKER_CANCELLED') { - // Alert.alert(ERROR_UPLOAD); - // } - // }); - // }; + const navigateToImagePicker = () => { + ImagePicker.openPicker({ + smartAlbums: [ + 'Favorites', + 'RecentlyAdded', + 'SelfPortraits', + 'Screenshots', + 'UserLibrary', + ], + width: 580, + height: 580, + cropping: true, + cropperToolbarTitle: 'Upload a moment', + mediaType: 'photo', + }) + .then((picture) => { + if ('path' in picture) { + navigation.navigate('CaptionScreen', { + screenType, + title: title, + image: picture, + }); + } + }) + .catch((err) => { + if (err.code && err.code !== 'E_PICKER_CANCELLED') { + Alert.alert(ERROR_UPLOAD); + } + }); + }; return ( @@ -150,6 +156,13 @@ const Moment: React.FC = ({ width={23} height={23} onPress={() => navigateToVideoPicker()} + color={'black'} + style={styles.horizontalMargin} + /> + navigateToImagePicker()} color={TAGG_LIGHT_BLUE} style={styles.horizontalMargin} /> @@ -179,7 +192,7 @@ const Moment: React.FC = ({ /> ))} {(images === undefined || images.length === 0) && !userXId && ( - navigateToVideoPicker()}> + navigateToImagePicker()}> diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index 28191421..d7a27146 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -1,7 +1,6 @@ import AsyncStorage from '@react-native-community/async-storage'; import {Image, Video} from 'react-native-image-crop-picker'; import RNFetchBlob from 'rn-fetch-blob'; -import {ProfileBody} from '../components'; import { MOMENTS_ENDPOINT, MOMENTTAG_ENDPOINT, @@ -14,7 +13,7 @@ import { ERROR_SOMETHING_WENT_WRONG, ERROR_SOMETHING_WENT_WRONG_REFRESH, } from '../constants/strings'; -import {MomentPostType, MomentTagType, ResponseURL} from '../types'; +import {MomentPostType, MomentTagType, PresignedURLResponse} from '../types'; import {checkImageUploadStatus} from '../utils'; export const postMoment = async ( @@ -217,7 +216,13 @@ export const deleteMomentTag = async (moment_tag_id: string) => { return false; } }; - +/** + * 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 ( value: string | undefined, filename: string | undefined, @@ -231,7 +236,7 @@ export const handlePresignedURL = async ( }), }); const status = response.status; - let data: ResponseURL = await response.json(); + let data: PresignedURLResponse = await response.json(); if (status === 200) { console.log(data.response_msg); return data; @@ -250,16 +255,29 @@ export const handlePresignedURL = async ( console.log(ERROR_SOMETHING_WENT_WRONG); } }; +/** + * 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 file: Video, Image, Undefined + * @param urlObj PresignedURLResponse | undefined + * @returns responseURL or boolean + */ export const handleVideoUpload = async ( file: Video | Image | undefined, - urlObj: ResponseURL | undefined, + urlObj: PresignedURLResponse | undefined, ) => { try { - console.log('inside of second handler: \n' + JSON.stringify(urlObj)); - console.log(urlObj?.response_url.url); - let fileName = file?.filename || ''; // check if null - //build payload packet - console.log('LOG'); + let fileName = file?.filename; + if (fileName === null || '') { + console.log('Invalid filename'); + return false; + } + if (urlObj === null || urlObj === undefined) { + console.log('Invalid urlObj'); + return false; + } + //build formData for POST request + // Could not get a forEach to work and could not assign directly, will look into cleaning this series of appends up later. const form = new FormData(); form.append('key', urlObj.response_url.fields.key); form.append( @@ -276,14 +294,12 @@ export const handleVideoUpload = async ( 'x-amz-signature', urlObj.response_url.fields['x-amz-signature'], ); - console.log('date: \n' + urlObj.response_url.fields['x-amz-date']); form.append('file', { uri: file?.sourceURL, + // 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, }); - console.log('FORM: \n' + JSON.stringify(form)); - const response = await fetch(urlObj.response_url.url, { method: 'POST', headers: { @@ -291,9 +307,7 @@ export const handleVideoUpload = async ( }, body: form, }); - console.log('response' + JSON.stringify(response)); const status = response.status; - console.log('responmse text' + response.text()); let data = await response.json(); if (status === 200) { console.log(data.response_msg); @@ -304,7 +318,6 @@ export const handleVideoUpload = async ( `Please make sure that the email / username entered is registered with us. You may contact our customer support at ${TAGG_CUSTOMER_SUPPORT}`, ); } else { - console.log('bad'); console.log(ERROR_SOMETHING_WENT_WRONG_REFRESH); } console.log(response); diff --git a/src/types/types.ts b/src/types/types.ts index 4da226b6..f6f23fc8 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -365,8 +365,8 @@ export type ReactionType = { id: string; type: ReactionOptionsType; }; - -export type ResponseURL = { +// used to handle direct S3 uploads by packaging presigned_url info into one object +export type PresignedURLResponse = { response_msg: string; response_url: { url: string; -- cgit v1.2.3-70-g09d2 From afb2c5b57faef1c809e8fdf3c7a5c1b1eab7e999 Mon Sep 17 00:00:00 2001 From: George Rusu Date: Tue, 22 Jun 2021 11:47:07 -0700 Subject: Refactored according to suggestions --- src/services/MomentService.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/services') diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index d7a27146..c7a88be3 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -238,7 +238,7 @@ export const handlePresignedURL = async ( const status = response.status; let data: PresignedURLResponse = await response.json(); if (status === 200) { - console.log(data.response_msg); + console.log('done'); return data; } else { if (status === 404) { @@ -308,16 +308,18 @@ export const handleVideoUpload = async ( body: form, }); const status = response.status; - let data = await response.json(); - if (status === 200) { - console.log(data.response_msg); - return data.response_url; + // let data = await response.json(); + if (status === 200 || status === 204) { + console.log('complete'); + return response; } else { if (status === 404) { console.log( `Please make sure that the email / username entered is registered with us. You may contact our customer support at ${TAGG_CUSTOMER_SUPPORT}`, ); } else { + console.log('FFFFFF \n'); + console.log(response); console.log(ERROR_SOMETHING_WENT_WRONG_REFRESH); } console.log(response); -- cgit v1.2.3-70-g09d2 From 270cec35e17ff4a1fa2ee1690e596b3e3003fe34 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 22 Jun 2021 18:17:52 -0400 Subject: Update service file to match updated endpoint --- Makefile | 14 ++++++++++++++ src/components/moments/Moment.tsx | 4 +--- src/services/MomentService.ts | 8 ++++++-- 3 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 Makefile (limited to 'src/services') diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..b0022598 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +build: + yarn + cd ios && pod install && cd .. + +clean: + -rm *.lock + -rm ios/*.lock + +deep_clean: + -rm *.lock + -rm ios/*.lock + -rm -rf /Users/ivan/Library/Developer/Xcode/DerivedData + -rm -rf node_modules + yarn cache clean diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx index d2f7be2e..34b2c7ea 100644 --- a/src/components/moments/Moment.tsx +++ b/src/components/moments/Moment.tsx @@ -1,4 +1,3 @@ -import AsyncStorage from '@react-native-community/async-storage'; import {useNavigation} from '@react-navigation/native'; import React from 'react'; import {Alert, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'; @@ -75,8 +74,7 @@ const Moment: React.FC = ({ if (fileName.endsWith('.heic') || fileName.endsWith('.HEIC')) { fileName = fileName.split('.')[0] + '.jpg'; } - const user = await AsyncStorage.getItem('username'); - let presignedURL = await handlePresignedURL(user || '', fileName); + let presignedURL = await handlePresignedURL(fileName, title); console.log('presigned' + JSON.stringify(presignedURL)); handleVideoUpload(vid, presignedURL); } diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index c7a88be3..d0ed56ab 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -224,15 +224,19 @@ export const deleteMomentTag = async (moment_tag_id: string) => { * @returns a PresignedURLResponse object */ export const handlePresignedURL = async ( - value: string | undefined, filename: string | undefined, + momentCategory: string, ) => { try { + const token = await AsyncStorage.getItem('token'); const response = await fetch(PRESIGNED_URL_ENDPOINT, { method: 'POST', + headers: { + Authorization: 'Token ' + token, + }, body: JSON.stringify({ - value, filename, + category: momentCategory, }), }); const status = response.status; -- cgit v1.2.3-70-g09d2 From 345a20590da5fd1cabc85c4e7b0b72c05e1f0665 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 23 Jun 2021 17:08:20 -0400 Subject: Squashed commit of the following: commit 8c2b915678b852f597c38ab00d18c22bf62d2051 Merge: cd586326 16f6341d Author: Ivan Chen Date: Wed Jun 23 16:26:12 2021 -0400 Merge pull request #473 from IvanIFChen/tma938-video-from-camera-video-streaming [TMA-938] Video From Camera & Video Streaming commit 16f6341daff56f827fbb6c6ade9cf1238e8e5f3e Author: Ivan Chen Date: Wed Jun 23 14:22:29 2021 -0400 Lint commit 7e7a3ea6b6462d295717db052c37d87e1f58a091 Author: Ivan Chen Date: Wed Jun 23 14:20:00 2021 -0400 Change to use image crop pickers instead commit 25d1a6514c9df784df27d22da648340ae018c1f8 Author: Ivan Chen Date: Wed Jun 23 13:37:39 2021 -0400 Minor logic cleanup commit 4ecc4525738dc8a56c9f37a05d0870e54cfa5be9 Author: Ivan Chen Date: Tue Jun 22 19:21:26 2021 -0400 Lint commit c3066187b9d253767e32880cc35ddd1abc6db6b3 Author: Ivan Chen Date: Tue Jun 22 19:16:04 2021 -0400 Remove test code commit ab13c937f1de947e68df03b443cf9fccb0cd54c9 Author: Ivan Chen Date: Tue Jun 22 19:14:06 2021 -0400 Add alert for gallery and camera commit 0561794c67d827c918029f2e06572fd03b4531fd Author: Ivan Chen Date: Tue Jun 22 18:51:57 2021 -0400 Finish poc for for video streaming commit 457b5cf34c032e7b73f972d5ba801ff420915190 Author: Ivan Chen Date: Tue Jun 22 18:37:20 2021 -0400 Squashed commit of the following: commit b6e4676b6ea262580453963ed6cfd85932d32341 Author: Ivan Chen Date: Mon Jun 21 19:04:08 2021 -0400 Add library, Add sample code commit 49dfe6a52e09bdb218e2b1ceb5a8df9ed46873f1 Author: Ivan Chen Date: Tue Jun 22 18:37:12 2021 -0400 Squashed commit of the following: commit b1c2002caa349dc96f81d3cc3e00ea545f4e654e Author: Ivan Chen Date: Tue Jun 22 15:28:18 2021 -0400 Add video for camerea commit ee890e517dd3688a4fd63c64592fdb6ce683d7b4 Author: Ivan Chen Date: Tue Jun 22 18:36:47 2021 -0400 Improve poc UX --- ios/Frontend/Info.plist | 4 + ios/Podfile.lock | 18 +- package.json | 1 + src/components/moments/Moment.tsx | 50 ++- src/components/moments/MomentPostContent.tsx | 41 ++- src/components/profile/MomentMoreInfoDrawer.tsx | 11 +- src/components/profile/ProfileBody.tsx | 1 - src/constants/regex.ts | 2 +- src/services/MomentService.ts | 16 +- yarn.lock | 395 ++++++++++++++---------- 10 files changed, 343 insertions(+), 196 deletions(-) (limited to 'src/services') diff --git a/ios/Frontend/Info.plist b/ios/Frontend/Info.plist index 0d75ca35..fa67d073 100644 --- a/ios/Frontend/Info.plist +++ b/ios/Frontend/Info.plist @@ -66,6 +66,8 @@ UILaunchStoryboardName LaunchScreen + NSMicrophoneUsageDescription + This lets you share video audio from your camera for a Moment post on your profile! UIRequiredDeviceCapabilities armv7 @@ -74,6 +76,8 @@ UIInterfaceOrientationPortrait + NSCameraUsageDescription + This lets you share photos/videos from your camera for a Moment post on your profile! UIViewControllerBasedStatusBarAppearance diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5679c1c7..86339554 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -333,11 +333,13 @@ PODS: - React-Core - react-native-contacts (6.0.5): - React-Core - - react-native-date-picker (3.3.1): + - react-native-date-picker (3.3.2): - React-Core - - react-native-document-picker (5.1.0): + - react-native-document-picker (5.2.0): - React-Core - - react-native-image-resizer (1.4.4): + - react-native-image-picker (4.0.4): + - React-Core + - react-native-image-resizer (1.4.5): - React-Core - react-native-netinfo (6.0.0): - React-Core @@ -532,6 +534,7 @@ DEPENDENCIES: - react-native-contacts (from `../node_modules/react-native-contacts`) - react-native-date-picker (from `../node_modules/react-native-date-picker`) - react-native-document-picker (from `../node_modules/react-native-document-picker`) + - react-native-image-picker (from `../node_modules/react-native-image-picker`) - react-native-image-resizer (from `../node_modules/react-native-image-resizer`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) @@ -637,6 +640,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-date-picker" react-native-document-picker: :path: "../node_modules/react-native-document-picker" + react-native-image-picker: + :path: "../node_modules/react-native-image-picker" react-native-image-resizer: :path: "../node_modules/react-native-image-resizer" react-native-netinfo: @@ -747,9 +752,10 @@ SPEC CHECKSUMS: react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c react-native-cameraroll: 88f4e62d9ecd0e1f253abe4f685474f2ea14bfa2 react-native-contacts: 931baebf460125c5a7bbce1c4521a96c69795123 - react-native-date-picker: 2dfef0fcb6c36d078bc62f5de3ca79eff7f42486 - react-native-document-picker: f2f73db94328c84e22144e369fb4a3ede47bc1f5 - react-native-image-resizer: 13ac4af788f88af36d0353a1324401ebabd04fe4 + react-native-date-picker: 96a07ca27a6225da8a3935324d85046028456b0f + react-native-document-picker: f1b5398801b332c77bc62ae0eae2116f49bdff26 + react-native-image-picker: c07b072faa83f3480b473a15ea3c19cc39b3d6fa + react-native-image-resizer: d9fb629a867335bdc13230ac2a58702bb8c8828f react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d react-native-safe-area-context: f0906bf8bc9835ac9a9d3f97e8bde2a997d8da79 react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865 diff --git a/package.json b/package.json index 92d6b501..399a40ea 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "react-native-haptic-feedback": "^1.11.0", "react-native-hyperlink": "^0.0.19", "react-native-image-crop-picker": "^0.36.0", + "react-native-image-picker": "^4.0.4", "react-native-image-resizer": "^1.4.4", "react-native-inappbrowser-reborn": "^3.5.0", "react-native-linear-gradient": "^2.5.6", diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx index 6441bca3..1c266307 100644 --- a/src/components/moments/Moment.tsx +++ b/src/components/moments/Moment.tsx @@ -5,6 +5,7 @@ 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 {useDispatch, useSelector} from 'react-redux'; import DeleteIcon from '../../assets/icons/delete-logo.svg'; import DownIcon from '../../assets/icons/down_icon.svg'; import BigPlusIcon from '../../assets/icons/plus-icon-white.svg'; @@ -16,6 +17,8 @@ import { handlePresignedURL, handleVideoUpload, } from '../../services/MomentService'; +import {loadUserMoments} from '../../store/actions'; +import {RootState} from '../../store/rootReducer'; import {MomentType, ScreenType} from '../../types'; import {normalize, SCREEN_WIDTH} from '../../utils'; import MomentTile from './MomentTile'; @@ -46,6 +49,23 @@ const Moment: React.FC = ({ externalStyles, }) => { const navigation = useNavigation(); + const dispatch = useDispatch(); + const { + user: {userId}, + } = useSelector((state: RootState) => state.user); + + const uploadVideo = async (filePath: string) => { + const randHash = Math.random().toString(36).substring(7); + const filename = `poc_${randHash}.mov`; + const presignedURL = await handlePresignedURL(filename, title); + if (presignedURL) { + console.log('presigned' + JSON.stringify(presignedURL)); + Alert.alert('Upload begin in background...'); + await handleVideoUpload(filename, filePath, presignedURL); + Alert.alert('Finish uploading, refreshing moments...'); + dispatch(loadUserMoments(userId)); + } + }; /** * 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, @@ -58,12 +78,19 @@ const Moment: React.FC = ({ mediaType: 'video', }) .then(async (vid) => { + // if ('path' in vid) { + // console.log(vid); + // // vid.path is compressed mp4, vid.sourceURL is uncompressed original + // if (vid.path) { + // uploadVideo(vid.path); + // } if (vid.path) { + const randHash = Math.random().toString(36).substring(7); navigation.navigate('CaptionScreen', { screenType, title, media: { - filename: `poc_${Math.random().toString(36).substring(7)}.mov`, + filename: `poc_${randHash}.mov`, uri: vid.path, isVideo: true, }, @@ -148,7 +175,26 @@ const Moment: React.FC = ({ navigateToVideoPicker()} + onPress={() => + Alert.alert('Video Upload', 'pick one', [ + { + text: 'gallery', + onPress: navigateToVideoPicker, + }, + { + text: 'camera (simulator will not work)', + onPress: () => + ImagePicker.openCamera({ + mediaType: 'video', + }).then((vid) => { + console.log(vid); + if (vid.path) { + uploadVideo(vid.path); + } + }), + }, + ]) + } color={'black'} style={styles.horizontalMargin} /> diff --git a/src/components/moments/MomentPostContent.tsx b/src/components/moments/MomentPostContent.tsx index aca2999c..27a68e47 100644 --- a/src/components/moments/MomentPostContent.tsx +++ b/src/components/moments/MomentPostContent.tsx @@ -3,6 +3,7 @@ import React, {useContext, useEffect, useRef, useState} from 'react'; import {Image, StyleSheet, Text, View, ViewProps} from 'react-native'; import {TouchableWithoutFeedback} from 'react-native-gesture-handler'; import Animated, {EasingNode} from 'react-native-reanimated'; +import Video from 'react-native-video'; import {useDispatch, useStore} from 'react-redux'; import {MomentContext} from '../../screens/profile/IndividualMoment'; import {RootState} from '../../store/rootReducer'; @@ -54,6 +55,12 @@ const MomentPostContent: React.FC = ({ useState(moment.comment_preview); const {keyboardVisible, scrollTo} = useContext(MomentContext); const [hideText, setHideText] = useState(false); + const isVideo = !( + moment.moment_url.endsWith('jpg') || + moment.moment_url.endsWith('JPG') || + moment.moment_url.endsWith('PNG') || + moment.moment_url.endsWith('png') + ); useEffect(() => { setTags(momentTags); @@ -83,12 +90,32 @@ const MomentPostContent: React.FC = ({ setVisible(!visible); setFadeValue(new Animated.Value(0)); }}> - + {isVideo ? ( +