aboutsummaryrefslogtreecommitdiff
path: root/src/screens
diff options
context:
space:
mode:
Diffstat (limited to 'src/screens')
-rw-r--r--src/screens/moments/CameraScreen.tsx16
-rw-r--r--src/screens/moments/TagFriendsScreen.tsx239
-rw-r--r--src/screens/profile/CaptionScreen.tsx361
-rw-r--r--src/screens/profile/CategorySelection.tsx1
-rw-r--r--src/screens/profile/ChoosingCategoryScreen.tsx192
-rw-r--r--src/screens/profile/CreateCustomCategory.tsx4
-rw-r--r--src/screens/profile/IndividualMoment.tsx2
-rw-r--r--src/screens/profile/MomentCommentsScreen.tsx5
-rw-r--r--src/screens/profile/index.ts1
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';