diff options
Diffstat (limited to 'src/screens')
| -rw-r--r-- | src/screens/moments/CameraScreen.tsx | 16 | ||||
| -rw-r--r-- | src/screens/moments/TagFriendsScreen.tsx | 239 | ||||
| -rw-r--r-- | src/screens/profile/CaptionScreen.tsx | 361 | ||||
| -rw-r--r-- | src/screens/profile/CategorySelection.tsx | 1 | ||||
| -rw-r--r-- | src/screens/profile/ChoosingCategoryScreen.tsx | 192 | ||||
| -rw-r--r-- | src/screens/profile/CreateCustomCategory.tsx | 4 | ||||
| -rw-r--r-- | src/screens/profile/IndividualMoment.tsx | 2 | ||||
| -rw-r--r-- | src/screens/profile/MomentCommentsScreen.tsx | 5 | ||||
| -rw-r--r-- | src/screens/profile/index.ts | 1 |
9 files changed, 634 insertions, 187 deletions
diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx index 7643f6ab..10e42e6d 100644 --- a/src/screens/moments/CameraScreen.tsx +++ b/src/screens/moments/CameraScreen.tsx @@ -30,7 +30,7 @@ interface CameraScreenProps { navigation: CameraScreenNavigationProps; } const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => { - const {title, screenType} = route.params; + const {screenType, selectedCategory} = route.params; const cameraRef = createRef<RNCamera>(); const tabBarHeight = useBottomTabBarHeight(); const [cameraType, setCameraType] = useState<keyof CameraType>('front'); @@ -45,11 +45,6 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => { navigation.dangerouslyGetParent()?.setOptions({ tabBarVisible: false, }); - return () => { - navigation.dangerouslyGetParent()?.setOptions({ - tabBarVisible: true, - }); - }; }, [navigation]), ); @@ -72,18 +67,17 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => { const navigateToCropper = (uri: string) => { navigation.navigate('ZoomInCropper', { screenType, - title, media: { uri, isVideo: false, }, + selectedCategory, }); }; const navigateToCaptionScreen = (isVideo: boolean, uri: string) => { navigation.navigate('CaptionScreen', { screenType, - title, media: { uri, isVideo, @@ -101,6 +95,9 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => { setShowSaveButton(false); setMediaFromGallery(''); } else { + navigation.dangerouslyGetParent()?.setOptions({ + tabBarVisible: true, + }); navigation.goBack(); } }; @@ -116,6 +113,9 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => { style={styles.camera} type={cameraType} flashMode={flashMode} + onDoubleTap={() => { + setCameraType(cameraType === 'front' ? 'back' : 'front'); + }} /> <View style={[styles.bottomContainer, {bottom: tabBarHeight}]}> {showSaveButton ? ( diff --git a/src/screens/moments/TagFriendsScreen.tsx b/src/screens/moments/TagFriendsScreen.tsx index 201caf49..fc3bccf2 100644 --- a/src/screens/moments/TagFriendsScreen.tsx +++ b/src/screens/moments/TagFriendsScreen.tsx @@ -1,19 +1,27 @@ import {RouteProp} from '@react-navigation/core'; import {useNavigation} from '@react-navigation/native'; import React, {useEffect, useRef, useState} from 'react'; -import {Image, StyleSheet, TouchableWithoutFeedback, View} from 'react-native'; -import {Button} from 'react-native-elements'; +import { + Image, + StyleSheet, + Text, + TouchableOpacity, + TouchableWithoutFeedback, + View, +} from 'react-native'; import Video from 'react-native-video'; import {MainStackParams} from 'src/routes'; -import { - CaptionScreenHeader, - MomentTags, - SearchBackground, -} from '../../components'; +import BackArrow from '../../assets/icons/back-arrow.svg'; +import {MomentTags} from '../../components'; import {TagFriendsFooter} from '../../components/moments'; -import {TAGG_LIGHT_BLUE_2} from '../../constants'; import {MomentTagType} from '../../types'; -import {SCREEN_WIDTH, StatusBarHeight} from '../../utils'; +import { + HeaderHeight, + isIPhoneX, + normalize, + SCREEN_HEIGHT, + SCREEN_WIDTH, +} from '../../utils'; type TagFriendsScreenRouteProps = RouteProp< MainStackParams, @@ -29,6 +37,9 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => { const [tags, setTags] = useState<MomentTagType[]>([]); const [imageWidth, setImageWidth] = useState<number>(0); const [imageHeight, setImageHeight] = useState<number>(0); + const [bottomBound, setBottomBound] = useState<number>(0); + const [topHeight, setTopHeight] = useState<number>(0); + const [topBound, setTopBound] = useState<number>(0); /* * Update list of tagged users from route params @@ -41,28 +52,57 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => { * Navigate back to Tag Users Screen, send selected users */ const handleDone = () => { - navigation.navigate('CaptionScreen', { - ...route.params, - selectedTags: tags, - }); + // Makes sure that this can only be pressed if there are tags + if (tags.length !== 0) { + navigation.navigate('CaptionScreen', { + ...route.params, + selectedTags: tags, + }); + } }; const setMediaDimensions = (width: number, height: number) => { const imageAspectRatio = width / height; + const screenRatio = SCREEN_WIDTH / SCREEN_HEIGHT; - // aspectRatio: >= 1 [Landscape] [1:1] - if (imageAspectRatio >= 1) { + // aspectRatio is wider than or equal to screen + if (imageAspectRatio >= screenRatio) { setImageWidth(SCREEN_WIDTH); setImageHeight(SCREEN_WIDTH / imageAspectRatio); } - // aspectRatio: < 1 [Portrait] - if (imageAspectRatio < 1) { - setImageHeight(SCREEN_WIDTH); - setImageWidth(SCREEN_WIDTH * imageAspectRatio); + // aspectRatio is taller than screen + if (imageAspectRatio < screenRatio) { + setImageHeight(SCREEN_HEIGHT); + setImageWidth(SCREEN_HEIGHT * imageAspectRatio); } }; /* + * Calculate boundary (if any) for drag from bottom and top + */ + useEffect(() => { + // Bottom dead zone + if (SCREEN_HEIGHT / 2 - imageHeight / 2 < SCREEN_HEIGHT * 0.15) { + if (SCREEN_HEIGHT / 2 - imageHeight / 2 < 0) { + setBottomBound(SCREEN_HEIGHT * 0.15); + } else { + setBottomBound( + SCREEN_HEIGHT * 0.15 - (SCREEN_HEIGHT / 2 - imageHeight / 2), + ); + } + } + + // Top dead zone + if (SCREEN_HEIGHT / 2 - imageHeight / 2 < topHeight) { + if (SCREEN_HEIGHT / 2 - imageHeight / 2 < 0) { + setTopBound(topHeight + 15); + } else { + setTopBound(topHeight - (SCREEN_HEIGHT / 2 - imageHeight / 2) + 15); + } + } + }, [imageHeight, imageWidth, topHeight]); + + /* * Calculating image width and height with respect to it's enclosing view's dimensions. Only works for images. */ useEffect(() => { @@ -78,25 +118,12 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => { }, []); return ( - <SearchBackground> - <View style={styles.contentContainer}> - <View style={styles.buttonsContainer}> - <Button - title="Cancel" - buttonStyle={styles.button} - onPress={() => navigation.goBack()} - /> - <Button - title="Done" - titleStyle={styles.shareButtonTitle} - buttonStyle={styles.button} - onPress={handleDone} - /> - </View> - <CaptionScreenHeader - style={styles.header} - title={'Tap on photo to tag friends!'} - /> + <View style={styles.contentContainer}> + <View + style={[ + styles.innerContainer, + {paddingTop: SCREEN_HEIGHT / 2 - imageHeight / 2}, + ]}> <TouchableWithoutFeedback onPress={() => navigation.navigate('TagSelectionScreen', { @@ -108,7 +135,6 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => { style={{ width: imageWidth, height: imageHeight, - marginVertical: (SCREEN_WIDTH - imageHeight) / 2, marginHorizontal: (SCREEN_WIDTH - imageWidth) / 2, }} ref={imageRef}> @@ -136,55 +162,142 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => { style={{ width: imageWidth, height: imageHeight, - marginVertical: (SCREEN_WIDTH - imageHeight) / 2, marginHorizontal: (SCREEN_WIDTH - imageWidth) / 2, }} source={{uri: media.uri}} /> )} </TouchableWithoutFeedback> - {tags.length !== 0 && ( - <MomentTags - tags={tags} - setTags={setTags} - editing={true} - imageRef={imageRef} - deleteFromList={(user) => - setTags(tags.filter((tag) => tag.user.id !== user.id)) - } - /> - )} + </View> + <View + style={styles.titleContainer} + onLayout={(event) => { + const {y, height} = event.nativeEvent.layout; + setTopHeight(y + height); + }}> + <TouchableOpacity + onPress={() => { + navigation.goBack(); + }} + style={styles.backArrow}> + <View style={styles.backArrowContainer}> + <BackArrow + height={normalize(18)} + width={normalize(18)} + color={'white'} + /> + </View> + </TouchableOpacity> + <TouchableWithoutFeedback style={styles.captionContainer}> + {tags.length === 0 ? ( + <Text style={styles.header}>Tap on photo to tag friends!</Text> + ) : ( + <Text style={styles.header}>Press and drag to move</Text> + )} + </TouchableWithoutFeedback> + <TouchableOpacity + style={styles.buttonContainer} + // Altering the opacity style of TouchableOpacity doesn't work, + // so the next two lines are needed + disabled={tags.length === 0} + activeOpacity={tags.length === 0 ? 0 : 1} + onPress={handleDone}> + <Text + style={[ + styles.shareButtonTitle, + // makes the Done buttomn invisible if there are no tags + // eslint-disable-next-line react-native/no-inline-styles + {opacity: tags.length !== 0 ? 1 : 0}, + ]}> + Done + </Text> + </TouchableOpacity> + </View> + {tags.length !== 0 && ( + <MomentTags + tags={tags} + setTags={setTags} + editing={true} + imageRef={imageRef} + deleteFromList={(user) => + setTags(tags.filter((tag) => tag.user.id !== user.id)) + } + boundaries={{top: topBound, bottom: bottomBound}} + /> + )} + {tags.length !== 0 && ( <View style={styles.footerContainer}> <TagFriendsFooter tags={tags} setTags={setTags} /> </View> - </View> - </SearchBackground> + )} + </View> ); }; const styles = StyleSheet.create({ contentContainer: { - paddingTop: StatusBarHeight, + backgroundColor: 'black', + height: SCREEN_HEIGHT, + alignContent: 'center', }, - buttonsContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - marginLeft: '5%', - marginRight: '5%', + backArrow: { + marginTop: isIPhoneX() ? HeaderHeight : '10%', + zIndex: 9999, + }, + backArrowContainer: { + flex: 1, + flexDirection: 'column', + justifyContent: 'center', + alignContent: 'center', }, button: { - backgroundColor: 'transparent', + zIndex: 9999, + }, + buttonContainer: { + marginTop: isIPhoneX() ? HeaderHeight : '10%', + right: 0, + zIndex: 9999, + flexDirection: 'row', + justifyContent: 'flex-end', + }, + captionContainer: { + width: SCREEN_WIDTH, + flexDirection: 'row', + justifyContent: 'center', + zIndex: 9999, }, shareButtonTitle: { fontWeight: 'bold', - color: TAGG_LIGHT_BLUE_2, + fontSize: 18, + color: 'white', + }, + titleContainer: { + flexDirection: 'row', + justifyContent: 'space-evenly', + alignContent: 'center', + zIndex: 9999, }, header: { - marginVertical: 20, + marginTop: isIPhoneX() ? HeaderHeight : '10%', + fontSize: 18, + fontWeight: 'bold', + color: 'white', + textAlign: 'center', }, footerContainer: { - marginHorizontal: '5%', marginTop: '3%', + backgroundColor: 'black', + padding: '5%', + flexDirection: 'column', + justifyContent: 'center', + width: SCREEN_WIDTH, + position: 'absolute', + paddingBottom: 0, + bottom: 0, + height: SCREEN_HEIGHT * 0.15, + }, + innerContainer: { + position: 'absolute', }, }); diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index e77f40c2..383737e7 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,19 +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, @@ -40,34 +47,36 @@ 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 {TrimmerPlayer} from '../../components/moments/trimmer'; /** * 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 [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; // TODO: change this once moment refactor is done const isMediaAVideo = moment @@ -80,48 +89,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, + }); } }; @@ -136,15 +165,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; @@ -157,7 +186,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; @@ -172,19 +206,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) { @@ -196,12 +233,46 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { } }; + 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}> @@ -210,60 +281,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={'Moments'} /> + <View style={styles.captionContainer}> + {isMediaAVideo ? ( + <Video + style={styles.media} + source={{uri: mediaUri}} + repeat={true} + /> + ) : ( + <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> - <CaptionScreenHeader - style={styles.header} - {...{title: moment ? moment.moment_category : title ?? ''}} - /> - {isMediaAVideo ? ( - <TrimmerPlayer - source={mediaUri} - styles={styles.media} - hideTrimmer={true} + {useMemo( + () => ( + <SelectableItem + text={'Category'} + imageUri={require('../../assets/images/images.png')} + onPress={() => + navigation.navigate('ChoosingCategoryScreen', {}) + } + /> + ), + [momentCategory], + )} + {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} /> ) : ( - <Image - style={styles.media} - source={{uri: mediaUri}} - resizeMode={'contain'} + <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} /> )} - <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} - /> - <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> @@ -290,48 +385,90 @@ const styles = StyleSheet.create({ color: TAGG_LIGHT_BLUE_2, }, header: { - marginVertical: 20, + marginTop: 20, + marginBottom: 30, + }, + captionContainer: { + flexDirection: 'row', + padding: normalize(15), + marginBottom: normalize(35), + borderColor: 'white', + borderTopWidth: 1, + borderBottomWidth: 1, + zIndex: 1, }, media: { - position: 'relative', - width: SCREEN_WIDTH, - aspectRatio: 1, - marginBottom: '3%', + 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), }, - taggedListContainer: { + asteriskText: { + color: TAGG_LIGHT_BLUE_2, + fontWeight: 'bold', + fontSize: normalize(15), + height: 15, + alignSelf: 'center', + }, + 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; diff --git a/src/screens/profile/CategorySelection.tsx b/src/screens/profile/CategorySelection.tsx index ea443fce..2f364e59 100644 --- a/src/screens/profile/CategorySelection.tsx +++ b/src/screens/profile/CategorySelection.tsx @@ -170,6 +170,7 @@ const CategorySelection: React.FC<CategorySelectionProps> = ({ onPress={() => { navigation.push('CreateCustomCategory', { existingCategories: momentCategories.concat(selectedCategories), + fromScreen: route.name, }); }}> <PlusIcon width={30} height={30} color="white" /> diff --git a/src/screens/profile/ChoosingCategoryScreen.tsx b/src/screens/profile/ChoosingCategoryScreen.tsx new file mode 100644 index 00000000..8a7e3007 --- /dev/null +++ b/src/screens/profile/ChoosingCategoryScreen.tsx @@ -0,0 +1,192 @@ +import {useBottomTabBarHeight} from '@react-navigation/bottom-tabs'; +import {RouteProp, useNavigation} from '@react-navigation/native'; +import React, {FC, useEffect} from 'react'; +import { + Image, + ScrollView, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import LinearGradient from 'react-native-linear-gradient'; +import {useDispatch, useSelector} from 'react-redux'; +import FrontArrow from '../../assets/icons/front-arrow.svg'; +import PlusIcon from '../../assets/icons/plus-icon.svg'; +import {SearchBackground, TaggSquareButton} from '../../components'; +import {TAGGS_GRADIENT, TAGG_DARK_PURPLEISH_BLUE} from '../../constants'; +import {MainStackParams} from '../../routes'; +import {updateMomentCategories} from '../../store/actions'; +import {RootState} from '../../store/rootReducer'; +import { + getMomentCategoryIconInfo, + HeaderHeight, + normalize, + SCREEN_HEIGHT, + SCREEN_WIDTH, + StatusBarHeight, +} from '../../utils'; + +type ChoosingCategoryScreenRouteProps = RouteProp< + MainStackParams, + 'ChoosingCategoryScreen' +>; + +interface ChoosingCategoryScreenProps { + route: ChoosingCategoryScreenRouteProps; +} + +const ChoosingCategoryScreen: React.FC<ChoosingCategoryScreenProps> = ({ + route, +}) => { + const {momentCategories} = useSelector( + (state: RootState) => state.momentCategories, + ); + const dispatch = useDispatch(); + const navigation = useNavigation(); + const tabBarHeight = useBottomTabBarHeight(); + + useEffect(() => { + if (route.params.newCustomCategory) { + dispatch( + updateMomentCategories( + momentCategories.concat([route.params.newCustomCategory]), + false, + ), + ); + } + }, [route.params.newCustomCategory]); + + const ListItem: FC<{ + title: string; + onPress: () => void; + }> = ({title, onPress}) => { + const icon = getMomentCategoryIconInfo(title).icon; + return ( + <TouchableOpacity onPress={onPress} style={styles.itemContainer}> + <View style={styles.row}> + <LinearGradient + style={styles.gradientIcon} + colors={[TAGGS_GRADIENT.start, TAGGS_GRADIENT.end]} + useAngle={true} + angle={-45}> + <View style={styles.iconBackground}> + <Image style={styles.icon} source={icon} /> + </View> + </LinearGradient> + <Text style={styles.itemTitle}>{title}</Text> + </View> + <View style={styles.row}> + <FrontArrow + width={normalize(13)} + height={normalize(13)} + color={'white'} + /> + </View> + </TouchableOpacity> + ); + }; + + return ( + <SearchBackground> + <View style={{marginTop: StatusBarHeight + HeaderHeight}}> + <ScrollView + style={{height: SCREEN_HEIGHT * 0.9}} + contentContainerStyle={{paddingBottom: tabBarHeight}}> + {momentCategories.map((title) => ( + <ListItem + key={title} + title={title} + onPress={() => + navigation.navigate('CaptionScreen', { + selectedCategory: title, + }) + } + /> + ))} + <TaggSquareButton + onPress={() => + navigation.navigate('CreateCustomCategory', { + existingCategories: momentCategories, + fromScreen: route.name, + }) + } + title={'Create a new category'} + buttonStyle={'large'} + buttonColor={'blue'} + labelColor={'white'} + style={styles.button} + labelStyle={styles.buttonText} + icon={<PlusIcon style={styles.plusIcon} />} + /> + </ScrollView> + </View> + </SearchBackground> + ); +}; + +const styles = StyleSheet.create({ + container: { + marginTop: StatusBarHeight, + }, + itemContainer: { + marginHorizontal: '5%', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginVertical: normalize(20), + borderRadius: 4, + }, + gradientIcon: { + width: normalize(40), + height: normalize(40), + justifyContent: 'center', + alignItems: 'center', + borderRadius: 4, + }, + iconBackground: { + height: '85%', + width: '85%', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: TAGG_DARK_PURPLEISH_BLUE, + }, + icon: { + height: normalize(25), + width: normalize(25), + }, + itemTitle: { + color: 'white', + fontSize: normalize(14), + lineHeight: normalize(16.71), + letterSpacing: normalize(0.3), + fontWeight: '600', + alignSelf: 'center', + marginLeft: normalize(25), + }, + row: { + flexDirection: 'row', + }, + button: { + width: SCREEN_WIDTH * 0.9, + height: normalize(67), + justifyContent: 'center', + alignItems: 'center', + borderRadius: 8, + alignSelf: 'center', + marginTop: 40, + }, + buttonText: { + color: 'white', + fontSize: normalize(15), + lineHeight: 18, + }, + plusIcon: { + color: 'white', + marginRight: normalize(25), + width: 30, + height: 30, + }, +}); + +export default ChoosingCategoryScreen; diff --git a/src/screens/profile/CreateCustomCategory.tsx b/src/screens/profile/CreateCustomCategory.tsx index c4b17b1e..91083c7c 100644 --- a/src/screens/profile/CreateCustomCategory.tsx +++ b/src/screens/profile/CreateCustomCategory.tsx @@ -37,14 +37,14 @@ const CreateCustomCategory: React.FC<CreateCustomCategoryProps> = ({ /** * Same component to be used for category selection while onboarding and while on profile */ - const {existingCategories} = route.params; + const {existingCategories, fromScreen} = route.params; const [newCategory, setNewCategory] = useState(''); const handleButtonPress = () => { if (existingCategories.includes(newCategory)) { Alert.alert('Looks like you already have that one created!'); } else { - navigation.navigate('CategorySelection', { + navigation.navigate(fromScreen, { newCustomCategory: newCategory, }); } diff --git a/src/screens/profile/IndividualMoment.tsx b/src/screens/profile/IndividualMoment.tsx index 7d231312..1b0c7c2b 100644 --- a/src/screens/profile/IndividualMoment.tsx +++ b/src/screens/profile/IndividualMoment.tsx @@ -92,6 +92,8 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({route}) => { screenType={screenType} /> )} + keyboardShouldPersistTaps={'handled'} + scrollEnabled={!keyboardVisible} keyExtractor={(item, _) => item.moment_id} showsVerticalScrollIndicator={false} initialScrollIndex={initialIndex} diff --git a/src/screens/profile/MomentCommentsScreen.tsx b/src/screens/profile/MomentCommentsScreen.tsx index 402e5f44..7dfe8ae9 100644 --- a/src/screens/profile/MomentCommentsScreen.tsx +++ b/src/screens/profile/MomentCommentsScreen.tsx @@ -48,8 +48,9 @@ const MomentCommentsScreen: React.FC<MomentCommentsScreenProps> = ({route}) => { React.useState(true); //Keeps track of the current comments object in focus so that the application knows which comment to post a reply to - const [commentTapped, setCommentTapped] = - useState<CommentType | CommentThreadType | undefined>(); + const [commentTapped, setCommentTapped] = useState< + CommentType | CommentThreadType | undefined + >(); useEffect(() => { navigation.setOptions({ diff --git a/src/screens/profile/index.ts b/src/screens/profile/index.ts index ea0505a2..101101b8 100644 --- a/src/screens/profile/index.ts +++ b/src/screens/profile/index.ts @@ -13,3 +13,4 @@ export {default as AccountType} from './AccountType'; export {default as CategorySelection} from './CategorySelection'; export {default as CreateCustomCategory} from './CreateCustomCategory'; export {default as CommentReactionScreen} from './CommentReactionScreen'; +export {default as ChoosingCategoryScreen} from './ChoosingCategoryScreen'; |
