diff options
Diffstat (limited to 'src')
23 files changed, 653 insertions, 280 deletions
diff --git a/src/components/comments/ZoomInCropper.tsx b/src/components/comments/ZoomInCropper.tsx index 7fa88f6e..e624c81c 100644 --- a/src/components/comments/ZoomInCropper.tsx +++ b/src/components/comments/ZoomInCropper.tsx @@ -1,7 +1,6 @@ import {RouteProp} from '@react-navigation/core'; -import {useFocusEffect} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; -import React, {useCallback, useEffect, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {Image, StyleSheet, TouchableOpacity} from 'react-native'; import {normalize} from 'react-native-elements'; import ImageZoom, {IOnMove} from 'react-native-image-pan-zoom'; @@ -25,7 +24,7 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ route, navigation, }) => { - const {screenType, title, media} = route.params; + const {screenType, media, selectedCategory} = route.params; const [aspectRatio, setAspectRatio] = useState<number>(1); // Stores the coordinates of the cropped image @@ -34,19 +33,6 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ const [y0, setY0] = useState<number>(); const [y1, setY1] = useState<number>(); - useFocusEffect( - useCallback(() => { - navigation.dangerouslyGetParent()?.setOptions({ - tabBarVisible: false, - }); - return () => { - navigation.dangerouslyGetParent()?.setOptions({ - tabBarVisible: true, - }); - }; - }, [navigation]), - ); - // Setting original aspect ratio of image useEffect(() => { if (media.uri) { @@ -77,11 +63,11 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ .then((croppedURL) => { navigation.navigate('CaptionScreen', { screenType, - title: title, media: { uri: croppedURL, isVideo: false, }, + selectedCategory, }); }) .catch((err) => console.log('err: ', err)); @@ -93,8 +79,8 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ ) { navigation.navigate('CaptionScreen', { screenType, - title: title, media, + selectedCategory, }); } }; diff --git a/src/components/common/NavigationIcon.tsx b/src/components/common/NavigationIcon.tsx index debb36b3..a5f42992 100644 --- a/src/components/common/NavigationIcon.tsx +++ b/src/components/common/NavigationIcon.tsx @@ -37,7 +37,7 @@ const NavigationIcon = (props: NavigationIconProps) => { case 'Upload': imgSrc = props.disabled ? require('../../assets/navigationIcons/new-upload.png') - : require('../../assets/navigationIcons/upload-clicked.png'); + : require('../../assets/navigationIcons/new-upload.png'); break; case 'Notifications': imgSrc = props.disabled diff --git a/src/components/common/TaggSquareButton.tsx b/src/components/common/TaggSquareButton.tsx index 1a1c33b3..b1e65ba6 100644 --- a/src/components/common/TaggSquareButton.tsx +++ b/src/components/common/TaggSquareButton.tsx @@ -1,11 +1,12 @@ -import React from 'react'; +import React, {FC} from 'react'; import { GestureResponderEvent, + StyleProp, StyleSheet, Text, TextStyle, TouchableOpacity, - ViewProps, + TouchableOpacityProps, ViewStyle, } from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; @@ -15,14 +16,16 @@ import { TAGG_PURPLE, } from '../../constants'; import {normalize, SCREEN_WIDTH} from '../../utils'; -interface TaggSquareButtonProps extends ViewProps { + +interface TaggSquareButtonProps extends TouchableOpacityProps { onPress: (event: GestureResponderEvent) => void; title: string; buttonStyle: 'normal' | 'large' | 'gradient'; buttonColor: 'purple' | 'white' | 'blue'; labelColor: 'white' | 'blue'; - style?: ViewStyle; - labelStyle?: TextStyle; + style?: StyleProp<ViewStyle>; + labelStyle?: StyleProp<TextStyle>; + icon?: Element; } const TaggSquareButton: React.FC<TaggSquareButtonProps> = (props) => { @@ -50,8 +53,10 @@ const TaggSquareButton: React.FC<TaggSquareButtonProps> = (props) => { case 'large': return ( <TouchableOpacity + {...props} onPress={props.onPress} style={[styles.largeButton, buttonColor, props.style]}> + {props.icon} <Text style={[styles.largeLabel, labelColor, props.labelStyle]}> {props.title} </Text> @@ -59,12 +64,16 @@ const TaggSquareButton: React.FC<TaggSquareButtonProps> = (props) => { ); case 'gradient': return ( - <TouchableOpacity onPress={props.onPress} style={props.style}> + <TouchableOpacity + {...props} + onPress={props.onPress} + style={props.style}> <LinearGradient style={styles.gradientButton} colors={BACKGROUND_GRADIENT_MAP[0]} useAngle angle={90}> + {props.icon} <Text style={[styles.gradientLabel, props.labelStyle]}> {props.title} </Text> @@ -75,8 +84,10 @@ const TaggSquareButton: React.FC<TaggSquareButtonProps> = (props) => { default: return ( <TouchableOpacity + {...props} onPress={props.onPress} style={[styles.normalButton, buttonColor, props.style]}> + {props.icon} <Text style={[styles.normalLabel, labelColor, props.labelStyle]}> {props.title} </Text> @@ -87,6 +98,7 @@ const TaggSquareButton: React.FC<TaggSquareButtonProps> = (props) => { const styles = StyleSheet.create({ largeButton: { + flexDirection: 'row', justifyContent: 'center', alignItems: 'center', width: '70%', @@ -99,6 +111,7 @@ const styles = StyleSheet.create({ color: '#eee', }, normalButton: { + flexDirection: 'row', justifyContent: 'center', alignItems: 'center', width: SCREEN_WIDTH * 0.45, @@ -111,6 +124,7 @@ const styles = StyleSheet.create({ fontWeight: '500', }, gradientButton: { + flexDirection: 'row', marginTop: '8%', borderRadius: 5, paddingVertical: '5%', diff --git a/src/components/common/TaggTypeahead.tsx b/src/components/common/TaggTypeahead.tsx index 7967fdbc..672cff69 100644 --- a/src/components/common/TaggTypeahead.tsx +++ b/src/components/common/TaggTypeahead.tsx @@ -1,27 +1,34 @@ -import React, {Fragment, useEffect, useState} from 'react'; -import {ScrollView, StyleSheet, View} from 'react-native'; +import React, {Fragment, useEffect, useRef, useState} from 'react'; +import {LayoutChangeEvent, ScrollView, StyleSheet, View} from 'react-native'; import {Suggestion} from 'react-native-controlled-mentions'; import {useSelector} from 'react-redux'; import {SEARCH_ENDPOINT_MESSAGES} from '../../constants'; import {loadSearchResults} from '../../services'; import {RootState} from '../../store/rootReducer'; import {ProfilePreviewType} from '../../types'; -import {SCREEN_HEIGHT, SCREEN_WIDTH, shuffle} from '../../utils'; +import {isIPhoneX, SCREEN_HEIGHT, SCREEN_WIDTH, shuffle} from '../../utils'; import TaggUserRowCell from './TaggUserRowCell'; type TaggTypeaheadProps = { keyword: string | undefined; component: string | undefined; onSuggestionPress: (suggestion: Suggestion) => void; + isShowBelowStyle?: boolean; }; const TaggTypeahead: React.FC<TaggTypeaheadProps> = ({ keyword, component, onSuggestionPress, + isShowBelowStyle = false, }) => { const {friends} = useSelector((state: RootState) => state.friends); const [results, setResults] = useState<ProfilePreviewType[]>([]); + const [viewPxy, setViewPxy] = useState<{px: number; py: number}>({ + px: 0, + py: 0, + }); + const viewRef = useRef<View>(null); const [height, setHeight] = useState(0); const margin = component === 'comment' ? -10 : 0; @@ -33,6 +40,22 @@ const TaggTypeahead: React.FC<TaggTypeaheadProps> = ({ } }, [keyword]); + const onLayout = (e: LayoutChangeEvent) => { + setHeight(e.nativeEvent.layout.height); + viewRef.current?.measure( + ( + _fx: number, + _fy: number, + _width: number, + _height: number, + px: number, + py: number, + ) => { + setViewPxy({px, py}); + }, + ); + }; + const getQuerySuggested = async () => { if (keyword === undefined || keyword === '@') { return; @@ -50,14 +73,17 @@ const TaggTypeahead: React.FC<TaggTypeaheadProps> = ({ } return ( - <View> - <View style={styles.overlay} /> + <View ref={viewRef} onLayout={onLayout}> + {/* <View ref={viewRef} onLayout={onLayout}> */} + {!isShowBelowStyle && <View style={styles.overlay} />} <ScrollView - style={[styles.container, {top: -height, margin: margin}]} + style={[ + styles.container, + isShowBelowStyle + ? [styles.topPadding, {left: -viewPxy.px}] + : {top: -height, margin: margin}, + ]} showsVerticalScrollIndicator={false} - onLayout={(event) => { - setHeight(event.nativeEvent.layout.height); - }} keyboardShouldPersistTaps={'always'}> {results.map((user) => ( <TaggUserRowCell @@ -78,10 +104,10 @@ const TaggTypeahead: React.FC<TaggTypeaheadProps> = ({ const styles = StyleSheet.create({ container: { + position: 'absolute', width: SCREEN_WIDTH, maxHeight: 264, backgroundColor: 'white', - position: 'absolute', alignSelf: 'center', zIndex: 1, }, @@ -95,6 +121,9 @@ const styles = StyleSheet.create({ bottom: 10, zIndex: -1, }, + topPadding: { + top: isIPhoneX() ? 180 : 150, + }, }); export default TaggTypeahead; diff --git a/src/components/moments/CaptionScreenHeader.tsx b/src/components/moments/CaptionScreenHeader.tsx index 0638c128..cda85e57 100644 --- a/src/components/moments/CaptionScreenHeader.tsx +++ b/src/components/moments/CaptionScreenHeader.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {Text, View, StyleSheet, ViewProps} from 'react-native'; +import {normalize} from '../../utils'; interface CaptionScreenHeaderProps extends ViewProps { title: string; } @@ -26,8 +27,8 @@ const styles = StyleSheet.create({ width: '90%', }, header: { - fontSize: 20, - fontWeight: 'bold', + fontSize: normalize(18), + fontWeight: '700', color: 'white', textAlign: 'center', }, diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx index 73503c5e..087b343f 100644 --- a/src/components/moments/Moment.tsx +++ b/src/components/moments/Moment.tsx @@ -7,7 +7,6 @@ import LinearGradient from 'react-native-linear-gradient'; import DeleteIcon from '../../assets/icons/delete-logo.svg'; import DownIcon from '../../assets/icons/down_icon.svg'; import BigPlusIcon from '../../assets/icons/plus-icon-white.svg'; -import PlusIcon from '../../assets/icons/plus-icon.svg'; import UpIcon from '../../assets/icons/up_icon.svg'; import {TAGG_LIGHT_BLUE} from '../../constants'; import {MomentType, ScreenType} from '../../types'; @@ -40,14 +39,6 @@ const Moment: React.FC<MomentProps> = ({ externalStyles, }) => { const navigation = useNavigation(); - - const navigateToCameraScreen = () => { - navigation.navigate('CameraScreen', { - title, - screenType, - }); - }; - return ( <View style={[styles.container, externalStyles?.container]}> <View style={[styles.header, externalStyles?.header]}> @@ -81,13 +72,6 @@ const Moment: React.FC<MomentProps> = ({ )} {!userXId && ( <View style={styles.row}> - <PlusIcon - width={23} - height={23} - onPress={navigateToCameraScreen} - color={TAGG_LIGHT_BLUE} - style={styles.horizontalMargin} - /> {shouldAllowDeletion && ( <DeleteIcon onPress={() => handleMomentCategoryDelete(title)} @@ -114,7 +98,13 @@ const Moment: React.FC<MomentProps> = ({ /> ))} {(images === undefined || images.length === 0) && !userXId && ( - <TouchableOpacity onPress={navigateToCameraScreen}> + <TouchableOpacity + onPress={() => + navigation.navigate('CameraScreen', { + screenType: ScreenType.Profile, + selectedCategory: title, + }) + }> <LinearGradient colors={['rgba(105, 141, 211, 1)', 'rgba(105, 141, 211, 0.3)']}> <View style={styles.defaultImage}> diff --git a/src/components/onboarding/MomentCategory.tsx b/src/components/onboarding/MomentCategory.tsx index 97099b9e..eb9d5bcc 100644 --- a/src/components/onboarding/MomentCategory.tsx +++ b/src/components/onboarding/MomentCategory.tsx @@ -3,11 +3,12 @@ import {StyleSheet} from 'react-native'; import {Image, Text} from 'react-native-animatable'; import {TouchableOpacity} from 'react-native-gesture-handler'; import LinearGradient from 'react-native-linear-gradient'; +import {BACKGROUND_GRADIENT_MAP} from '../../constants'; import { - BACKGROUND_GRADIENT_MAP, - MOMENT_CATEGORY_BG_COLORS, -} from '../../constants'; -import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; + getMomentCategoryIconInfo, + SCREEN_HEIGHT, + SCREEN_WIDTH, +} from '../../utils'; type MomentCategoryProps = { categoryType: string; @@ -22,85 +23,7 @@ const MomentCategory: React.FC<MomentCategoryProps> = ({ isAdded, onSelect, }) => { - var icon, bgColor; - - /** - * Choose icon and color based on category type - */ - switch (categoryType) { - case 'Friends': - icon = require('../../assets/moment-categories/friends-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[0]; - break; - case 'Adventure': - icon = require('../../assets/moment-categories/adventure-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[1]; - break; - case 'Photo Dump': - icon = require('../../assets/moment-categories/photo-dump-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[2]; - break; - case 'Food': - icon = require('../../assets/moment-categories/food-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[3]; - break; - case 'Music': - icon = require('../../assets/moment-categories/music-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[4]; - break; - case 'Art': - icon = require('../../assets/moment-categories/art-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[5]; - break; - case 'Sports': - icon = require('../../assets/moment-categories/sports-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[6]; - break; - case 'Fashion': - icon = require('../../assets/moment-categories/fashion-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[7]; - break; - case 'Travel': - icon = require('../../assets/moment-categories/travel-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[8]; - break; - case 'Pets': - icon = require('../../assets/moment-categories/pets-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[9]; - break; - case 'Fitness': - icon = require('../../assets/moment-categories/fitness-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[10]; - break; - case 'DIY': - icon = require('../../assets/moment-categories/diy-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[11]; - break; - case 'Nature': - icon = require('../../assets/moment-categories/nature-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[12]; - break; - case 'Early Life': - icon = require('../../assets/moment-categories/early-life-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[13]; - break; - case 'Beauty': - icon = require('../../assets/moment-categories/beauty-icon.png'); - bgColor = MOMENT_CATEGORY_BG_COLORS[14]; - break; - default: - // All custom categories - icon = require('../../assets/moment-categories/custom-icon.png'); - // A quick deterministic "random" color picker by summing up ascii char codees - const charCodeSum = categoryType - .split('') - .reduce((acc: number, x: string) => acc + x.charCodeAt(0), 0); - bgColor = - MOMENT_CATEGORY_BG_COLORS[ - charCodeSum % MOMENT_CATEGORY_BG_COLORS.length - ]; - break; - } + const {icon, bgColor} = getMomentCategoryIconInfo(categoryType); /** * The Linear Gradient helps us add a gradient border when the category is already added /selected by user diff --git a/src/components/profile/MomentMoreInfoDrawer.tsx b/src/components/profile/MomentMoreInfoDrawer.tsx index 910aa095..493f6238 100644 --- a/src/components/profile/MomentMoreInfoDrawer.tsx +++ b/src/components/profile/MomentMoreInfoDrawer.tsx @@ -129,6 +129,7 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => { screenType: screenType, selectedTags: tags, moment: moment, + selectedCategory: moment.moment_category, }); }; diff --git a/src/constants/constants.ts b/src/constants/constants.ts index f4ffd750..13a73208 100644 --- a/src/constants/constants.ts +++ b/src/constants/constants.ts @@ -66,6 +66,7 @@ export const YOUTUBE_FONT_COLOR: string = '#FCA4A4'; export const TAGG_PURPLE = '#8F01FF'; export const TAGG_DARK_BLUE = '#4E699C'; +export const TAGG_DARK_PURPLEISH_BLUE = '#4755A1'; export const TAGG_LIGHT_BLUE: string = '#698DD3'; export const TAGG_LIGHT_BLUE_2: string = '#6EE7E7'; export const TAGG_LIGHT_PURPLE = '#F4DDFF'; diff --git a/src/constants/strings.ts b/src/constants/strings.ts index 112bc546..071b3835 100644 --- a/src/constants/strings.ts +++ b/src/constants/strings.ts @@ -45,6 +45,7 @@ export const ERROR_SELECT_GENDER = 'Please select your gender'; export const ERROR_SELECT_UNIVERSITY = 'Please select your University'; export const ERROR_SERVER_DOWN = 'mhm, looks like our servers are down, please refresh and try again in a few mins'; export const ERROR_SOMETHING_WENT_WRONG = 'Oh dear, don’t worry someone will be held responsible for this error, In the meantime refresh the app'; +export const ERROR_NO_MOMENT_CATEGORY = 'Please select a category!'; export const ERROR_SOMETHING_WENT_WRONG_REFRESH = "Ha, looks like this one's on us, please refresh and try again"; export const ERROR_SOMETHING_WENT_WRONG_RELOAD = "You broke it, Just kidding! we don't know what happened... Please reload the app and try again"; export const ERROR_T_AND_C_NOT_ACCEPTED = 'You must first agree to the terms and conditions.'; diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx index bc023da8..c569d2d6 100644 --- a/src/routes/main/MainStackNavigator.tsx +++ b/src/routes/main/MainStackNavigator.tsx @@ -35,13 +35,25 @@ export type MainStackParams = { userXId: string | undefined; screenType: ScreenType; }; + CameraScreen: { + screenType: ScreenType; + selectedCategory?: string; + }; + ZoomInCropper: { + media: {uri: string; isVideo: boolean}; + screenType: ScreenType; + selectedCategory?: string; + }; CaptionScreen: { - title?: string; - media?: {uri: string; isVideo: boolean}; screenType: ScreenType; + media?: {uri: string; isVideo: boolean}; + selectedCategory?: string; selectedTags?: MomentTagType[]; moment?: MomentType; }; + ChoosingCategoryScreen: { + newCustomCategory?: string; + }; TagFriendsScreen: { media: { uri: string; @@ -80,6 +92,7 @@ export type MainStackParams = { }; CreateCustomCategory: { existingCategories: string[]; + fromScreen: 'ChoosingCategoryScreen' | 'CategorySelection'; }; Notifications: { screenType: ScreenType; @@ -109,15 +122,6 @@ export type MainStackParams = { ChatList: undefined; Chat: undefined; NewChatModal: undefined; - ZoomInCropper: { - media: {uri: string; isVideo: boolean}; - screenType: ScreenType; - title: string; - }; - CameraScreen: { - title: string; - screenType: ScreenType; - }; }; export const MainStack = createStackNavigator<MainStackParams>(); diff --git a/src/routes/main/MainStackScreen.tsx b/src/routes/main/MainStackScreen.tsx index 948f37b8..15300c0d 100644 --- a/src/routes/main/MainStackScreen.tsx +++ b/src/routes/main/MainStackScreen.tsx @@ -40,6 +40,7 @@ import {ScreenType} from '../../types'; import {AvatarHeaderHeight, ChatHeaderHeight, SCREEN_WIDTH} from '../../utils'; import {MainStack, MainStackParams} from './MainStackNavigator'; import {ZoomInCropper} from '../../components/comments/ZoomInCropper'; +import ChoosingCategoryScreen from '../../screens/profile/ChoosingCategoryScreen'; /** * Profile : To display the logged in user's profile when the userXId passed in to it is (undefined | null | empty string) else displays profile of the user being visited. @@ -181,6 +182,13 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => { }} /> <MainStack.Screen + name="ChoosingCategoryScreen" + component={ChoosingCategoryScreen} + options={{ + ...headerBarOptions('white', 'Categories'), + }} + /> + <MainStack.Screen name="SocialMediaTaggs" component={SocialMediaTaggs} initialParams={{screenType}} diff --git a/src/routes/tabs/NavigationBar.tsx b/src/routes/tabs/NavigationBar.tsx index 12f6ab58..c3f0b9f8 100644 --- a/src/routes/tabs/NavigationBar.tsx +++ b/src/routes/tabs/NavigationBar.tsx @@ -66,7 +66,7 @@ const NavigationBar: React.FC = () => { disabled={!focused} /> ); - case 'Profile': + case 'ProfileTab': return <NavigationIcon tab="Profile" disabled={!focused} />; case 'SuggestedPeople': return ( @@ -118,7 +118,7 @@ const NavigationBar: React.FC = () => { }} /> <Tabs.Screen - name="Profile" + name="ProfileTab" component={MainStackScreen} initialParams={{screenType: ScreenType.Profile}} /> diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx index dd3612ca..2282ecad 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(); } }; diff --git a/src/screens/moments/TagFriendsScreen.tsx b/src/screens/moments/TagFriendsScreen.tsx index 6c982936..fc3bccf2 100644 --- a/src/screens/moments/TagFriendsScreen.tsx +++ b/src/screens/moments/TagFriendsScreen.tsx @@ -16,11 +16,11 @@ import {MomentTags} from '../../components'; import {TagFriendsFooter} from '../../components/moments'; import {MomentTagType} from '../../types'; import { - SCREEN_WIDTH, - SCREEN_HEIGHT, HeaderHeight, isIPhoneX, normalize, + SCREEN_HEIGHT, + SCREEN_WIDTH, } from '../../utils'; type TagFriendsScreenRouteProps = RouteProp< @@ -120,10 +120,10 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => { return ( <View style={styles.contentContainer}> <View - style={{ - position: 'absolute', - paddingTop: SCREEN_HEIGHT / 2 - imageHeight / 2, - }}> + style={[ + styles.innerContainer, + {paddingTop: SCREEN_HEIGHT / 2 - imageHeight / 2}, + ]}> <TouchableWithoutFeedback onPress={() => navigation.navigate('TagSelectionScreen', { @@ -206,6 +206,7 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => { 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 @@ -295,6 +296,9 @@ const styles = StyleSheet.create({ bottom: 0, height: SCREEN_HEIGHT * 0.15, }, + innerContainer: { + position: 'absolute', + }, }); export default TagFriendsScreen; diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 7fef73db..1232eb66 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'; /** * 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,54 +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(() => { if (isMediaAVideo) { Alert.alert( 'Beautiful, the Moment was uploaded successfully! Check back in a bit and refresh to see it!', ); } else { - Alert.alert(''); + Alert.alert(SUCCESS_PIC_UPLOAD); } }, 500); + } else { + // if editing, simply go back to profile screen + navigation.navigate('Profile', { + userXId: undefined, + screenType: route.params.screenType, + }); } }; @@ -142,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; @@ -163,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; @@ -187,12 +215,13 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { 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) { @@ -204,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}> @@ -218,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 ? ( - <Video - style={styles.media} - source={{uri: mediaUri}} - repeat={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> @@ -297,48 +384,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/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'; diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index 0c93876a..50cedef9 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -59,6 +59,7 @@ export const postMoment = async ( export const patchMoment = async ( momentId: string, caption: string, + category: string, tags: { x: number; y: number; @@ -69,7 +70,8 @@ export const patchMoment = async ( try { const request = new FormData(); request.append('moment_id', momentId); - request.append('captions', JSON.stringify({[momentId]: caption})); + request.append('caption', caption); + request.append('category', category); request.append('tags', JSON.stringify(tags)); const token = await AsyncStorage.getItem('token'); let response = await fetch(MOMENTS_ENDPOINT, { diff --git a/src/utils/comments.tsx b/src/utils/comments.tsx index 28879622..504631f5 100644 --- a/src/utils/comments.tsx +++ b/src/utils/comments.tsx @@ -82,12 +82,17 @@ export const renderTextWithMentions: React.FC<RenderProps> = ({ export const mentionPartTypes: ( theme: 'blue' | 'white', component: 'caption' | 'comment', -) => PartType[] = (theme, component) => { + isShowBelowStyle?: boolean, +) => PartType[] = (theme, component, isShowBelowStyle = false) => { return [ { trigger: '@', renderSuggestions: (props) => ( - <TaggTypeahead component={component} {...props} /> + <TaggTypeahead + component={component} + isShowBelowStyle={isShowBelowStyle} + {...props} + /> ), allowedSpacesCount: 0, isInsertSpaceAfterMention: true, diff --git a/src/utils/moments.ts b/src/utils/moments.ts index 9e8cc332..4aadb4d4 100644 --- a/src/utils/moments.ts +++ b/src/utils/moments.ts @@ -1,4 +1,6 @@ import moment from 'moment'; +import {ImageSourcePropType} from 'react-native'; +import {MOMENT_CATEGORY_BG_COLORS} from '../constants'; /** * Formats elapsed time from a given time. @@ -71,3 +73,85 @@ export const getTimeInShorthand = (date_time: string) => { } return time; }; + +export const getMomentCategoryIconInfo = (category: string) => { + let icon: ImageSourcePropType, bgColor: string; + switch (category) { + case 'Friends': + icon = require('../assets/moment-categories/friends-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[0]; + break; + case 'Adventure': + icon = require('../assets/moment-categories/adventure-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[1]; + break; + case 'Photo Dump': + icon = require('../assets/moment-categories/photo-dump-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[2]; + break; + case 'Food': + icon = require('../assets/moment-categories/food-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[3]; + break; + case 'Music': + icon = require('../assets/moment-categories/music-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[4]; + break; + case 'Art': + icon = require('../assets/moment-categories/art-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[5]; + break; + case 'Sports': + icon = require('../assets/moment-categories/sports-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[6]; + break; + case 'Fashion': + icon = require('../assets/moment-categories/fashion-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[7]; + break; + case 'Travel': + icon = require('../assets/moment-categories/travel-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[8]; + break; + case 'Pets': + icon = require('../assets/moment-categories/pets-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[9]; + break; + case 'Fitness': + icon = require('../assets/moment-categories/fitness-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[10]; + break; + case 'DIY': + icon = require('../assets/moment-categories/diy-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[11]; + break; + case 'Nature': + icon = require('../assets/moment-categories/nature-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[12]; + break; + case 'Early Life': + icon = require('../assets/moment-categories/early-life-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[13]; + break; + case 'Beauty': + icon = require('../assets/moment-categories/beauty-icon.png'); + bgColor = MOMENT_CATEGORY_BG_COLORS[14]; + break; + default: + // All custom categories + icon = require('../assets/moment-categories/custom-icon.png'); + // A quick deterministic "random" color picker by summing up ascii char codees + const charCodeSum = category + .split('') + .reduce((acc: number, x: string) => acc + x.charCodeAt(0), 0); + bgColor = + MOMENT_CATEGORY_BG_COLORS[ + charCodeSum % MOMENT_CATEGORY_BG_COLORS.length + ]; + break; + } + return { + icon, + bgColor, + }; +}; |