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/components/moments/MomentUploadProgressBar.tsx | 14 ++++++++++++++ src/components/moments/index.ts | 1 + src/screens/profile/ProfileScreen.tsx | 5 +++-- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 src/components/moments/MomentUploadProgressBar.tsx (limited to 'src') diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx new file mode 100644 index 00000000..1dee4185 --- /dev/null +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import {StyleSheet} from 'react-native'; +import * as Progress from 'react-native-progress'; + +interface MomentUploadProgressBarProps {} + +const MomentUploadProgressBar: React.FC = + ({}) => { + return ; + }; + +const styles = StyleSheet.create({}); + +export default MomentUploadProgressBar; diff --git a/src/components/moments/index.ts b/src/components/moments/index.ts index 16c9aed2..3f33ec53 100644 --- a/src/components/moments/index.ts +++ b/src/components/moments/index.ts @@ -5,3 +5,4 @@ export {default as TagFriendsFooter} from './TagFriendsFoooter'; export {default as MomentPost} from './MomentPost'; export {default as TaggedUsersDrawer} from './TaggedUsersDrawer'; export {default as TrimmerPlayer} from './TrimmerPlayer'; +export {default as MomentUploadProgressBar} from './MomentUploadProgressBar'; 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 142c84c7c45411b9badf7da3182c9e4bd0e96e38 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 20 Jul 2021 17:35:20 -0400 Subject: Add gradient progress bar --- src/components/common/GradientProgressBar.tsx | 45 ++++++++++++++++++++ src/components/common/index.ts | 1 + src/components/moments/MomentUploadProgressBar.tsx | 49 +++++++++++++++++++--- src/constants/constants.ts | 1 + src/store/initialStates.ts | 2 + src/types/types.ts | 7 ++++ 6 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 src/components/common/GradientProgressBar.tsx (limited to 'src') diff --git a/src/components/common/GradientProgressBar.tsx b/src/components/common/GradientProgressBar.tsx new file mode 100644 index 00000000..3483f8ac --- /dev/null +++ b/src/components/common/GradientProgressBar.tsx @@ -0,0 +1,45 @@ +import React, {FC} from 'react'; +import {StyleSheet, View, ViewProps} from 'react-native'; +import LinearGradient from 'react-native-linear-gradient'; +import { + TAGG_LIGHT_BLUE_2, + TAGG_LIGHT_BLUE_3, + TAGG_PURPLE, +} from '../../constants'; +import {normalize, SCREEN_WIDTH} from '../../utils'; + +interface GradientProgressBarProps extends ViewProps { + progress: number; +} + +const GradientProgressBar: FC = ({ + style, + progress, +}) => { + return ( + + + + ); +}; +const styles = StyleSheet.create({ + container: { + borderRadius: 6.5, + }, + bar: { + width: SCREEN_WIDTH * 0.9, + height: normalize(10), + borderRadius: 6.5, + }, + blank: { + alignSelf: 'flex-end', + height: normalize(10), + width: '80%', + backgroundColor: TAGG_LIGHT_BLUE_3, + }, +}); + +export default GradientProgressBar; diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 4f5c0232..5edbb3ad 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -29,3 +29,4 @@ export {default as TaggUserRowCell} from './TaggUserRowCell'; export {default as LikeButton} from './LikeButton'; export {default as TaggUserSelectionCell} from './TaggUserSelectionCell'; export {default as MomentTags} from './MomentTags'; +export {default as GradientProgressBar} from './GradientProgressBar'; diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index 1dee4185..bbd0cb06 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -1,14 +1,53 @@ -import React from 'react'; -import {StyleSheet} from 'react-native'; -import * as Progress from 'react-native-progress'; +import React, {useState} from 'react'; +import {StyleSheet, Text} from 'react-native'; +import {View} from 'react-native-animatable'; +import Animated, {useSharedValue} from 'react-native-reanimated'; +import {SafeAreaView} from 'react-native-safe-area-context'; +import {useSelector} from 'react-redux'; +import {RootState} from '../../store/rootReducer'; +import {normalize, StatusBarHeight} from '../../utils'; +import {GradientProgressBar} from '../common'; interface MomentUploadProgressBarProps {} const MomentUploadProgressBar: React.FC = ({}) => { - return ; + const {momentUploadStatus} = useSelector((state: RootState) => state.user); + // const [progress, setProgress] = useState(0); + // const progressTime = useSharedValue(0); + // const [indeterminate, setIndeterminate] = useState(false); + // const range = new Animated.Value(0); + // const transX = new Animated.Value(0); + + return ( + + + Uploading Moment... + + + + ); }; -const styles = StyleSheet.create({}); +const styles = StyleSheet.create({ + background: { + position: 'absolute', + zIndex: 999, + height: StatusBarHeight + 100, + backgroundColor: 'white', + width: '100%', + alignItems: 'center', + }, + container: { + justifyContent: 'space-evenly', + height: '100%', + }, + text: { + fontSize: normalize(14), + fontWeight: 'bold', + lineHeight: 17, + marginVertical: 12, + }, +}); export default MomentUploadProgressBar; diff --git a/src/constants/constants.ts b/src/constants/constants.ts index 13a73208..476e7af4 100644 --- a/src/constants/constants.ts +++ b/src/constants/constants.ts @@ -69,6 +69,7 @@ export const TAGG_DARK_BLUE = '#4E699C'; export const TAGG_DARK_PURPLEISH_BLUE = '#4755A1'; export const TAGG_LIGHT_BLUE: string = '#698DD3'; export const TAGG_LIGHT_BLUE_2: string = '#6EE7E7'; +export const TAGG_LIGHT_BLUE_3 = '#DDE8FE'; export const TAGG_LIGHT_PURPLE = '#F4DDFF'; export const RADIO_BUTTON_GREY: string = '#BEBEBE'; diff --git a/src/store/initialStates.ts b/src/store/initialStates.ts index 92a1e456..5ae62838 100644 --- a/src/store/initialStates.ts +++ b/src/store/initialStates.ts @@ -10,6 +10,7 @@ import { import { CommentThreadType, MomentPostType, + MomentUploadStatusType, UniversityType, } from './../types/types'; @@ -48,6 +49,7 @@ export const NO_USER_DATA = { profile: NO_PROFILE, avatar: undefined, cover: undefined, + momentUploadStatus: MomentUploadStatusType.Empty, isOnboardedUser: false, newVersionAvailable: false, newNotificationReceived: false, diff --git a/src/types/types.ts b/src/types/types.ts index 5f70d1f8..930f833b 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -61,6 +61,13 @@ export interface ProfileInfoType { is_private: boolean; } +export enum MomentUploadStatusType { + Empty = 'Empty', + Uploading = 'Uploading', + Done = 'Done', + Error = 'Error', +} + export interface SocialAccountType { handle?: string; profile_pic?: string; -- cgit v1.2.3-70-g09d2 From 3d616c5d445919c476aa926c6d2c44db38cbab37 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 20 Jul 2021 17:38:04 -0400 Subject: Move out width style --- src/components/common/GradientProgressBar.tsx | 3 +-- src/components/moments/MomentUploadProgressBar.tsx | 10 ++++++---- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/components/common/GradientProgressBar.tsx b/src/components/common/GradientProgressBar.tsx index 3483f8ac..2f7f0431 100644 --- a/src/components/common/GradientProgressBar.tsx +++ b/src/components/common/GradientProgressBar.tsx @@ -6,7 +6,7 @@ import { TAGG_LIGHT_BLUE_3, TAGG_PURPLE, } from '../../constants'; -import {normalize, SCREEN_WIDTH} from '../../utils'; +import {normalize} from '../../utils'; interface GradientProgressBarProps extends ViewProps { progress: number; @@ -30,7 +30,6 @@ const styles = StyleSheet.create({ borderRadius: 6.5, }, bar: { - width: SCREEN_WIDTH * 0.9, height: normalize(10), borderRadius: 6.5, }, diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index bbd0cb06..7310727e 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -1,11 +1,10 @@ -import React, {useState} from 'react'; +import React from 'react'; import {StyleSheet, Text} from 'react-native'; import {View} from 'react-native-animatable'; -import Animated, {useSharedValue} from 'react-native-reanimated'; import {SafeAreaView} from 'react-native-safe-area-context'; import {useSelector} from 'react-redux'; import {RootState} from '../../store/rootReducer'; -import {normalize, StatusBarHeight} from '../../utils'; +import {normalize, SCREEN_WIDTH, StatusBarHeight} from '../../utils'; import {GradientProgressBar} from '../common'; interface MomentUploadProgressBarProps {} @@ -23,7 +22,7 @@ const MomentUploadProgressBar: React.FC = Uploading Moment... - + ); @@ -48,6 +47,9 @@ const styles = StyleSheet.create({ lineHeight: 17, marginVertical: 12, }, + bar: { + width: SCREEN_WIDTH * 0.9, + }, }); export default MomentUploadProgressBar; -- cgit v1.2.3-70-g09d2 From 74c853034e893aeda18ee78f59e4539fba6d8fc0 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 20 Jul 2021 17:58:31 -0400 Subject: Fix logic to make progress bar work --- src/components/common/GradientProgressBar.tsx | 10 +++++++--- src/components/moments/MomentUploadProgressBar.tsx | 18 +++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/components/common/GradientProgressBar.tsx b/src/components/common/GradientProgressBar.tsx index 2f7f0431..fc62bd3c 100644 --- a/src/components/common/GradientProgressBar.tsx +++ b/src/components/common/GradientProgressBar.tsx @@ -1,6 +1,7 @@ import React, {FC} from 'react'; -import {StyleSheet, View, ViewProps} from 'react-native'; +import {StyleSheet, ViewProps, ViewStyle} from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; +import Animated, {useAnimatedStyle} from 'react-native-reanimated'; import { TAGG_LIGHT_BLUE_2, TAGG_LIGHT_BLUE_3, @@ -9,19 +10,22 @@ import { import {normalize} from '../../utils'; interface GradientProgressBarProps extends ViewProps { - progress: number; + progress: Animated.SharedValue; } const GradientProgressBar: FC = ({ style, progress, }) => { + const animatedProgressStyle = useAnimatedStyle(() => ({ + width: `${(1 - progress.value) * 100}%`, + })); return ( - + ); }; diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index 7310727e..26c20a46 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -1,6 +1,7 @@ -import React from 'react'; +import React, {useEffect} from 'react'; import {StyleSheet, Text} from 'react-native'; import {View} from 'react-native-animatable'; +import {Easing, useSharedValue, withTiming} from 'react-native-reanimated'; import {SafeAreaView} from 'react-native-safe-area-context'; import {useSelector} from 'react-redux'; import {RootState} from '../../store/rootReducer'; @@ -12,17 +13,20 @@ interface MomentUploadProgressBarProps {} const MomentUploadProgressBar: React.FC = ({}) => { const {momentUploadStatus} = useSelector((state: RootState) => state.user); - // const [progress, setProgress] = useState(0); - // const progressTime = useSharedValue(0); - // const [indeterminate, setIndeterminate] = useState(false); - // const range = new Animated.Value(0); - // const transX = new Animated.Value(0); + const progress = useSharedValue(0); + + useEffect(() => { + progress.value = withTiming(1, { + duration: 5000, + easing: Easing.linear, + }); + }, []); return ( Uploading Moment... - + ); -- cgit v1.2.3-70-g09d2 From fbb9d410b4a525dd6f21d5de7e1e3844b6cf78a7 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 20 Jul 2021 18:51:17 -0400 Subject: Add state check --- src/components/moments/MomentUploadProgressBar.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index 26c20a46..285f4e84 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -5,6 +5,7 @@ import {Easing, useSharedValue, withTiming} from 'react-native-reanimated'; import {SafeAreaView} from 'react-native-safe-area-context'; import {useSelector} from 'react-redux'; import {RootState} from '../../store/rootReducer'; +import {MomentUploadStatusType} from '../../types'; import {normalize, SCREEN_WIDTH, StatusBarHeight} from '../../utils'; import {GradientProgressBar} from '../common'; @@ -16,11 +17,13 @@ const MomentUploadProgressBar: React.FC = const progress = useSharedValue(0); useEffect(() => { - progress.value = withTiming(1, { - duration: 5000, - easing: Easing.linear, - }); - }, []); + if (momentUploadStatus === MomentUploadStatusType.Uploading) { + progress.value = withTiming(1, { + duration: 30 * 1000, + easing: Easing.out(Easing.quad), + }); + } + }, [momentUploadStatus]); return ( -- cgit v1.2.3-70-g09d2 From 9b94f60df0b62a9d3762a1963ec7dac024658a51 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 21 Jul 2021 19:11:32 -0400 Subject: Update progress bar type --- src/components/moments/MomentUploadProgressBar.tsx | 16 +++++++++++----- src/constants/api.ts | 1 + src/store/initialStates.ts | 3 ++- src/types/types.ts | 7 ++++++- 4 files changed, 20 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index 285f4e84..07d876e8 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -13,17 +13,23 @@ interface MomentUploadProgressBarProps {} const MomentUploadProgressBar: React.FC = ({}) => { - const {momentUploadStatus} = useSelector((state: RootState) => state.user); - const progress = useSharedValue(0); + const {momentUploadProgressBar} = useSelector( + (state: RootState) => state.user, + ); + const progress = useSharedValue(0.001); useEffect(() => { - if (momentUploadStatus === MomentUploadStatusType.Uploading) { + if ( + momentUploadProgressBar?.status === MomentUploadStatusType.Uploading + ) { progress.value = withTiming(1, { - duration: 30 * 1000, + duration: momentUploadProgressBar.originalVideoDuration * 1000, easing: Easing.out(Easing.quad), }); } - }, [momentUploadStatus]); + }, [momentUploadProgressBar?.status]); + + useEffect(() => {}, []); return ( diff --git a/src/constants/api.ts b/src/constants/api.ts index 6dab1153..ec2f0897 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -39,6 +39,7 @@ export const COMMENTS_ENDPOINT: string = API_URL + 'comments/'; export const COMMENT_REACTIONS_ENDPOINT: string = API_URL + 'reaction-comment/'; export const COMMENT_REACTIONS_REPLY_ENDPOINT: string = API_URL + 'reaction-reply/'; export const PRESIGNED_URL_ENDPOINT: string = API_URL + 'presigned-url/'; +export const CHECK_MOMENT_UPLOAD_FINISHED_ENDPOINT: string = API_URL + 'moments/check_upload_finished/'; export const FRIENDS_ENDPOINT: string = API_URL + 'friends/'; export const ALL_USERS_ENDPOINT: string = API_URL + 'users/'; export const REPORT_ISSUE_ENDPOINT: string = API_URL + 'report/'; diff --git a/src/store/initialStates.ts b/src/store/initialStates.ts index 5ae62838..ddfdf5d2 100644 --- a/src/store/initialStates.ts +++ b/src/store/initialStates.ts @@ -10,6 +10,7 @@ import { import { CommentThreadType, MomentPostType, + MomentUploadProgressBarType, MomentUploadStatusType, UniversityType, } from './../types/types'; @@ -49,7 +50,7 @@ export const NO_USER_DATA = { profile: NO_PROFILE, avatar: undefined, cover: undefined, - momentUploadStatus: MomentUploadStatusType.Empty, + momentUploadProgressBar: undefined, isOnboardedUser: false, newVersionAvailable: false, newNotificationReceived: false, diff --git a/src/types/types.ts b/src/types/types.ts index 930f833b..2001426a 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -61,8 +61,13 @@ export interface ProfileInfoType { is_private: boolean; } +export interface MomentUploadProgressBarType { + status: MomentUploadStatusType; + originalVideoDuration: number; + momentId: string; +} + export enum MomentUploadStatusType { - Empty = 'Empty', Uploading = 'Uploading', Done = 'Done', Error = 'Error', -- cgit v1.2.3-70-g09d2 From 7dc3d23577355b18e7089251823ff254e65f1137 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 21 Jul 2021 19:12:58 -0400 Subject: Add default case for progress bar --- src/components/moments/MomentUploadProgressBar.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index 07d876e8..28fbd8cb 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -16,7 +16,7 @@ const MomentUploadProgressBar: React.FC = const {momentUploadProgressBar} = useSelector( (state: RootState) => state.user, ); - const progress = useSharedValue(0.001); + const progress = useSharedValue(0); useEffect(() => { if ( @@ -29,7 +29,9 @@ const MomentUploadProgressBar: React.FC = } }, [momentUploadProgressBar?.status]); - useEffect(() => {}, []); + if (!momentUploadProgressBar) { + return null; + } return ( -- cgit v1.2.3-70-g09d2 From d518a5e7ae053ef55ca4ede254586f77be86e768 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Wed, 21 Jul 2021 19:19:30 -0400 Subject: Add service, Add logic for checking upload finished --- src/components/moments/MomentUploadProgressBar.tsx | 16 ++++++++++++++++ src/services/MomentService.ts | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'src') diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index 28fbd8cb..0d84236d 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -4,6 +4,7 @@ import {View} from 'react-native-animatable'; import {Easing, useSharedValue, withTiming} from 'react-native-reanimated'; import {SafeAreaView} from 'react-native-safe-area-context'; import {useSelector} from 'react-redux'; +import {checkMomentUploadFinished} from '../../services'; import {RootState} from '../../store/rootReducer'; import {MomentUploadStatusType} from '../../types'; import {normalize, SCREEN_WIDTH, StatusBarHeight} from '../../utils'; @@ -18,6 +19,21 @@ const MomentUploadProgressBar: React.FC = ); const progress = useSharedValue(0); + useEffect(() => { + if ( + momentUploadProgressBar?.status === MomentUploadStatusType.Uploading + ) { + const timer = setInterval(async () => { + if (checkMomentUploadFinished(momentUploadProgressBar.momentId)) { + // call upload finished action + } + }, 5 * 1000); + setTimeout(() => { + clearInterval(timer); + }, 5 * 60 * 1000); + } + }, []); + useEffect(() => { if ( momentUploadProgressBar?.status === MomentUploadStatusType.Uploading diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index 25d44041..2a0d9113 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -1,6 +1,7 @@ import AsyncStorage from '@react-native-community/async-storage'; import RNFetchBlob from 'rn-fetch-blob'; import { + CHECK_MOMENT_UPLOAD_FINISHED_ENDPOINT, MOMENTS_ENDPOINT, MOMENTTAG_ENDPOINT, MOMENT_TAGS_ENDPOINT, @@ -320,3 +321,22 @@ export const handleVideoUpload = async ( } return false; }; + +export const checkMomentUploadFinished = async (momentId: string) => { + try { + const token = await AsyncStorage.getItem('token'); + const response = await fetch( + CHECK_MOMENT_UPLOAD_FINISHED_ENDPOINT + '?moment_id=' + momentId, + { + method: 'GET', + headers: { + Authorization: 'Token ' + token, + }, + }, + ); + return response.status === 200; + } catch (error) { + console.error(error); + return false; + } +}; -- cgit v1.2.3-70-g09d2 From bc82aaf481949d690af814b9dd4a0e9cab387011 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 22 Jul 2021 15:09:39 -0400 Subject: Rename util function, Pass video duration to caption screen --- src/components/camera/GalleryIcon.tsx | 4 ++-- src/components/moments/TrimmerPlayer.tsx | 2 +- src/routes/main/MainStackNavigator.tsx | 2 +- src/screens/upload/EditMedia.tsx | 14 ++++++++++---- src/store/reducers/userReducer.ts | 5 +++++ src/types/types.ts | 2 +- src/utils/camera.ts | 2 +- 7 files changed, 21 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/components/camera/GalleryIcon.tsx b/src/components/camera/GalleryIcon.tsx index ca2d2559..44297d6d 100644 --- a/src/components/camera/GalleryIcon.tsx +++ b/src/components/camera/GalleryIcon.tsx @@ -1,6 +1,6 @@ import React from 'react'; import {Image, Text, TouchableOpacity, View} from 'react-native'; -import {navigateToImagePicker} from '../../utils/camera'; +import {navigateToMediaPicker} from '../../utils/camera'; import {ImageOrVideo} from 'react-native-image-crop-picker'; import {styles} from './styles'; @@ -19,7 +19,7 @@ export const GalleryIcon: React.FC = ({ }) => { return ( navigateToImagePicker(callback)} + onPress={() => navigateToMediaPicker(callback)} style={styles.saveButton}> {mostRecentPhotoUri !== '' ? ( = ({ repeat={true} onLoad={(payload) => { setEnd(payload.duration); - handleLoad(payload.naturalSize); + handleLoad(payload.naturalSize, payload.duration); }} onProgress={(e) => { if (!paused) { diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx index 11e9d08d..585980b5 100644 --- a/src/routes/main/MainStackNavigator.tsx +++ b/src/routes/main/MainStackNavigator.tsx @@ -46,7 +46,7 @@ export type MainStackParams = { }; CaptionScreen: { screenType: ScreenType; - media?: {uri: string; isVideo: boolean}; + media?: {uri: string; isVideo: boolean; videoDuration: number | undefined}; selectedCategory?: string; selectedTags?: MomentTagType[]; moment?: MomentType; diff --git a/src/screens/upload/EditMedia.tsx b/src/screens/upload/EditMedia.tsx index 1dc408ee..38450337 100644 --- a/src/screens/upload/EditMedia.tsx +++ b/src/screens/upload/EditMedia.tsx @@ -43,6 +43,7 @@ export const EditMedia: React.FC = ({route, navigation}) => { const vidRef = useRef(null); const [cropLoading, setCropLoading] = useState(false); const [hideTrimmer, setHideTrimmer] = useState(true); + const [videoDuration, setVideoDuration] = useState(); // Stores the coordinates of the cropped image const [x0, setX0] = useState(); @@ -139,7 +140,7 @@ export const EditMedia: React.FC = ({route, navigation}) => { mediaUri, (croppedURL: string) => { setCropLoading(false); - // Pass the trimmed/cropped video + // Pass the cropped video callback(croppedURL); }, videoCrop, @@ -334,8 +335,12 @@ export const EditMedia: React.FC = ({route, navigation}) => { height: SCREEN_WIDTH / aspectRatio, }, ]} - handleLoad={(response: {width: number; height: number}) => { + handleLoad={( + response: {width: number; height: number}, + duration: number, + ) => { const {width, height} = response; + setVideoDuration(duration); setOrigDimensions([width, height]); setAspectRatio(width / height); }} @@ -383,8 +388,9 @@ export const EditMedia: React.FC = ({route, navigation}) => { navigation.navigate('CaptionScreen', { screenType, media: { - uri: uri, - isVideo: isVideo, + uri, + isVideo, + videoDuration, }, selectedCategory, }), diff --git a/src/store/reducers/userReducer.ts b/src/store/reducers/userReducer.ts index 4692c5d3..617c60be 100644 --- a/src/store/reducers/userReducer.ts +++ b/src/store/reducers/userReducer.ts @@ -85,6 +85,10 @@ const userDataSlice = createSlice({ state.avatar = ''; state.cover = ''; }, + + setMomentUploadProgressBar: (state, action) => { + state.momentUploadProgressBar = action.payload.momentUploadProgressBar; + }, }, }); @@ -102,5 +106,6 @@ export const { clearHeaderAndProfileImages, profileBadgesUpdated, profileBadgeRemoved, + setMomentUploadProgressBar, } = userDataSlice.actions; export const userDataReducer = userDataSlice.reducer; diff --git a/src/types/types.ts b/src/types/types.ts index 2001426a..34bf73ac 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -63,8 +63,8 @@ export interface ProfileInfoType { export interface MomentUploadProgressBarType { status: MomentUploadStatusType; - originalVideoDuration: number; momentId: string; + originalVideoDuration: number | undefined; } export enum MomentUploadStatusType { diff --git a/src/utils/camera.ts b/src/utils/camera.ts index 9d7ff67f..97592fe5 100644 --- a/src/utils/camera.ts +++ b/src/utils/camera.ts @@ -57,7 +57,7 @@ export const saveImageToGallery = ( .catch((_err) => Alert.alert('Failed to save to device!')); }; -export const navigateToImagePicker = ( +export const navigateToMediaPicker = ( callback: (media: ImageOrVideo) => void, ) => { ImagePicker.openPicker({ -- 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') 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 95d7c6594ead6f17105573e0cef07ff435909ff5 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 22 Jul 2021 17:48:21 -0400 Subject: Add timing and state changes for the progress bar --- src/components/moments/MomentUploadProgressBar.tsx | 106 ++++++++++++++++++--- 1 file changed, 95 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index 0d84236d..742c403b 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -1,10 +1,16 @@ import React, {useEffect} from 'react'; import {StyleSheet, Text} from 'react-native'; import {View} from 'react-native-animatable'; -import {Easing, useSharedValue, withTiming} from 'react-native-reanimated'; +import { + cancelAnimation, + Easing, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; import {SafeAreaView} from 'react-native-safe-area-context'; -import {useSelector} from 'react-redux'; +import {useDispatch, useSelector} from 'react-redux'; import {checkMomentUploadFinished} from '../../services'; +import {setMomentUploadProgressBar} from '../../store/reducers'; import {RootState} from '../../store/rootReducer'; import {MomentUploadStatusType} from '../../types'; import {normalize, SCREEN_WIDTH, StatusBarHeight} from '../../utils'; @@ -14,37 +20,105 @@ interface MomentUploadProgressBarProps {} const MomentUploadProgressBar: React.FC = ({}) => { + const dispatch = useDispatch(); const {momentUploadProgressBar} = useSelector( (state: RootState) => state.user, ); const progress = useSharedValue(0); + const showLoading = + momentUploadProgressBar?.status === + MomentUploadStatusType.UploadingToS3 || + momentUploadProgressBar?.status === + MomentUploadStatusType.WaitingForDoneProcessing; useEffect(() => { + let doneProcessing = false; + const checkDone = () => { + if ( + momentUploadProgressBar && + checkMomentUploadFinished(momentUploadProgressBar?.momentId) + ) { + doneProcessing = true; + cancelAnimation(progress); + // upload is done, but let's finish the progress bar animation in a velocity of 10%/s + const finishProgressBarDuration = (1 - progress.value) * 10 * 1000; + progress.value = withTiming(1, { + duration: finishProgressBarDuration, + easing: Easing.linear, + }); + // change status to Done 1s after the progress bar animation is done + setTimeout(() => { + dispatch({ + type: setMomentUploadProgressBar.type, + payload: { + momentUploadProgressBar: { + ...momentUploadProgressBar, + status: MomentUploadStatusType.Done, + }, + }, + }); + }, finishProgressBarDuration + 1000); + } + }; if ( - momentUploadProgressBar?.status === MomentUploadStatusType.Uploading + momentUploadProgressBar?.status === + MomentUploadStatusType.WaitingForDoneProcessing ) { + checkDone(); const timer = setInterval(async () => { - if (checkMomentUploadFinished(momentUploadProgressBar.momentId)) { - // call upload finished action + if (!doneProcessing) { + checkDone(); } }, 5 * 1000); + // timeout if takes longer than 1 minute to process setTimeout(() => { + console.error('Check for done processing timed out'); clearInterval(timer); - }, 5 * 60 * 1000); + dispatch({ + type: setMomentUploadProgressBar.type, + payload: { + momentUploadProgressBar: { + ...momentUploadProgressBar, + status: MomentUploadStatusType.Error, + }, + }, + }); + }, 60 * 1000); + return () => clearInterval(timer); } - }, []); + }, [momentUploadProgressBar?.status]); useEffect(() => { if ( - momentUploadProgressBar?.status === MomentUploadStatusType.Uploading + momentUploadProgressBar?.status === MomentUploadStatusType.UploadingToS3 ) { + // assume it takes video duration (upload) + 1/2 of the video (process) duration to upload + // e.g. 30s video => 30 + 30 * .5 = 37.5s + const videoDuration = + momentUploadProgressBar.originalVideoDuration ?? 30; + const durationInSeconds = videoDuration * 1.5; progress.value = withTiming(1, { - duration: momentUploadProgressBar.originalVideoDuration * 1000, + duration: durationInSeconds * 1000, easing: Easing.out(Easing.quad), }); } }, [momentUploadProgressBar?.status]); + useEffect(() => { + if (momentUploadProgressBar?.status === MomentUploadStatusType.Done) { + progress.value = 0; + // clear this component after a duration + setTimeout(() => { + dispatch({ + type: setMomentUploadProgressBar.type, + payload: { + momentUploadProgressBar: undefined, + }, + }); + }, 5000); + } + }, [momentUploadProgressBar?.status]); + if (!momentUploadProgressBar) { return null; } @@ -52,8 +126,18 @@ const MomentUploadProgressBar: React.FC = return ( - Uploading Moment... - + {showLoading && ( + <> + Uploading Moment... + + + )} + {momentUploadProgressBar.status === MomentUploadStatusType.Done && ( + Done + )} + {momentUploadProgressBar.status === MomentUploadStatusType.Error && ( + Error + )} ); -- cgit v1.2.3-70-g09d2 From 8100e6a9cff95227778158d2bdc870f32caa72d4 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 22 Jul 2021 17:52:16 -0400 Subject: Resolved key warning --- src/components/profile/ProfileBadges.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/components/profile/ProfileBadges.tsx b/src/components/profile/ProfileBadges.tsx index 8e68dc46..c7d3b5ba 100644 --- a/src/components/profile/ProfileBadges.tsx +++ b/src/components/profile/ProfileBadges.tsx @@ -64,8 +64,8 @@ const ProfileBadges: React.FC = ({userXId, screenType}) => { {Array(BADGE_LIMIT) .fill(0) - .map(() => ( - + .map((_item, index) => ( + ))} )} @@ -85,8 +85,8 @@ const ProfileBadges: React.FC = ({userXId, screenType}) => { {Array(BADGE_LIMIT + 1) .fill(0) .splice(displayBadges.length + 1, BADGE_LIMIT) - .map(() => ( - + .map((_item, index) => ( + ))} {/* X button */} {displayBadges.length === BADGE_LIMIT && isOwnProfile && ( -- 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') 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') 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 From 6c17a1b13912b3e52ae4301d5a4b0ce50ba5d31d Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 22 Jul 2021 18:54:37 -0400 Subject: Style error and done state --- src/assets/images/green-check.png | Bin 0 -> 116479 bytes src/assets/images/white-x.png | Bin 0 -> 111493 bytes src/components/moments/MomentUploadProgressBar.tsx | 71 +++++++++++++++++---- 3 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 src/assets/images/green-check.png create mode 100644 src/assets/images/white-x.png (limited to 'src') diff --git a/src/assets/images/green-check.png b/src/assets/images/green-check.png new file mode 100644 index 00000000..c8680c53 Binary files /dev/null and b/src/assets/images/green-check.png differ diff --git a/src/assets/images/white-x.png b/src/assets/images/white-x.png new file mode 100644 index 00000000..17f0c50a Binary files /dev/null and b/src/assets/images/white-x.png differ diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index afda16e2..0c93ea95 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -1,5 +1,5 @@ import React, {useEffect} from 'react'; -import {StyleSheet, Text} from 'react-native'; +import {Image, StyleSheet, Text} from 'react-native'; import {View} from 'react-native-animatable'; import { cancelAnimation, @@ -7,7 +7,6 @@ import { useSharedValue, withTiming, } from 'react-native-reanimated'; -import {SafeAreaView} from 'react-native-safe-area-context'; import {useDispatch, useSelector} from 'react-redux'; import {checkMomentUploadFinished} from '../../services'; import {loadUserMoments} from '../../store/actions'; @@ -111,11 +110,16 @@ const MomentUploadProgressBar: React.FC = }, [momentUploadProgressBar?.status]); useEffect(() => { - if (momentUploadProgressBar?.status === MomentUploadStatusType.Done) { + if ( + momentUploadProgressBar?.status === MomentUploadStatusType.Done || + momentUploadProgressBar?.status === MomentUploadStatusType.Error + ) { progress.value = 0; // clear this component after a duration setTimeout(() => { - dispatch(loadUserMoments(loggedInUserId)); + if (momentUploadProgressBar?.status === MomentUploadStatusType.Done) { + dispatch(loadUserMoments(loggedInUserId)); + } dispatch({ type: setMomentUploadProgressBar.type, payload: { @@ -131,8 +135,14 @@ const MomentUploadProgressBar: React.FC = } return ( - - + + {showLoading && ( <> Uploading Moment... @@ -140,12 +150,28 @@ const MomentUploadProgressBar: React.FC = )} {momentUploadProgressBar.status === MomentUploadStatusType.Done && ( - Done + + + + Beautiful, the Moment was uploaded successfully! + + )} {momentUploadProgressBar.status === MomentUploadStatusType.Error && ( - Error + + + + Unable to upload Moment. Please retry + + )} - + ); }; @@ -154,24 +180,45 @@ const styles = StyleSheet.create({ background: { position: 'absolute', zIndex: 999, - height: StatusBarHeight + 100, + height: StatusBarHeight + normalize(84), backgroundColor: 'white', width: '100%', alignItems: 'center', }, container: { - justifyContent: 'space-evenly', - height: '100%', + justifyContent: 'center', + marginTop: StatusBarHeight, + height: normalize(84), }, text: { fontSize: normalize(14), fontWeight: 'bold', lineHeight: 17, marginVertical: 12, + width: '80%', }, bar: { width: SCREEN_WIDTH * 0.9, }, + redBackground: { + backgroundColor: '#EA574C', + }, + row: { + flexDirection: 'row', + alignItems: 'center', + }, + whiteText: { + color: 'white', + fontSize: normalize(14), + fontWeight: 'bold', + lineHeight: 17, + marginVertical: 12, + }, + x: { + width: normalize(26), + height: normalize(26), + marginRight: 10, + }, }); export default MomentUploadProgressBar; -- cgit v1.2.3-70-g09d2 From 9a043ad37aefb5c1b5908c4171cef36977acd5f4 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 23 Jul 2021 14:48:37 -0400 Subject: Fix finish process check issue, Adjust progress bar timing --- src/components/moments/MomentUploadProgressBar.tsx | 17 +++++++---------- src/constants/api.ts | 2 +- src/services/MomentService.ts | 6 +++--- 3 files changed, 11 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index 0c93ea95..9412fcd6 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -8,7 +8,7 @@ import { withTiming, } from 'react-native-reanimated'; import {useDispatch, useSelector} from 'react-redux'; -import {checkMomentUploadFinished} from '../../services'; +import {checkMomentDoneProcessing} from '../../services'; import {loadUserMoments} from '../../store/actions'; import {setMomentUploadProgressBar} from '../../store/reducers'; import {RootState} from '../../store/rootReducer'; @@ -36,10 +36,10 @@ const MomentUploadProgressBar: React.FC = useEffect(() => { let doneProcessing = false; - const checkDone = () => { + const checkDone = async () => { if ( momentUploadProgressBar && - checkMomentUploadFinished(momentUploadProgressBar?.momentId) + (await checkMomentDoneProcessing(momentUploadProgressBar!.momentId)) ) { doneProcessing = true; cancelAnimation(progress); @@ -51,6 +51,7 @@ const MomentUploadProgressBar: React.FC = }); // change status to Done 1s after the progress bar animation is done setTimeout(() => { + dispatch(loadUserMoments(loggedInUserId)); dispatch({ type: setMomentUploadProgressBar.type, payload: { @@ -60,7 +61,7 @@ const MomentUploadProgressBar: React.FC = }, }, }); - }, finishProgressBarDuration + 1000); + }, finishProgressBarDuration); } }; if ( @@ -97,11 +98,10 @@ const MomentUploadProgressBar: React.FC = if ( momentUploadProgressBar?.status === MomentUploadStatusType.UploadingToS3 ) { - // assume it takes video duration (upload) + 1/2 of the video (process) duration to upload - // e.g. 30s video => 30 + 30 * .5 = 37.5s + // e.g. 30s video => 30 * 2 = 60s const videoDuration = momentUploadProgressBar.originalVideoDuration ?? 30; - const durationInSeconds = videoDuration * 1.5; + const durationInSeconds = videoDuration * 2; progress.value = withTiming(1, { duration: durationInSeconds * 1000, easing: Easing.out(Easing.quad), @@ -117,9 +117,6 @@ const MomentUploadProgressBar: React.FC = progress.value = 0; // clear this component after a duration setTimeout(() => { - if (momentUploadProgressBar?.status === MomentUploadStatusType.Done) { - dispatch(loadUserMoments(loggedInUserId)); - } dispatch({ type: setMomentUploadProgressBar.type, payload: { diff --git a/src/constants/api.ts b/src/constants/api.ts index ec2f0897..b4548634 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -39,7 +39,7 @@ export const COMMENTS_ENDPOINT: string = API_URL + 'comments/'; export const COMMENT_REACTIONS_ENDPOINT: string = API_URL + 'reaction-comment/'; export const COMMENT_REACTIONS_REPLY_ENDPOINT: string = API_URL + 'reaction-reply/'; export const PRESIGNED_URL_ENDPOINT: string = API_URL + 'presigned-url/'; -export const CHECK_MOMENT_UPLOAD_FINISHED_ENDPOINT: string = API_URL + 'moments/check_upload_finished/'; +export const CHECK_MOMENT_UPLOAD_DONE_PROCESSING_ENDPOINT: string = API_URL + 'moments/check_done_processing/'; export const FRIENDS_ENDPOINT: string = API_URL + 'friends/'; export const ALL_USERS_ENDPOINT: string = API_URL + 'users/'; export const REPORT_ISSUE_ENDPOINT: string = API_URL + 'report/'; diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index 2a0d9113..b67cd169 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -1,7 +1,7 @@ import AsyncStorage from '@react-native-community/async-storage'; import RNFetchBlob from 'rn-fetch-blob'; import { - CHECK_MOMENT_UPLOAD_FINISHED_ENDPOINT, + CHECK_MOMENT_UPLOAD_DONE_PROCESSING_ENDPOINT, MOMENTS_ENDPOINT, MOMENTTAG_ENDPOINT, MOMENT_TAGS_ENDPOINT, @@ -322,11 +322,11 @@ export const handleVideoUpload = async ( return false; }; -export const checkMomentUploadFinished = async (momentId: string) => { +export const checkMomentDoneProcessing = async (momentId: string) => { try { const token = await AsyncStorage.getItem('token'); const response = await fetch( - CHECK_MOMENT_UPLOAD_FINISHED_ENDPOINT + '?moment_id=' + momentId, + CHECK_MOMENT_UPLOAD_DONE_PROCESSING_ENDPOINT + '?moment_id=' + momentId, { method: 'GET', headers: { -- cgit v1.2.3-70-g09d2 From e3302c00072ab8275ccd01f58037fcadf54a9614 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 23 Jul 2021 15:08:53 -0400 Subject: Add check to prevent multiple uploads --- src/constants/strings.ts | 13 +++++------ src/screens/upload/EditMedia.tsx | 47 ++++++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/constants/strings.ts b/src/constants/strings.ts index 071b3835..450d7e5c 100644 --- a/src/constants/strings.ts +++ b/src/constants/strings.ts @@ -2,8 +2,8 @@ // Below is the regex to convert this into a csv for the Google Sheet // export const (.*) = .*?(['|"|`])(.*)\2; // replace with: $1\t$3 -export const APP_STORE_LINK = 'https://apps.apple.com/us/app/tagg-discover-your-community/id1537853613' export const ADD_COMMENT_TEXT = (username?: string) => username ? `Reply to ${username}` : 'Add a comment...' +export const APP_STORE_LINK = 'https://apps.apple.com/us/app/tagg-discover-your-community/id1537853613' export const COMING_SOON_MSG = 'Creating more fun things for you, surprises coming soon πŸ˜‰'; export const ERROR_ATTEMPT_EDIT_SP = 'Can\'t let you do that yet! Please onboard Suggested People first!'; export const ERROR_AUTHENTICATION = 'An error occurred during authentication. Please login again!'; @@ -31,8 +31,10 @@ export const ERROR_INVLAID_CODE = 'The code entered is not valid!'; export const ERROR_LINK = (str: string) => `Unable to link with ${str}, Please check your login and try again`; export const ERROR_LOGIN = 'There was a problem logging you in, please refresh and try again'; export const ERROR_LOGIN_FAILED = 'Login failed. Check your username and password, and try again'; +export const ERROR_MOMENT_UPLOAD_IN_PROGRESS = 'Please wait, there is a Moment upload in progress.'; export const ERROR_NEXT_PAGE = 'There was a problem while loading the next page πŸ˜“, try again in a couple minutes'; export const ERROR_NO_CONTACT_INVITE_LEFT = 'You have no more invites left!' +export const ERROR_NO_MOMENT_CATEGORY = 'Please select a category!'; export const ERROR_NOT_ONBOARDED = 'You are now on waitlist, please enter your invitation code if you have one'; export const ERROR_PHONE_IN_USE = 'Phone already in use, please try another one'; export const ERROR_PROFILE_CREATION_SHORT = 'Profile creation failed πŸ˜“'; @@ -45,7 +47,6 @@ export const ERROR_SELECT_GENDER = 'Please select your gender'; export const ERROR_SELECT_UNIVERSITY = 'Please select your University'; export const ERROR_SERVER_DOWN = 'mhm, looks like our servers are down, please refresh and try again in a few mins'; export const ERROR_SOMETHING_WENT_WRONG = 'Oh dear, don’t worry someone will be held responsible for this error, In the meantime refresh the app'; -export const ERROR_NO_MOMENT_CATEGORY = 'Please select a category!'; export const ERROR_SOMETHING_WENT_WRONG_REFRESH = "Ha, looks like this one's on us, please refresh and try again"; export const ERROR_SOMETHING_WENT_WRONG_RELOAD = "You broke it, Just kidding! we don't know what happened... Please reload the app and try again"; export const ERROR_T_AND_C_NOT_ACCEPTED = 'You must first agree to the terms and conditions.'; @@ -61,6 +62,7 @@ export const ERROR_UPLOAD_SMALL_PROFILE_PIC = "Can't have a profile without a pi export const ERROR_UPLOAD_SP_PHOTO = 'Unable to update suggested people photo. Please retry!'; export const ERROR_VERIFICATION_FAILED_SHORT = 'Verification failed πŸ˜“'; export const FIRST_MESSAGE = 'How about sending your first message to your friend'; +export const INVITE_USER_SMS_BODY = (invitedUserName: string, invitee: string, inviteCode: string) => `Hey ${invitedUserName}!\nYou've been tagged by ${invitee}. Follow the instructions below to skip the line and join them on Tagg!\nSign up and use this code to get in: ${inviteCode}\n ${APP_STORE_LINK}`; export const MARKED_AS_MSG = (str: string) => `Marked as ${str}`; export const MOMENT_DELETED_MSG = 'Moment deleted....Some moments have to go, to create space for greater ones'; export const NO_NEW_NOTIFICATIONS = 'You have no new notifications'; @@ -69,13 +71,10 @@ export const PRIVATE_ACCOUNT = 'This account is private'; export const START_CHATTING = 'Let’s Start Chatting!'; export const SUCCESS_BADGES_UPDATE = 'Badges updated successfully!' export const SUCCESS_CATEGORY_DELETE = 'Category successfully deleted, but its memory will live on'; +export const SUCCESS_CONFIRM_INVITE_CONTACT_MESSAGE = 'Use one now?'; +export const SUCCESS_CONFIRM_INVITE_CONTACT_TITLE = (str: string) => `You have ${str} invites left!`; export const SUCCESS_INVITATION_CODE = 'Welcome to Tagg!'; export const SUCCESS_INVITE_CONTACT = (str: string) => `Success! You now have ${str} invites left!`; -export const SUCCESS_CONFIRM_INVITE_CONTACT_TITLE = (str: string) => `You have ${str} invites left!`; -export const SUCCESS_CONFIRM_INVITE_CONTACT_MESSAGE = 'Use one now?'; -export const INVITE_USER_SMS_BODY = (invitedUserName: string, invitee: string, inviteCode: string) => `Hey ${invitedUserName}!\n -You've been tagged by ${invitee}. Follow the instructions below to skip the line and join them on Tagg!\n -Sign up and use this code to get in: ${inviteCode}\n ${APP_STORE_LINK}`; export const SUCCESS_LAST_CONTACT_INVITE = 'Done! That was your last invite, hope you used it wisely!'; export const SUCCESS_LINK = (str: string) => `Successfully linked ${str} πŸŽ‰`; export const SUCCESS_PIC_UPLOAD = 'Beautiful, the Moment was uploaded successfully!'; diff --git a/src/screens/upload/EditMedia.tsx b/src/screens/upload/EditMedia.tsx index 9a061a53..07d20a7b 100644 --- a/src/screens/upload/EditMedia.tsx +++ b/src/screens/upload/EditMedia.tsx @@ -2,14 +2,24 @@ import ReactNativeZoomableView from '@dudigital/react-native-zoomable-view/src/R import {RouteProp} from '@react-navigation/core'; import {StackNavigationProp} from '@react-navigation/stack'; import React, {useEffect, useRef, useState} from 'react'; -import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native'; +import { + Alert, + Image, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; import ImageZoom, {IOnMove} from 'react-native-image-pan-zoom'; import PhotoManipulator from 'react-native-photo-manipulator'; +import {useSelector} from 'react-redux'; import TrimIcon from '../../assets/icons/trim.svg'; import CloseIcon from '../../assets/ionicons/close-outline.svg'; import {SaveButton, TrimmerPlayer} from '../../components'; import {TaggLoadingIndicator, TaggSquareButton} from '../../components/common'; +import {ERROR_MOMENT_UPLOAD_IN_PROGRESS} from '../../constants/strings'; import {MainStackParams} from '../../routes'; +import {RootState} from '../../store/rootReducer'; import { cropVideo, HeaderHeight, @@ -36,6 +46,9 @@ export const EditMedia: React.FC = ({route, navigation}) => { selectedCategory, media: {isVideo}, } = route.params; + const {momentUploadProgressBar} = useSelector( + (state: RootState) => state.user, + ); const [aspectRatio, setAspectRatio] = useState(1); // width and height of video, if video const [origDimensions, setOrigDimensions] = useState([0, 0]); @@ -252,6 +265,24 @@ export const EditMedia: React.FC = ({route, navigation}) => { ); }; + const handleNext = () => { + if (momentUploadProgressBar) { + Alert.alert(ERROR_MOMENT_UPLOAD_IN_PROGRESS); + } else { + processVideo((uri) => + navigation.navigate('CaptionScreen', { + screenType, + media: { + uri, + isVideo, + videoDuration, + }, + selectedCategory, + }), + ); + } + }; + return ( {cropLoading && } @@ -391,19 +422,7 @@ export const EditMedia: React.FC = ({route, navigation}) => { /> - processVideo((uri) => - navigation.navigate('CaptionScreen', { - screenType, - media: { - uri, - isVideo, - videoDuration, - }, - selectedCategory, - }), - ) - } + onPress={handleNext} title={'Next'} buttonStyle={'large'} buttonColor={'blue'} -- cgit v1.2.3-70-g09d2 From ca77b022552e01af490029b7c8990b5d1e0de098 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 23 Jul 2021 15:09:03 -0400 Subject: Tweak progress bar duration --- src/components/moments/MomentUploadProgressBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index 9412fcd6..792ac33f 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -98,10 +98,10 @@ const MomentUploadProgressBar: React.FC = if ( momentUploadProgressBar?.status === MomentUploadStatusType.UploadingToS3 ) { - // e.g. 30s video => 30 * 2 = 60s + // e.g. 30s video => 30 * 2.5 = 60s const videoDuration = momentUploadProgressBar.originalVideoDuration ?? 30; - const durationInSeconds = videoDuration * 2; + const durationInSeconds = videoDuration * 2.5; progress.value = withTiming(1, { duration: durationInSeconds * 1000, easing: Easing.out(Easing.quad), -- cgit v1.2.3-70-g09d2 From d5eabf15913597fc61127d7b501d271cdeac683c Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 23 Jul 2021 15:13:04 -0400 Subject: Tweak duration again --- src/components/moments/MomentUploadProgressBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index 792ac33f..d56a8337 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -98,10 +98,10 @@ const MomentUploadProgressBar: React.FC = if ( momentUploadProgressBar?.status === MomentUploadStatusType.UploadingToS3 ) { - // e.g. 30s video => 30 * 2.5 = 60s + // e.g. 30s video => 30 * 3 = 60s const videoDuration = momentUploadProgressBar.originalVideoDuration ?? 30; - const durationInSeconds = videoDuration * 2.5; + const durationInSeconds = videoDuration * 3; progress.value = withTiming(1, { duration: durationInSeconds * 1000, easing: Easing.out(Easing.quad), -- cgit v1.2.3-70-g09d2