diff options
Diffstat (limited to 'src/screens/profile/CaptionScreen.tsx')
-rw-r--r-- | src/screens/profile/CaptionScreen.tsx | 409 |
1 files changed, 245 insertions, 164 deletions
diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 912865ae..38c6d8f0 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -1,9 +1,10 @@ import {RouteProp} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; -import React, {Fragment, useEffect, useState} from 'react'; +import React, {FC, useEffect, useMemo, useState} from 'react'; import { Alert, Image, + ImageSourcePropType, Keyboard, KeyboardAvoidingView, Platform, @@ -13,20 +14,25 @@ import { TouchableWithoutFeedback, View, } from 'react-native'; -import {MentionInputControlled} from '../../components'; -import {Button, normalize} from 'react-native-elements'; +import {Button} from 'react-native-elements'; import Video from 'react-native-video'; import {useDispatch, useSelector} from 'react-redux'; import FrontArrow from '../../assets/icons/front-arrow.svg'; -import {SearchBackground} from '../../components'; +import { + MentionInputControlled, + SearchBackground, + TaggSquareButton, +} from '../../components'; import {CaptionScreenHeader} from '../../components/'; import TaggLoadingIndicator from '../../components/common/TaggLoadingIndicator'; import {TAGG_LIGHT_BLUE_2} from '../../constants'; 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 { handlePresignedURL, @@ -41,7 +47,7 @@ import { } from '../../store/actions'; import {RootState} from '../../store/rootReducer'; import {MomentTagType} from '../../types'; -import {SCREEN_WIDTH, StatusBarHeight} from '../../utils'; +import {isIPhoneX, normalize, SCREEN_WIDTH, StatusBarHeight} from '../../utils'; import {mentionPartTypes} from '../../utils/comments'; import {Trimmer, VideoPlayer} from 'react-native-video-processing'; @@ -49,28 +55,29 @@ import {Trimmer, VideoPlayer} from 'react-native-video-processing'; * Upload Screen to allow users to upload posts to Tagg */ type CaptionScreenRouteProp = RouteProp<MainStackParams, 'CaptionScreen'>; + type CaptionScreenNavigationProp = StackNavigationProp< MainStackParams, 'CaptionScreen' >; + interface CaptionScreenProps { route: CaptionScreenRouteProp; navigation: CaptionScreenNavigationProp; } const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { - const {title, screenType, selectedTags, moment} = route.params; + // moment is only present when editing + const {moment} = route.params; const { user: {userId}, } = useSelector((state: RootState) => state.user); const dispatch = useDispatch(); const [caption, setCaption] = useState(moment ? moment.caption : ''); const [loading, setLoading] = useState(false); - const [tags, setTags] = useState<MomentTagType[]>( - selectedTags ? selectedTags : [], - ); - const [aspectRatio, setAspectRatio] = useState<number>(1); - const [taggedList, setTaggedList] = useState<string>(''); + const [tags, setTags] = useState<MomentTagType[]>([]); + const [taggedUsersText, setTaggedUsersText] = useState(''); + const [momentCategory, setMomentCategory] = useState<string | undefined>(); const mediaUri = moment ? moment.moment_url : route.params.media!.uri; // console.log('mediaUri', mediaUri); // TODO: change this once moment refactor is done @@ -84,48 +91,68 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { : route.params.media?.isVideo ?? false; useEffect(() => { - setTags(selectedTags ? selectedTags : []); - }, [selectedTags]); + setTags(route.params.selectedTags ?? []); + }, [route.params.selectedTags]); useEffect(() => { - const getTaggedUsersListString = () => { - let listString = ''; - for (let i = 0; i < tags.length; i++) { - if (listString.length < 21) { - listString = listString.concat(`@${tags[i].user.username} `); - } else { - break; - } + setMomentCategory(route.params.selectedCategory); + }, [route.params.selectedCategory]); + + useEffect(() => { + let listString = ''; + // Append non-truncated usernames together and no more than 21 characters total + // e.g. "@ivan.tagg" + // e.g. "@ivan.tagg @foobar . . ." + for (const tag of tags) { + const usernameStr = `@${tag.user.username} `; + if (listString.length + usernameStr.length > 21) { + listString = listString.concat('. . .'); + break; + } else { + listString = listString.concat(usernameStr); } - setTaggedList(listString); - }; - getTaggedUsersListString(); + } + setTaggedUsersText(listString); }, [tags]); - const navigateToProfile = () => { - //Since the logged In User is navigating to own profile, useXId is not required - navigation.navigate('Profile', { - screenType, - userXId: undefined, - }); - }; - - const handleFailed = () => { + const handleFailed = (noCategory = false) => { setLoading(false); + navigation.dangerouslyGetParent()?.setOptions({ + tabBarVisible: true, + }); setTimeout(() => { - Alert.alert(moment ? ERROR_SOMETHING_WENT_WRONG_REFRESH : ERROR_UPLOAD); + if (noCategory) { + Alert.alert(ERROR_NO_MOMENT_CATEGORY); + } else { + Alert.alert(moment ? ERROR_SOMETHING_WENT_WRONG_REFRESH : ERROR_UPLOAD); + } }, 500); }; const handleSuccess = () => { setLoading(false); - if (moment) { - setLoading(false); - navigation.goBack(); - } else { - navigateToProfile(); + navigation.dangerouslyGetParent()?.setOptions({ + tabBarVisible: true, + }); + if (!moment) { + // if posting, pop all screens until at camera screen (default upload screen) + // then switch to the profile tab + navigation.popToTop(); + RootNavigation.navigate('ProfileTab'); setTimeout(() => { - Alert.alert(SUCCESS_PIC_UPLOAD); + if (isMediaAVideo) { + Alert.alert( + 'Beautiful, the Moment was uploaded successfully! Check back in a bit and refresh to see it!', + ); + } else { + Alert.alert(SUCCESS_PIC_UPLOAD); + } }, 500); + } else { + // if editing, simply go back to profile screen + navigation.navigate('Profile', { + userXId: undefined, + screenType: route.params.screenType, + }); } }; @@ -140,15 +167,15 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { const handleShare = async () => { setLoading(true); - if (moment || !title) { - handleFailed(); + if (moment || !momentCategory) { + handleFailed(true); return; } let profileCompletionStage; let momentId; // separate upload logic for image/video if (isMediaAVideo) { - const presignedURLResponse = await handlePresignedURL(title); + const presignedURLResponse = await handlePresignedURL(momentCategory); if (!presignedURLResponse) { handleFailed(); return; @@ -161,7 +188,12 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { handleFailed(); } } else { - const momentResponse = await postMoment(mediaUri, caption, title, userId); + const momentResponse = await postMoment( + mediaUri, + caption, + momentCategory, + userId, + ); if (!momentResponse) { handleFailed(); return; @@ -176,19 +208,22 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { return; } } - dispatch(loadUserMoments(userId)); + if (!isMediaAVideo) { + dispatch(loadUserMoments(userId)); + } if (profileCompletionStage) { dispatch(updateProfileCompletionStage(profileCompletionStage)); } handleSuccess(); }; - const handleDoneEditing = async () => { + const handleSubmitEditChanges = async () => { setLoading(true); - if (moment?.moment_id) { + if (moment?.moment_id && momentCategory) { const success = await patchMoment( moment.moment_id, caption, + momentCategory, formattedTags(), ); if (success) { @@ -200,17 +235,46 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { } }; - const [currentTime, setCurrentTime] = useState(0); - const [startTime, setStartTime] = useState(0); - // todo: update with vid times - const [endTime, setEndTime] = useState(60); + const SelectableItem: FC<{ + text: 'Tag Friends' | 'Category'; + imageUri: ImageSourcePropType; + onPress: () => void; + }> = ({text, imageUri, onPress}) => { + return ( + <TouchableOpacity + onPress={onPress} + style={styles.selectableItemContainer}> + <View style={styles.row}> + {text === 'Category' && !momentCategory && ( + <Text style={styles.asteriskText}>* </Text> + )} + <Image style={styles.tagIcon} source={imageUri} /> + <Text style={styles.selectableItemTitle}>{text}</Text> + </View> + <View style={styles.row}> + {text === 'Tag Friends' && ( + <Text style={styles.itemInfoText}>{taggedUsersText}</Text> + )} + {text === 'Category' && ( + <Text style={styles.itemInfoText}>{momentCategory}</Text> + )} + <FrontArrow + width={normalize(13)} + height={normalize(13)} + color={'white'} + /> + </View> + </TouchableOpacity> + ); + }; return ( <SearchBackground> - {loading ? <TaggLoadingIndicator fullscreen /> : <Fragment />} + {loading && <TaggLoadingIndicator fullscreen />} <TouchableWithoutFeedback onPress={Keyboard.dismiss}> <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'} + keyboardVerticalOffset={isIPhoneX() ? 40 : 30} style={styles.flex}> <View style={styles.contentContainer}> <View style={styles.buttonsContainer}> @@ -219,104 +283,84 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { buttonStyle={styles.button} onPress={() => navigation.goBack()} /> - <Button - title={moment ? 'Done' : 'Share'} - titleStyle={styles.shareButtonTitle} - buttonStyle={styles.button} - onPress={moment ? handleDoneEditing : handleShare} - /> </View> - <CaptionScreenHeader - style={styles.header} - {...{title: moment ? moment.moment_category : title ?? ''}} - /> - {isMediaAVideo ? ( - <View style={styles.mediaContainer}> - {/* <VideoPlayer - //style={styles.media} - ref={(ref) => (this.videoPlayerRef = ref)} - startTime={startTime} // seconds - endTime={endTime} // seconds - play={true} // default false - replay={true} // should player play video again if it's ended - //rotate={true} // use this prop to rotate video if it captured in landscape mode iOS only - source={mediaUri} - playerWidth={360} // iOS only - playerHeight={680} // iOS only - //resizeMode={VideoPlayer.Constants.resizeMode.CONTAIN} - onChange={({nativeEvent}) => - setCurrentTime(nativeEvent.currentTime) - } // get Current time on every second - /> - <Trimmer - source={mediaUri} - height={50} - width={350} - onTrackerMove={(e) => setCurrentTime(e.currentTime)} // iOS only - currentTime={currentTime} // use this prop to set tracker position iOS only - themeColor={'white'} // iOS only - showTrackerHandle={true} - thumbWidth={10} // iOS only - trackerColor={'green'} // iOS only - onChange={(e) => { - setStartTime(e.startTime); - setEndTime(e.endTime); - }} - /> */} + <CaptionScreenHeader style={styles.header} title={'Moments'} /> + <View style={styles.captionContainer}> + {isMediaAVideo ? ( <Video + style={styles.media} + source={{uri: mediaUri}} + repeat={true} + /> + ) : ( + <Image + style={styles.media} source={{uri: mediaUri}} - volume={1} - style={[ - styles.media, - { - height: SCREEN_WIDTH / aspectRatio, - }, - ]} - repeat={false} resizeMode={'contain'} - onLoad={(response) => { - const {width, height} = response.naturalSize; - setAspectRatio(width / height); - }} /> - </View> - ) : ( - <Image - style={styles.media} - source={{uri: mediaUri}} - resizeMode={'contain'} + )} + <MentionInputControlled + style={styles.text} + containerStyle={styles.flex} + placeholder="Write something....." + placeholderTextColor="white" + value={caption} + onChange={setCaption} + partTypes={mentionPartTypes('white', 'caption', true)} /> + </View> + {useMemo( + () => ( + <SelectableItem + text={'Category'} + imageUri={require('../../assets/images/images.png')} + onPress={() => + navigation.navigate('ChoosingCategoryScreen', {}) + } + /> + ), + [momentCategory], )} - <MentionInputControlled - containerStyle={styles.text} - placeholder="Write something....." - placeholderTextColor="gray" - value={caption} - onChange={setCaption} - partTypes={mentionPartTypes('blue', 'caption')} - /> - <TouchableOpacity - onPress={() => - navigation.navigate('TagFriendsScreen', { - media: { - uri: mediaUri, - isVideo: isMediaAVideo, - }, - selectedTags: tags, - }) - } - style={styles.tagFriendsContainer}> - <Image - source={require('../../assets/icons/tagging/tag-icon.png')} - style={styles.tagIcon} + {useMemo( + () => ( + <SelectableItem + text={'Tag Friends'} + imageUri={require('../../assets/icons/tagging/tag-icon.png')} + onPress={() => + navigation.navigate('TagFriendsScreen', { + media: { + uri: mediaUri, + isVideo: isMediaAVideo, + }, + selectedTags: tags, + }) + } + /> + ), + [taggedUsersText], + )} + {momentCategory ? ( + <TaggSquareButton + onPress={moment ? handleSubmitEditChanges : handleShare} + title={moment ? 'Update' : 'Post'} + buttonStyle={'large'} + buttonColor={'blue'} + labelColor={'white'} + style={styles.postButton} + labelStyle={styles.postText} + /> + ) : ( + <TaggSquareButton + disabled={true} + onPress={moment ? handleSubmitEditChanges : handleShare} + title={moment ? 'Update' : 'Post'} + buttonStyle={'large'} + buttonColor={'blue'} + labelColor={'white'} + style={[styles.postButton, styles.greyBackground]} + labelStyle={styles.postText} /> - <Text style={styles.tagFriendsTitle}>Tag Friends</Text> - <Text numberOfLines={1} style={styles.taggedListContainer}> - {taggedList} - {taggedList.length > 21 ? '. . .' : ''} - </Text> - <FrontArrow width={12} height={12} color={'white'} /> - </TouchableOpacity> + )} </View> </KeyboardAvoidingView> </TouchableWithoutFeedback> @@ -342,53 +386,90 @@ const styles = StyleSheet.create({ color: TAGG_LIGHT_BLUE_2, }, header: { - marginVertical: 20, + marginTop: 20, + marginBottom: 30, }, - media: { - position: 'relative', - width: SCREEN_WIDTH, - aspectRatio: 1, - marginBottom: '3%', - flex: 1, + captionContainer: { + flexDirection: 'row', + padding: normalize(15), + marginBottom: normalize(35), + borderColor: 'white', + borderTopWidth: 1, + borderBottomWidth: 1, + zIndex: 1, }, - mediaContainer: { - width: SCREEN_WIDTH, - aspectRatio: 1, + media: { + height: normalize(150), + aspectRatio: 9 / 16, }, text: { - position: 'relative', - backgroundColor: 'white', - width: '100%', - paddingHorizontal: '2%', - paddingVertical: '1%', - height: 60, + color: 'white', + fontSize: normalize(12), + lineHeight: 14, + fontWeight: '500', + height: normalize(150), + marginLeft: normalize(15), }, flex: { flex: 1, }, - tagFriendsTitle: { + selectableItemTitle: { color: 'white', - fontSize: normalize(12), + fontSize: normalize(14), lineHeight: normalize(16.71), letterSpacing: normalize(0.3), fontWeight: '600', }, - tagFriendsContainer: { + selectableItemContainer: { marginHorizontal: '5%', - marginTop: '3%', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', + marginBottom: normalize(42), + }, + asteriskText: { + color: TAGG_LIGHT_BLUE_2, + fontWeight: 'bold', + fontSize: normalize(15), + height: 15, + alignSelf: 'center', }, - taggedListContainer: { + itemInfoText: { color: 'white', width: 150, fontSize: normalize(10), lineHeight: normalize(11), letterSpacing: normalize(0.3), textAlign: 'right', + + marginRight: 5, + }, + tagIcon: { + width: normalize(20), + height: normalize(20), + marginRight: 15, + }, + row: { + flexDirection: 'row', + }, + greyBackground: { + backgroundColor: '#C4C4C4', + }, + postButton: { + width: SCREEN_WIDTH * 0.8, + height: normalize(37), + justifyContent: 'center', + alignItems: 'center', + borderRadius: 6, + alignSelf: 'center', + }, + postText: { + color: 'white', + fontWeight: 'bold', + fontSize: normalize(15), + lineHeight: 18, + letterSpacing: 2, }, - tagIcon: {width: 20, height: 20}, }); export default CaptionScreen; |