From 7d57106ae614e42ea1d7d871a098e0acefc83762 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 20 Jul 2021 16:07:36 -0400 Subject: Add progress bar library, Add placeholder component --- src/screens/profile/ProfileScreen.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/screens/profile') diff --git a/src/screens/profile/ProfileScreen.tsx b/src/screens/profile/ProfileScreen.tsx index 3dd142e1..f8e50af1 100644 --- a/src/screens/profile/ProfileScreen.tsx +++ b/src/screens/profile/ProfileScreen.tsx @@ -1,7 +1,7 @@ +import {RouteProp} from '@react-navigation/native'; import React, {useEffect} from 'react'; import {StatusBar} from 'react-native'; -import {Content, TabsGradient} from '../../components'; -import {RouteProp} from '@react-navigation/native'; +import {Content, MomentUploadProgressBar, TabsGradient} from '../../components'; import {MainStackParams} from '../../routes/'; import {visitedUserProfile} from '../../services'; @@ -24,6 +24,7 @@ const ProfileScreen: React.FC = ({route}) => { return ( <> + -- cgit v1.2.3-70-g09d2 From 848204d684f6dfb8ada0856a50487d00ad2c77df Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 22 Jul 2021 16:08:45 -0400 Subject: Add action for handling all video uploads --- src/screens/profile/CaptionScreen.tsx | 39 ++++++++------ src/store/actions/user.ts | 98 ++++++++++++++++++++++++++++++++++- src/store/initialStates.ts | 1 - src/types/types.ts | 3 +- 4 files changed, 120 insertions(+), 21 deletions(-) (limited to 'src/screens/profile') diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 7f77bdca..88ff0ecc 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -42,6 +42,7 @@ import { postMomentTags, } from '../../services'; import { + handleVideoMomentUpload, loadUserMoments, updateProfileCompletionStage, } from '../../store/actions'; @@ -76,6 +77,8 @@ const CaptionScreen: React.FC = ({route, navigation}) => { const [tags, setTags] = useState([]); const [taggedUsersText, setTaggedUsersText] = useState(''); const [momentCategory, setMomentCategory] = useState(); + // only used for upload purposes, undefined for editing is fine + const videoDuration = moment ? undefined : route.params.media!.videoDuration; const mediaUri = moment ? moment.moment_url : route.params.media!.uri; // TODO: change this once moment refactor is done const isMediaAVideo = moment @@ -166,18 +169,17 @@ const CaptionScreen: React.FC = ({route, navigation}) => { return; } let profileCompletionStage; - let momentId; // separate upload logic for image/video if (isMediaAVideo) { - const presignedURLResponse = await handlePresignedURL(momentCategory); - if (!presignedURLResponse) { - handleFailed(); - return; - } - momentId = presignedURLResponse.moment_id; - const fileHash = presignedURLResponse.response_url.fields.key; - if (fileHash !== null && fileHash !== '' && fileHash !== undefined) { - await handleVideoUpload(mediaUri, presignedURLResponse); + if (videoDuration) { + dispatch( + handleVideoMomentUpload( + mediaUri, + videoDuration, + momentCategory, + formattedTags(), + ), + ); } else { handleFailed(); } @@ -193,13 +195,16 @@ const CaptionScreen: React.FC = ({route, navigation}) => { return; } profileCompletionStage = momentResponse.profile_completion_stage; - momentId = momentResponse.moment_id; - } - if (momentId) { - const momentTagResponse = await postMomentTags(momentId, formattedTags()); - if (!momentTagResponse) { - handleFailed(); - return; + const momentId = momentResponse.moment_id; + if (momentId) { + const momentTagResponse = await postMomentTags( + momentId, + formattedTags(), + ); + if (!momentTagResponse) { + handleFailed(); + return; + } } } if (!isMediaAVideo) { diff --git a/src/store/actions/user.ts b/src/store/actions/user.ts index b1cb8719..1acbb519 100644 --- a/src/store/actions/user.ts +++ b/src/store/actions/user.ts @@ -1,13 +1,21 @@ import AsyncStorage from '@react-native-community/async-storage'; -import {StreamChat} from 'stream-chat'; import {Action, ThunkAction} from '@reduxjs/toolkit'; +import {StreamChat} from 'stream-chat'; import { getProfilePic, + handlePresignedURL, + handleVideoUpload, loadProfileInfo, + postMomentTags, removeBadgesService, sendSuggestedPeopleLinked, } from '../../services'; -import {UniversityBadge, UserType} from '../../types/types'; +import { + MomentUploadProgressBarType, + MomentUploadStatusType, + UniversityBadge, + UserType, +} from '../../types/types'; import {getTokenOrLogout} from '../../utils'; import { clearHeaderAndProfileImages, @@ -15,6 +23,7 @@ import { profileBadgesUpdated, profileCompletionStageUpdated, setIsOnboardedUser, + setMomentUploadProgressBar, setNewNotificationReceived, setNewVersionAvailable, setReplyPosted, @@ -275,3 +284,88 @@ export const suggestedPeopleAnimatedTutorialFinished = ); } }; + +/** + * state is now UploadingToS3: + * - get presigned url (backend creates the moment object) + * - upload moment tags + * - upload video to s3 + * state is now WaitingForDoneProcessing + */ +export const handleVideoMomentUpload = + ( + videoUri: string, + videoLength: number, + momentCategory: string, + formattedTags: { + x: number; + y: number; + z: number; + user_id: string; + }[], + ): ThunkAction, RootState, unknown, Action> => + async (dispatch) => { + try { + const handleError = (reason: string) => { + console.error('Moment video upload failed,', reason); + dispatch({ + type: setMomentUploadProgressBar.type, + payload: { + momentUploadProgressBar: { + ...momentUploadProgressBar, + status: MomentUploadStatusType.Error, + }, + }, + }); + }; + let momentUploadProgressBar: MomentUploadProgressBarType = { + status: MomentUploadStatusType.UploadingToS3, + momentId: '', + originalVideoDuration: videoLength, + }; + // set progress bar as loading + dispatch({ + type: setMomentUploadProgressBar.type, + payload: {momentUploadProgressBar}, + }); + // get a presigned url for the video + const presignedURLResponse = await handlePresignedURL(momentCategory); + if (!presignedURLResponse) { + handleError('Presigned URL failed'); + return; + } + const momentId = presignedURLResponse.moment_id; + const fileHash = presignedURLResponse.response_url.fields.key; + // upload moment tags, now that we have a moment id + const momentTagResponse = await postMomentTags(momentId, formattedTags); + if (!momentTagResponse) { + handleError('Upload moment tags failed'); + return; + } + if (!fileHash) { + handleError('Unable to parse file hash from presigned response'); + return; + } + // upload video to s3 + const videoUploadResponse = await handleVideoUpload( + videoUri, + presignedURLResponse, + ); + if (!videoUploadResponse) { + handleError('Video upload failed'); + return; + } + dispatch({ + type: setMomentUploadProgressBar.type, + payload: { + momentUploadProgressBar: { + ...momentUploadProgressBar, + status: MomentUploadStatusType.WaitingForDoneProcessing, + momentId, + }, + }, + }); + } catch (error) { + console.log(error); + } + }; diff --git a/src/store/initialStates.ts b/src/store/initialStates.ts index ddfdf5d2..7d8cf439 100644 --- a/src/store/initialStates.ts +++ b/src/store/initialStates.ts @@ -11,7 +11,6 @@ import { CommentThreadType, MomentPostType, MomentUploadProgressBarType, - MomentUploadStatusType, UniversityType, } from './../types/types'; diff --git a/src/types/types.ts b/src/types/types.ts index 34bf73ac..685e3784 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -68,7 +68,8 @@ export interface MomentUploadProgressBarType { } export enum MomentUploadStatusType { - Uploading = 'Uploading', + UploadingToS3 = 'UploadingToS3', + WaitingForDoneProcessing = 'WaitingForDoneProcessing', Done = 'Done', Error = 'Error', } -- cgit v1.2.3-70-g09d2 From 8181eacc003342fc6bff649b8d1bd793a88efee5 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 22 Jul 2021 18:13:08 -0400 Subject: Handle error case for displaying alert, Add logic to auto reload --- src/components/moments/MomentUploadProgressBar.tsx | 25 ++++++++++++++-------- src/routes/main/MainStackNavigator.tsx | 1 + src/screens/profile/CaptionScreen.tsx | 16 ++++---------- 3 files changed, 21 insertions(+), 21 deletions(-) (limited to 'src/screens/profile') diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index 742c403b..afda16e2 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -10,6 +10,7 @@ import { import {SafeAreaView} from 'react-native-safe-area-context'; import {useDispatch, useSelector} from 'react-redux'; import {checkMomentUploadFinished} from '../../services'; +import {loadUserMoments} from '../../store/actions'; import {setMomentUploadProgressBar} from '../../store/reducers'; import {RootState} from '../../store/rootReducer'; import {MomentUploadStatusType} from '../../types'; @@ -21,6 +22,9 @@ interface MomentUploadProgressBarProps {} const MomentUploadProgressBar: React.FC = ({}) => { const dispatch = useDispatch(); + const {userId: loggedInUserId} = useSelector( + (state: RootState) => state.user.user, + ); const {momentUploadProgressBar} = useSelector( (state: RootState) => state.user, ); @@ -72,17 +76,19 @@ const MomentUploadProgressBar: React.FC = }, 5 * 1000); // timeout if takes longer than 1 minute to process setTimeout(() => { - console.error('Check for done processing timed out'); clearInterval(timer); - dispatch({ - type: setMomentUploadProgressBar.type, - payload: { - momentUploadProgressBar: { - ...momentUploadProgressBar, - status: MomentUploadStatusType.Error, + if (!doneProcessing) { + console.error('Check for done processing timed out'); + dispatch({ + type: setMomentUploadProgressBar.type, + payload: { + momentUploadProgressBar: { + ...momentUploadProgressBar, + status: MomentUploadStatusType.Error, + }, }, - }, - }); + }); + } }, 60 * 1000); return () => clearInterval(timer); } @@ -109,6 +115,7 @@ const MomentUploadProgressBar: React.FC = progress.value = 0; // clear this component after a duration setTimeout(() => { + dispatch(loadUserMoments(loggedInUserId)); dispatch({ type: setMomentUploadProgressBar.type, payload: { diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx index 585980b5..8def19e3 100644 --- a/src/routes/main/MainStackNavigator.tsx +++ b/src/routes/main/MainStackNavigator.tsx @@ -58,6 +58,7 @@ export type MainStackParams = { media: { uri: string; isVideo: boolean; + videoDuration: number | undefined; }; selectedTags?: MomentTagType[]; }; diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 88ff0ecc..6ba1791c 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -34,13 +34,7 @@ import { } from '../../constants/strings'; import * as RootNavigation from '../../RootNavigation'; import {MainStackParams} from '../../routes'; -import { - handlePresignedURL, - handleVideoUpload, - patchMoment, - postMoment, - postMomentTags, -} from '../../services'; +import {patchMoment, postMoment, postMomentTags} from '../../services'; import { handleVideoMomentUpload, loadUserMoments, @@ -136,11 +130,7 @@ const CaptionScreen: React.FC = ({route, navigation}) => { navigation.popToTop(); RootNavigation.navigate('ProfileTab'); setTimeout(() => { - if (isMediaAVideo) { - Alert.alert( - 'Beautiful, the Moment was uploaded successfully! Check back in a bit and refresh to see it!', - ); - } else { + if (!isMediaAVideo) { Alert.alert(SUCCESS_PIC_UPLOAD); } }, 500); @@ -182,6 +172,7 @@ const CaptionScreen: React.FC = ({route, navigation}) => { ); } else { handleFailed(); + return; } } else { const momentResponse = await postMoment( @@ -330,6 +321,7 @@ const CaptionScreen: React.FC = ({route, navigation}) => { media: { uri: mediaUri, isVideo: isMediaAVideo, + videoDuration, }, selectedTags: tags, }) -- cgit v1.2.3-70-g09d2 From 4f68cb3f7280222223ccc2ad086125f113660921 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 22 Jul 2021 18:32:13 -0400 Subject: Made progress bar non-stick to top --- src/components/profile/Content.tsx | 2 ++ src/screens/profile/ProfileScreen.tsx | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/screens/profile') diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx index 2d1002dd..9edd890d 100644 --- a/src/components/profile/Content.tsx +++ b/src/components/profile/Content.tsx @@ -6,6 +6,7 @@ import Animated, { useSharedValue, } from 'react-native-reanimated'; import {useDispatch, useSelector, useStore} from 'react-redux'; +import {MomentUploadProgressBar} from '..'; import { blockUnblockUser, loadFriendsData, @@ -140,6 +141,7 @@ const Content: React.FC = ({userXId, screenType}) => { refreshControl={ }> + {!userXId && } = ({route}) => { return ( <> - -- cgit v1.2.3-70-g09d2