diff options
-rw-r--r-- | .github/auto_assign.yml | 2 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/components/comments/CommentTextField.tsx | 23 | ||||
-rw-r--r-- | src/components/common/GradientProgressBar.tsx | 25 | ||||
-rw-r--r-- | src/components/moments/MomentPost.tsx | 81 | ||||
-rw-r--r-- | src/components/moments/MomentUploadProgressBar.tsx | 17 | ||||
-rw-r--r-- | src/components/profile/Content.tsx | 2 | ||||
-rw-r--r-- | src/screens/main/NotificationsScreen.tsx | 4 | ||||
-rw-r--r-- | src/screens/profile/CaptionScreen.tsx | 73 | ||||
-rw-r--r-- | src/services/MomentService.ts | 6 | ||||
-rw-r--r-- | src/store/actions/user.ts | 90 | ||||
-rw-r--r-- | src/utils/camera.ts | 3 | ||||
-rw-r--r-- | yarn.lock | 5 |
13 files changed, 196 insertions, 136 deletions
diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml index d5d2a2fe..5b8b13db 100644 --- a/.github/auto_assign.yml +++ b/.github/auto_assign.yml @@ -12,6 +12,8 @@ reviewers: - shravyaramesh - grusuTagg - brian-tagg + - hsalhab + - sotech117 # A number of reviewers added to the pull request # Set 0 to add all the reviewers (default: 0) diff --git a/package.json b/package.json index fea5aa11..2023f78a 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,6 @@ "react-native-safe-area-context": "^3.2.0", "react-native-screens": "^2.9.0", "react-native-share": "^5.1.7", - "react-native-simple-gradient-progressbar-view": "^0.2.0", "react-native-snap-carousel": "^3.9.1", "react-native-splash-screen": "^3.2.0", "react-native-svg": "^12.1.1", diff --git a/src/components/comments/CommentTextField.tsx b/src/components/comments/CommentTextField.tsx index 6d86eb3f..f2e5251f 100644 --- a/src/components/comments/CommentTextField.tsx +++ b/src/components/comments/CommentTextField.tsx @@ -94,20 +94,17 @@ const CommentTextField: FC<CommentTextFieldProps> = ({ )} </Text> </TextInput> - <View style={styles.submitButton}> - <TouchableOpacity - style={ - comment === '' - ? [styles.submitButton, styles.greyButton] - : styles.submitButton - } - disabled={comment === ''} - onPress={addComment}> - <UpArrowIcon width={35} height={35} color={'white'} /> - </TouchableOpacity> - </View> + <TouchableOpacity + style={ + comment === '' + ? [styles.submitButton, styles.greyButton] + : styles.submitButton + } + disabled={comment === ''} + onPress={addComment}> + <UpArrowIcon width={35} height={35} color={'white'} /> + </TouchableOpacity> </View> - {validateInput(keyboardText) && ( partTypes.filter( diff --git a/src/components/common/GradientProgressBar.tsx b/src/components/common/GradientProgressBar.tsx index fc62bd3c..066295d0 100644 --- a/src/components/common/GradientProgressBar.tsx +++ b/src/components/common/GradientProgressBar.tsx @@ -2,20 +2,21 @@ import React, {FC} from 'react'; 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, - TAGG_PURPLE, -} from '../../constants'; import {normalize} from '../../utils'; interface GradientProgressBarProps extends ViewProps { progress: Animated.SharedValue<number>; + toColor: string; + fromColor: string; + unfilledColor: string; } const GradientProgressBar: FC<GradientProgressBarProps> = ({ style, progress, + toColor, + fromColor, + unfilledColor, }) => { const animatedProgressStyle = useAnimatedStyle<ViewStyle>(() => ({ width: `${(1 - progress.value) * 100}%`, @@ -24,8 +25,16 @@ const GradientProgressBar: FC<GradientProgressBarProps> = ({ <LinearGradient style={[styles.bar, style]} useAngle={true} - colors={[TAGG_PURPLE, TAGG_LIGHT_BLUE_2]}> - <Animated.View style={[styles.blank, animatedProgressStyle]} /> + colors={[fromColor, toColor]}> + <Animated.View + style={[ + styles.blank, + animatedProgressStyle, + { + backgroundColor: unfilledColor, + }, + ]} + /> </LinearGradient> ); }; @@ -36,12 +45,12 @@ const styles = StyleSheet.create({ bar: { height: normalize(10), borderRadius: 6.5, + opacity: 1, }, blank: { alignSelf: 'flex-end', height: normalize(10), width: '80%', - backgroundColor: TAGG_LIGHT_BLUE_3, }, }); diff --git a/src/components/moments/MomentPost.tsx b/src/components/moments/MomentPost.tsx index c232986d..921f7693 100644 --- a/src/components/moments/MomentPost.tsx +++ b/src/components/moments/MomentPost.tsx @@ -14,14 +14,18 @@ import { } from 'react-native'; // @ts-ignore import Pinchable from 'react-native-pinchable'; -import Animated, {EasingNode} from 'react-native-reanimated'; +import Animated, { + Easing, + EasingNode, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; import {SafeAreaView} from 'react-native-safe-area-context'; -import SimpleGradientProgressbarView from 'react-native-simple-gradient-progressbar-view'; import Video from 'react-native-video'; import {useDispatch, useSelector, useStore} from 'react-redux'; import {TaggedUsersDrawer} from '.'; import PauseIcon from '../../assets/icons/pause-icon.svg'; -import {TAGG_LIGHT_BLUE_2, TAGG_PURPLE} from '../../constants/constants'; +import {TAGG_PURPLE} from '../../constants/constants'; import {headerBarOptions} from '../../routes'; import {MomentContext} from '../../screens/profile/IndividualMoment'; import {deleteMomentTag, loadMomentTags} from '../../services'; @@ -30,16 +34,14 @@ import {RootState} from '../../store/rootReducer'; import {MomentPostType, MomentTagType, ScreenType, UserType} from '../../types'; import { getTimePosted, - isIPhoneX, navigateToProfile, normalize, SCREEN_HEIGHT, SCREEN_WIDTH, } from '../../utils'; import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments'; -import {AddComment} from '../comments'; import CommentsCount from '../comments/CommentsCount'; -import {MomentTags} from '../common'; +import {GradientProgressBar, MomentTags} from '../common'; import {MomentMoreInfoDrawer, TaggAvatar} from '../profile'; import IndividualMomentTitleBar from './IndividualMomentTitleBar'; interface MomentPostProps { @@ -75,9 +77,6 @@ const MomentPost: React.FC<MomentPostProps> = ({ const [fadeValue, setFadeValue] = useState<Animated.Value<number>>( new Animated.Value(0), ); - const [commentCount, setCommentCount] = useState<number>( - moment.comments_count, - ); const [aspectRatio, setAspectRatio] = useState<number>(1); const [momentTagId, setMomentTagId] = useState<string>(''); @@ -94,7 +93,7 @@ const MomentPost: React.FC<MomentPostProps> = ({ ); const mediaHeight = SCREEN_WIDTH / aspectRatio; const [isVideoPaused, setIsVideoPaused] = useState<boolean>(false); - const [videoProgress, setVideoProgress] = useState<number>(0); + const videoProgress = useSharedValue(0); /* * Load tags on initial render to pass tags data to moment header and content */ @@ -233,10 +232,16 @@ const MomentPost: React.FC<MomentPostProps> = ({ onProgress={({currentTime, seekableDuration}) => { const localProgress = currentTime / seekableDuration; if (!isNaN(localProgress)) { - setVideoProgress(localProgress); + videoProgress.value = withTiming(localProgress, { + duration: 250, + easing: Easing.linear, + }); } }} - onEnd={updateMomentViewCount} + onEnd={() => { + updateMomentViewCount(); + videoProgress.value = 0; + }} /> {isVideoPaused && ( <Animated.View @@ -260,20 +265,6 @@ const MomentPost: React.FC<MomentPostProps> = ({ /> ); - const ProgressBar = () => ( - <View style={styles.progressBaContainer}> - <SimpleGradientProgressbarView - progress={videoProgress} - style={styles.progressBar} - fromColor={TAGG_PURPLE} - toColor={TAGG_LIGHT_BLUE_2} - /> - <View - style={[styles.progressDot, {left: videoProgress * SCREEN_WIDTH - 5}]} - /> - </View> - ); - return ( <> <StatusBar barStyle={'light-content'} /> @@ -353,7 +344,7 @@ const MomentPost: React.FC<MomentPostProps> = ({ <View style={styles.commentsCountContainer}> <CommentsCount momentId={moment.moment_id} - count={commentCount} + count={moment.comments_count} screenType={screenType} /> </View> @@ -374,22 +365,18 @@ const MomentPost: React.FC<MomentPostProps> = ({ ), })} <View> - <AddComment - placeholderText={'Add a comment here!'} - momentId={moment.moment_id} - callback={() => { - setCommentCount(commentCount + 1); - }} - onFocus={() => { - setHideText(true); - }} - isKeyboardAvoiding={false} - theme={'dark'} - /> - {isVideo && <ProgressBar />} <Text style={styles.text}> {getTimePosted(moment.date_created)} </Text> + {isVideo && ( + <GradientProgressBar + style={styles.progressBar} + progress={videoProgress} + toColor={'#fff'} + fromColor={'#fff'} + unfilledColor={'#808080'} + /> + )} </View> </KeyboardAvoidingView> </View> @@ -409,7 +396,8 @@ const styles = StyleSheet.create({ color: 'white', fontWeight: '500', textAlign: 'right', - marginTop: 18, + marginTop: 10, + marginBottom: 10, }, captionText: { position: 'relative', @@ -501,10 +489,9 @@ const styles = StyleSheet.create({ top: '40%', }, progressBar: { - position: 'absolute', - top: 0, - width: SCREEN_WIDTH, - height: 5, + width: SCREEN_WIDTH * 0.99, + height: 3, + marginHorizontal: 2, }, progressDot: { backgroundColor: '#fff', @@ -516,10 +503,6 @@ const styles = StyleSheet.create({ position: 'absolute', top: -2.5, }, - progressBaContainer: { - position: 'absolute', - top: isIPhoneX() ? 75 : 70, - }, profilePreviewContainer: {paddingHorizontal: '3%'}, }); diff --git a/src/components/moments/MomentUploadProgressBar.tsx b/src/components/moments/MomentUploadProgressBar.tsx index d56a8337..96f9fa27 100644 --- a/src/components/moments/MomentUploadProgressBar.tsx +++ b/src/components/moments/MomentUploadProgressBar.tsx @@ -8,6 +8,11 @@ import { withTiming, } from 'react-native-reanimated'; import {useDispatch, useSelector} from 'react-redux'; +import { + TAGG_LIGHT_BLUE_2, + TAGG_LIGHT_BLUE_3, + TAGG_PURPLE, +} from '../../constants'; import {checkMomentDoneProcessing} from '../../services'; import {loadUserMoments} from '../../store/actions'; import {setMomentUploadProgressBar} from '../../store/reducers'; @@ -51,7 +56,6 @@ const MomentUploadProgressBar: React.FC<MomentUploadProgressBarProps> = }); // change status to Done 1s after the progress bar animation is done setTimeout(() => { - dispatch(loadUserMoments(loggedInUserId)); dispatch({ type: setMomentUploadProgressBar.type, payload: { @@ -110,6 +114,9 @@ const MomentUploadProgressBar: React.FC<MomentUploadProgressBarProps> = }, [momentUploadProgressBar?.status]); useEffect(() => { + if (momentUploadProgressBar?.status === MomentUploadStatusType.Done) { + dispatch(loadUserMoments(loggedInUserId)); + } if ( momentUploadProgressBar?.status === MomentUploadStatusType.Done || momentUploadProgressBar?.status === MomentUploadStatusType.Error @@ -143,7 +150,13 @@ const MomentUploadProgressBar: React.FC<MomentUploadProgressBarProps> = {showLoading && ( <> <Text style={styles.text}>Uploading Moment...</Text> - <GradientProgressBar style={styles.bar} progress={progress} /> + <GradientProgressBar + style={styles.bar} + progress={progress} + toColor={TAGG_LIGHT_BLUE_2} + fromColor={TAGG_PURPLE} + unfilledColor={TAGG_LIGHT_BLUE_3} + /> </> )} {momentUploadProgressBar.status === MomentUploadStatusType.Done && ( diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx index 9edd890d..df692a3f 100644 --- a/src/components/profile/Content.tsx +++ b/src/components/profile/Content.tsx @@ -136,7 +136,7 @@ const Content: React.FC<ContentProps> = ({userXId, screenType}) => { onScroll={scrollHandler} showsVerticalScrollIndicator={false} scrollEventThrottle={1} - stickyHeaderIndices={[4]} + stickyHeaderIndices={[5]} scrollEnabled={scrollEnabled} refreshControl={ <RefreshControl refreshing={refreshing} onRefresh={onRefresh} /> diff --git a/src/screens/main/NotificationsScreen.tsx b/src/screens/main/NotificationsScreen.tsx index b19107a7..55dd9051 100644 --- a/src/screens/main/NotificationsScreen.tsx +++ b/src/screens/main/NotificationsScreen.tsx @@ -1,4 +1,5 @@ import AsyncStorage from '@react-native-community/async-storage'; +import PushNotificationIOS from '@react-native-community/push-notification-ios'; import {useFocusEffect, useNavigation} from '@react-navigation/native'; import moment from 'moment'; import React, {useCallback, useEffect, useState} from 'react'; @@ -21,7 +22,6 @@ import FindFriendsBlueIcon from '../../assets/icons/findFriends/find-friends-blu import {TabsGradient} from '../../components'; import EmptyContentView from '../../components/common/EmptyContentView'; import {Notification} from '../../components/notifications'; -import {NewChatPrompt} from '../../components/notifications/NotificationPrompts'; import { loadUserNotifications, updateNewNotificationReceived, @@ -29,7 +29,6 @@ import { import {RootState} from '../../store/rootReducer'; import {NotificationType, ScreenType} from '../../types'; import {getDateAge, normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; -import PushNotificationIOS from '@react-native-community/push-notification-ios'; const NotificationsScreen: React.FC = () => { const {newNotificationReceived} = useSelector( @@ -299,7 +298,6 @@ const NotificationsScreen: React.FC = () => { renderItem={renderNotification} renderSectionHeader={renderSectionHeader} renderSectionFooter={renderSectionFooter} - ListHeaderComponent={<NewChatPrompt />} refreshControl={ <RefreshControl refreshing={refreshing} onRefresh={onRefresh} /> } diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 3ee0bd5b..d329c589 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -30,15 +30,14 @@ import { ERROR_NO_MOMENT_CATEGORY, ERROR_SOMETHING_WENT_WRONG_REFRESH, ERROR_UPLOAD, - SUCCESS_PIC_UPLOAD, } from '../../constants/strings'; import * as RootNavigation from '../../RootNavigation'; import {MainStackParams} from '../../routes'; -import {patchMoment, postMoment, postMomentTags} from '../../services'; +import {patchMoment} from '../../services'; import { + handleImageMomentUpload, handleVideoMomentUpload, loadUserMoments, - updateProfileCompletionStage, } from '../../store/actions'; import {RootState} from '../../store/rootReducer'; import {MomentTagType} from '../../types'; @@ -138,11 +137,6 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { // then switch to the profile tab navigation.popToTop(); RootNavigation.navigate('ProfileTab'); - setTimeout(() => { - if (!isMediaAVideo) { - Alert.alert(SUCCESS_PIC_UPLOAD); - } - }, 500); } else { // if editing, simply go back to profile screen navigation.navigate('Profile', { @@ -167,53 +161,30 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { handleFailed(true); return; } - let profileCompletionStage; - // separate upload logic for image/video if (isMediaAVideo) { - if (videoDuration) { - dispatch( - handleVideoMomentUpload( - mediaUri, - videoDuration, - momentCategory, - formattedTags(), - ), - ); - } else { - handleFailed(); - return; - } - } else { - const momentResponse = await postMoment( - mediaUri, - caption, - momentCategory, - userId, + dispatch( + handleVideoMomentUpload( + mediaUri, + videoDuration ?? 30, + caption, + momentCategory, + formattedTags(), + ), ); - if (!momentResponse) { - handleFailed(); - return; - } - profileCompletionStage = momentResponse.profile_completion_stage; - const momentId = momentResponse.moment_id; - if (momentId) { - const momentTagResponse = await postMomentTags( - momentId, + } else { + dispatch( + handleImageMomentUpload( + mediaUri, + caption, + momentCategory, + userId, formattedTags(), - ); - if (!momentTagResponse) { - handleFailed(); - return; - } - } - } - if (!isMediaAVideo) { - dispatch(loadUserMoments(userId)); - } - if (profileCompletionStage) { - dispatch(updateProfileCompletionStage(profileCompletionStage)); + ), + ); } - handleSuccess(); + setTimeout(() => { + handleSuccess(); + }, 500); }; const handleSubmitEditChanges = async () => { diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index 3a677ccc..0292f9ea 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -223,7 +223,10 @@ export const deleteMomentTag = async (moment_tag_id: string) => { * @param value: string | undefined * @returns a PresignedURLResponse object */ -export const handlePresignedURL = async (momentCategory: string) => { +export const handlePresignedURL = async ( + caption: string, + momentCategory: string, +) => { try { // TODO: just a random filename for video poc, we should not need to once complete const randHash = Math.random().toString(36).substring(7); @@ -236,6 +239,7 @@ export const handlePresignedURL = async (momentCategory: string) => { }, body: JSON.stringify({ filename, + caption: caption, category: momentCategory, }), }); diff --git a/src/store/actions/user.ts b/src/store/actions/user.ts index 1acbb519..f01e2bac 100644 --- a/src/store/actions/user.ts +++ b/src/store/actions/user.ts @@ -6,6 +6,7 @@ import { handlePresignedURL, handleVideoUpload, loadProfileInfo, + postMoment, postMomentTags, removeBadgesService, sendSuggestedPeopleLinked, @@ -285,6 +286,89 @@ export const suggestedPeopleAnimatedTutorialFinished = } }; +export const handleImageMomentUpload = + ( + imageUri: string, + caption: string, + momentCategory: string, + userId: string, + formattedTags: { + x: number; + y: number; + z: number; + user_id: string; + }[], + ): ThunkAction<Promise<void>, RootState, unknown, Action<string>> => + 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: 1, // assume upload time for an image is same as a 1s video + }; + // set progress bar as loading + dispatch({ + type: setMomentUploadProgressBar.type, + payload: {momentUploadProgressBar}, + }); + // upload image moment + const momentPostResponse = await postMoment( + imageUri, + caption, + momentCategory, + userId, + ); + if (!momentPostResponse) { + handleError('Moment post failed'); + return; + } + const profileCompletionStage = + momentPostResponse.profile_completion_stage; + const momentId = momentPostResponse.moment_id; + if (!momentId) { + handleError('Unable to parse moment id from moment post response'); + return; + } + // upload moment tags + const momentTagResponse = await postMomentTags(momentId, formattedTags); + if (!momentTagResponse) { + handleError('Moment tag post failed'); + return; + } + if (profileCompletionStage) { + dispatch(updateProfileCompletionStage(profileCompletionStage)); + } else { + console.error( + 'failed to parse profile complete stage from moment post response', + ); + } + // mark progress bar state as done + dispatch({ + type: setMomentUploadProgressBar.type, + payload: { + momentUploadProgressBar: { + ...momentUploadProgressBar, + status: MomentUploadStatusType.Done, + }, + }, + }); + } catch (error) { + console.log(error); + } + }; + /** * state is now UploadingToS3: * - get presigned url (backend creates the moment object) @@ -296,6 +380,7 @@ export const handleVideoMomentUpload = ( videoUri: string, videoLength: number, + caption: string, momentCategory: string, formattedTags: { x: number; @@ -329,7 +414,10 @@ export const handleVideoMomentUpload = payload: {momentUploadProgressBar}, }); // get a presigned url for the video - const presignedURLResponse = await handlePresignedURL(momentCategory); + const presignedURLResponse = await handlePresignedURL( + caption, + momentCategory, + ); if (!presignedURLResponse) { handleError('Presigned URL failed'); return; diff --git a/src/utils/camera.ts b/src/utils/camera.ts index 8104ba74..f21ef133 100644 --- a/src/utils/camera.ts +++ b/src/utils/camera.ts @@ -67,6 +67,7 @@ export const navigateToMediaPicker = ( 'SelfPortraits', 'Screenshots', 'UserLibrary', + 'Videos', ], mediaType: 'any', compressVideoPreset: 'Passthrough', @@ -153,7 +154,7 @@ export const cropVideo = ( ? Math.round(videoCropValues.cropOffsetY) : 0 : 0, - quality: 'highest', + quality: 'passthrough', }).then((data: any) => { if (muted) { removeAudio(data, handleData); @@ -7439,11 +7439,6 @@ react-native-share@^5.1.7: resolved "https://registry.yarnpkg.com/react-native-share/-/react-native-share-5.3.0.tgz#e501f974f2c0e12f8c78aa744dfc5bdf6bc54978" integrity sha512-VHRVxCyENhKThfap2Y7eXawuCqMpSvKYsvANZUfeTKrTlZ86hVi9h0+ITXKTLIj8gOB8qmSYUzKG7o6Kj4wKJg== -react-native-simple-gradient-progressbar-view@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/react-native-simple-gradient-progressbar-view/-/react-native-simple-gradient-progressbar-view-0.2.0.tgz#77dbce7555fca836d815f6644e1f23d35ed3908b" - integrity sha512-mqavQMhdTtb/StG/zsB844GhTNKQr3Bp+u1LTV9bU68mB/bS6VQiHh6WLfM1VPPXiAKTE1bI8i8RfKvOwrIp/A== - react-native-snap-carousel@^3.9.1: version "3.9.1" resolved "https://registry.yarnpkg.com/react-native-snap-carousel/-/react-native-snap-carousel-3.9.1.tgz#6fd9bd8839546c2c6043a41d2035afbc6fe0443e" |