diff options
author | Ashm Walia <40498934+ashmgarv@users.noreply.github.com> | 2020-12-22 08:50:27 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-22 11:50:27 -0500 |
commit | a954d6b6b88485dddc0ccfda634ffd102cb34ccd (patch) | |
tree | 560f152dd92ccb482a2bbf6b094060525373322c /src/components | |
parent | 49ed044f5103cf6288fcf5b3ff6d3d720795860c (diff) |
[TMA 446] Create category (#144)
* Added welcome page
* Working code
* Small fix
* Some more cleanup
* Fixes
* Cleanup
* Fix again
* Use gradient for white bg as well
* Fixed type
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/common/ComingSoon.tsx | 5 | ||||
-rw-r--r-- | src/components/common/TaggPopup.tsx | 133 | ||||
-rw-r--r-- | src/components/common/index.ts | 1 | ||||
-rw-r--r-- | src/components/moments/Moment.tsx | 32 | ||||
-rw-r--r-- | src/components/onboarding/Background.tsx | 12 | ||||
-rw-r--r-- | src/components/onboarding/MomentCategory.tsx | 175 | ||||
-rw-r--r-- | src/components/onboarding/index.ts | 1 | ||||
-rw-r--r-- | src/components/profile/Content.tsx | 96 |
8 files changed, 435 insertions, 20 deletions
diff --git a/src/components/common/ComingSoon.tsx b/src/components/common/ComingSoon.tsx index 16b65b58..d7654a20 100644 --- a/src/components/common/ComingSoon.tsx +++ b/src/components/common/ComingSoon.tsx @@ -1,11 +1,14 @@ import * as React from 'react'; import {StyleSheet, View, Text, Image} from 'react-native'; +import {BackgroundGradientType} from '../../types'; import {SCREEN_WIDTH} from '../../utils'; import {Background} from '../onboarding'; const ComingSoon: React.FC = () => { return ( - <Background style={styles.container}> + <Background + style={styles.container} + gradientType={BackgroundGradientType.Light}> <View style={styles.textContainer}> <Text style={styles.header}>Coming Soon</Text> <Text style={styles.subtext}> diff --git a/src/components/common/TaggPopup.tsx b/src/components/common/TaggPopup.tsx new file mode 100644 index 00000000..db24adb8 --- /dev/null +++ b/src/components/common/TaggPopup.tsx @@ -0,0 +1,133 @@ +import {RouteProp} from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; +import * as React from 'react'; +import {Platform, Text, StyleSheet, TouchableOpacity} from 'react-native'; +import {Image, View} from 'react-native-animatable'; +import {ArrowButton} from '..'; +import {OnboardingStackParams} from '../../routes'; +import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +import CloseIcon from '../../assets/ionicons/close-outline.svg'; + +type TaggPopupRouteProps = RouteProp<OnboardingStackParams, 'TaggPopup'>; +type TaggPopupNavigationProps = StackNavigationProp< + OnboardingStackParams, + 'TaggPopup' +>; + +interface TaggPopupProps { + route: TaggPopupRouteProps; + navigation: TaggPopupNavigationProps; +} + +const TaggPopup: React.FC<TaggPopupProps> = ({route, navigation}) => { + /** + * Custom popup / Tutorial screen for Tagg + * Just like a Singly Linked List, we have a next node + * if (next !== undefined) + * Display the next button and navigate to next popup node on click + * else + * Display close button, navigate back on close + */ + const {messageHeader, messageBody, next} = route.params.popupProps; + + return ( + <View style={styles.container}> + <View style={styles.popup}> + <Image + style={styles.icon} + source={require('../../assets/icons/plus-logo.png')} + /> + <View style={styles.textContainer}> + <Text style={styles.header}>{messageHeader}</Text> + <Text style={styles.subtext}>{messageBody}</Text> + </View> + {!next && ( + <TouchableOpacity + style={styles.closeButton} + onPress={() => { + navigation.goBack(); + }}> + <CloseIcon height={'50%'} width={'50%'} color={'white'} /> + </TouchableOpacity> + )} + </View> + {next && ( + <View style={styles.footer}> + <ArrowButton + direction="forward" + onPress={() => { + navigation.navigate('TaggPopup', {popupProps: next}); + }} + /> + </View> + )} + </View> + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + }, + whiteColor: { + color: 'white', + }, + closeButton: { + position: 'relative', + height: '50%', + aspectRatio: 1, + left: '20%', + }, + textContainer: { + flex: 1, + flexDirection: 'column', + }, + icon: { + width: 40, + height: 40, + marginVertical: '1%', + }, + header: { + color: '#fff', + fontSize: 16, + fontWeight: '600', + textAlign: 'justify', + marginBottom: '2%', + marginHorizontal: '2%', + }, + subtext: { + color: '#fff', + fontSize: 12, + fontWeight: '600', + textAlign: 'justify', + marginBottom: '15%', + marginHorizontal: '2%', + }, + popup: { + width: SCREEN_WIDTH * 0.8, + height: SCREEN_WIDTH * 0.2, + backgroundColor: 'black', + borderRadius: 8, + flexDirection: 'row', + alignSelf: 'auto', + flexWrap: 'wrap', + position: 'absolute', + bottom: SCREEN_HEIGHT * 0.7, + }, + footer: { + marginLeft: '50%', + flexDirection: 'column-reverse', + ...Platform.select({ + ios: { + bottom: '20%', + }, + android: { + bottom: '10%', + }, + }), + }, +}); +export default TaggPopup; diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 661d2f52..d5d36297 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -17,3 +17,4 @@ export {default as TaggDatePicker} from './TaggDatePicker'; export {default as BottomDrawer} from './BottomDrawer'; export {default as TaggLoadingTndicator} from './TaggLoadingIndicator'; export {default as GenericMoreInfoDrawer} from './GenericMoreInfoDrawer'; +export {default as TaggPopUp} from './TaggPopup'; diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx index 940b519c..fb6186c8 100644 --- a/src/components/moments/Moment.tsx +++ b/src/components/moments/Moment.tsx @@ -5,18 +5,21 @@ import {Text} from 'react-native-animatable'; import {ScrollView, TouchableOpacity} from 'react-native-gesture-handler'; import LinearGradient from 'react-native-linear-gradient'; import PlusIcon from '../../assets/icons/plus_icon-01.svg'; +import DeleteIcon from '../../assets/icons/delete-logo.svg'; import BigPlusIcon from '../../assets/icons/plus_icon-02.svg'; import {TAGG_TEXT_LIGHT_BLUE} from '../../constants'; import {SCREEN_WIDTH} from '../../utils'; import ImagePicker from 'react-native-image-crop-picker'; import MomentTile from './MomentTile'; -import {MomentType, ScreenType} from 'src/types'; +import {MomentCategoryType, MomentType, ScreenType} from 'src/types'; interface MomentProps { - title: string; + title: MomentCategoryType; images: MomentType[] | undefined; userXId: string | undefined; screenType: ScreenType; + handleMomentCategoryDelete: (_: MomentCategoryType) => void; + shouldAllowDeletion: boolean; } const Moment: React.FC<MomentProps> = ({ @@ -24,6 +27,8 @@ const Moment: React.FC<MomentProps> = ({ images, userXId, screenType, + handleMomentCategoryDelete, + shouldAllowDeletion, }) => { const navigation = useNavigation(); @@ -53,11 +58,21 @@ const Moment: React.FC<MomentProps> = ({ <View style={styles.header}> <Text style={styles.titleText}>{title}</Text> {!userXId ? ( - <PlusIcon - width={21} - height={21} - onPress={() => navigateToImagePicker()} - /> + <> + <PlusIcon + width={21} + height={21} + onPress={() => navigateToImagePicker()} + style={{marginRight: 10}} + /> + {shouldAllowDeletion && ( + <DeleteIcon + onPress={() => handleMomentCategoryDelete(title)} + width={19} + height={19} + /> + )} + </> ) : ( <React.Fragment /> )} @@ -113,6 +128,9 @@ const styles = StyleSheet.create({ fontSize: 16, fontWeight: 'bold', color: TAGG_TEXT_LIGHT_BLUE, + flex: 1, + flexDirection: 'row', + justifyContent: 'flex-end', }, scrollContainer: { height: SCREEN_WIDTH / 3.25, diff --git a/src/components/onboarding/Background.tsx b/src/components/onboarding/Background.tsx index 054eeff6..fb08e945 100644 --- a/src/components/onboarding/Background.tsx +++ b/src/components/onboarding/Background.tsx @@ -8,23 +8,27 @@ import { SafeAreaView, } from 'react-native'; import {CenteredView} from '../common'; +import {BackgroundGradientType} from '../../types'; +import {BACKGROUND_GRADIENT_MAP} from '../../constants'; interface BackgroundProps extends ViewProps { centered?: boolean; + gradientType: BackgroundGradientType; } const Background: React.FC<BackgroundProps> = (props) => { + const {centered, gradientType, children} = props; return ( <LinearGradient - colors={['#9F00FF', '#27EAE9']} + colors={BACKGROUND_GRADIENT_MAP[gradientType]} useAngle={true} angle={154.72} angleCenter={{x: 0.5, y: 0.5}} style={styles.container}> <TouchableWithoutFeedback accessible={false} onPress={Keyboard.dismiss}> - {props.centered ? ( - <CenteredView {...props}>{props.children}</CenteredView> + {centered ? ( + <CenteredView {...props}>{children}</CenteredView> ) : ( - <SafeAreaView {...props}>{props.children}</SafeAreaView> + <SafeAreaView {...props}>{children}</SafeAreaView> )} </TouchableWithoutFeedback> </LinearGradient> diff --git a/src/components/onboarding/MomentCategory.tsx b/src/components/onboarding/MomentCategory.tsx new file mode 100644 index 00000000..25e8995a --- /dev/null +++ b/src/components/onboarding/MomentCategory.tsx @@ -0,0 +1,175 @@ +import * as React from 'react'; +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 {MomentCategoryType} from '../../types'; +import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; + +type MomentCategoryProps = { + categoryType: MomentCategoryType; + onSelect: ( + category: MomentCategoryType, + isSelected: boolean, + isAdded: boolean, + ) => void; + isSelected: boolean; + isAdded: boolean; +}; + +const MomentCategory: React.FC<MomentCategoryProps> = ({ + categoryType, + isSelected, + 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 = '#5E4AE4'; + break; + case 'Adventure': + icon = require('../../assets/moment-categories/adventure-icon.png'); + bgColor = '#5044A6'; + break; + case 'Photo Dump': + icon = require('../../assets/moment-categories/photo-dump-icon.png'); + bgColor = '#4755A1'; + break; + case 'Food': + icon = require('../../assets/moment-categories/food-icon.png'); + bgColor = '#444BA8'; + break; + case 'Music': + icon = require('../../assets/moment-categories/music-icon.png'); + bgColor = '#374898'; + break; + case 'Art': + icon = require('../../assets/moment-categories/art-icon.png'); + bgColor = '#3F5C97'; + break; + case 'Sports': + icon = require('../../assets/moment-categories/sports-icon.png'); + bgColor = '#3A649F'; + break; + case 'Fashion': + icon = require('../../assets/moment-categories/fashion-icon.png'); + bgColor = '#386A95'; + break; + case 'Travel': + icon = require('../../assets/moment-categories/travel-icon.png'); + bgColor = '#366D84'; + break; + case 'Pets': + icon = require('../../assets/moment-categories/pets-icon.png'); + bgColor = '#335E76'; + break; + case 'Nightlife': + icon = require('../../assets/moment-categories/nightlife-icon.png'); + bgColor = '#2E5471'; + break; + case 'DIY': + icon = require('../../assets/moment-categories/diy-icon.png'); + bgColor = '#274765'; + break; + case 'Nature': + icon = require('../../assets/moment-categories/nature-icon.png'); + bgColor = '#225363'; + break; + case 'Early Life': + icon = require('../../assets/moment-categories/early-life-icon.png'); + bgColor = '#365F6A'; + break; + case 'Beauty': + icon = require('../../assets/moment-categories/beauty-icon.png'); + bgColor = '#4E7175'; + break; + } + + /** + * The Linear Gradient helps us add a gradient border when the category is already added /selected by user + * if(isAdded) + * gradient background + * if(isSelected) + * white background + * else + * transparent background + */ + return ( + <LinearGradient + colors={ + isAdded + ? BACKGROUND_GRADIENT_MAP[0] + : isSelected + ? ['white', 'white'] + : ['transparent', 'transparent'] + } + start={{x: 0, y: 0}} + end={{x: 1, y: 0}} + style={[styles.container, styles.gradient]}> + <TouchableOpacity + activeOpacity={0.5} + onPress={() => onSelect(categoryType, !isSelected, isAdded)} + style={[ + styles.container, + styles.touchable, + {backgroundColor: bgColor}, + ]}> + <Image source={icon} style={styles.icon} /> + <Text style={styles.label}>{categoryType}</Text> + {isAdded && ( + <Image + source={require('../../assets/images/link-tick.png')} + style={styles.tick} + /> + )} + </TouchableOpacity> + </LinearGradient> + ); +}; + +const styles = StyleSheet.create({ + gradient: { + width: SCREEN_WIDTH / 3.7, + height: SCREEN_HEIGHT / 5.8, + marginHorizontal: '2%', + marginVertical: '2%', + }, + touchable: { + width: SCREEN_WIDTH / 4, + height: SCREEN_HEIGHT / 6.2, + marginHorizontal: '2%', + marginVertical: '4%', + }, + container: { + borderRadius: 8, + shadowRadius: 2, + shadowOffset: {width: 0, height: 2}, + shadowOpacity: 0.4, + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + }, + icon: { + width: 40, + height: 40, + marginVertical: '8%', + }, + label: { + fontWeight: '500', + color: 'white', + }, + tick: { + marginTop: '3%', + width: 15, + height: 15, + }, +}); + +export default MomentCategory; diff --git a/src/components/onboarding/index.ts b/src/components/onboarding/index.ts index fde4e0af..b790933f 100644 --- a/src/components/onboarding/index.ts +++ b/src/components/onboarding/index.ts @@ -9,3 +9,4 @@ export {default as BirthDatePicker} from './BirthDatePicker'; export {default as TaggDropDown} from './TaggDropDown'; export {default as SocialMediaLinker} from './SocialMediaLinker'; export {default as LinkSocialMedia} from './LinkSocialMedia'; +export {default as MomentCategory} from './MomentCategory'; diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx index f2e0db0a..7064f775 100644 --- a/src/components/profile/Content.tsx +++ b/src/components/profile/Content.tsx @@ -1,21 +1,29 @@ import React, {useCallback, useEffect, useState} from 'react'; import { + Alert, LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, RefreshControl, StyleSheet, + Text, View, } from 'react-native'; import Animated from 'react-native-reanimated'; import { + CategorySelectionScreenType, + MomentCategoryType, MomentType, ProfilePreviewType, ProfileType, ScreenType, UserType, } from '../../types'; -import {COVER_HEIGHT, defaultMoments} from '../../constants'; +import { + COVER_HEIGHT, + MOMENT_CATEGORIES, + TAGG_TEXT_LIGHT_BLUE, +} from '../../constants'; import {fetchUserX, SCREEN_HEIGHT, userLogin} from '../../utils'; import TaggsBar from '../taggs/TaggsBar'; import {Moment} from '../moments'; @@ -29,15 +37,19 @@ import { blockUnblockUser, loadFollowData, updateUserXFollowersAndFollowing, + updateMomentCategories, } from '../../store/actions'; import { NO_USER, NO_PROFILE, EMPTY_PROFILE_PREVIEW_LIST, EMPTY_MOMENTS_LIST, + MOMENT_CATEGORIES_MAP, } from '../../store/initialStates'; import {Cover} from '.'; -import {Background} from '../onboarding'; +import {TouchableOpacity} from 'react-native-gesture-handler'; +import {useNavigation} from '@react-navigation/native'; +import {deleteMomentCategories} from '../../services'; interface ContentProps { y: Animated.Value<number>; @@ -60,6 +72,10 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { ? useSelector((state: RootState) => state.userX[screenType][userXId]) : useSelector((state: RootState) => state.moments); + const {momentCategories = MOMENT_CATEGORIES_MAP} = userXId + ? useSelector((state: RootState) => state.userX[screenType][userXId]) + : useSelector((state: RootState) => state.momentCategories); + const {blockedUsers = EMPTY_PROFILE_PREVIEW_LIST} = useSelector( (state: RootState) => state.blocked, ); @@ -68,6 +84,8 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { ); const state = useStore().getState(); + const navigation = useNavigation(); + /** * States */ @@ -80,6 +98,13 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { const [shouldBounce, setShouldBounce] = useState<boolean>(true); const [refreshing, setRefreshing] = useState<boolean>(false); + /** + * Filter list of categories already selected by user + */ + const userMomentCategories = MOMENT_CATEGORIES.filter( + (category) => momentCategories[category] === true, + ); + const onRefresh = useCallback(() => { const refrestState = async () => { if (!userXId) { @@ -194,6 +219,33 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { await dispatch(updateUserXFollowersAndFollowing(user.userId, state)); }; + /** + * Handle deletion of a category + * Confirm with user before deleting the category + * @param category category to be deleted + */ + const handleCategoryDeletion = (category: MomentCategoryType) => { + Alert.alert( + 'Category Deletion', + `Are you sure that you want to delete the category ${category} ?`, + [ + { + text: 'Cancel', + style: 'cancel', + }, + { + text: 'Yes', + onPress: () => { + dispatch( + updateMomentCategories([category], false, loggedInUser.userId), + ); + }, + }, + ], + {cancelable: true}, + ); + }; + const handleScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => { /** * Set the new y position @@ -239,32 +291,60 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { /> <TaggsBar {...{y, profileBodyHeight, userXId, screenType}} /> <View style={styles.momentsContainer}> - {defaultMoments.map((title, index) => ( + {userMomentCategories.map((title, index) => ( <Moment key={index} title={title} images={imagesMap.get(title)} userXId={userXId} screenType={screenType} + handleMomentCategoryDelete={handleCategoryDeletion} + shouldAllowDeletion={userMomentCategories.length > 2} /> ))} + {!userXId && userMomentCategories.length < 6 && ( + <TouchableOpacity + onPress={() => + navigation.push('CategorySelection', { + categories: momentCategories, + screenType: CategorySelectionScreenType.Profile, + user: loggedInUser, + }) + } + style={styles.createCategoryButton}> + <Text style={styles.createCategoryButtonLabel}> + Create a new category + </Text> + </TouchableOpacity> + )} </View> </Animated.ScrollView> ); }; const styles = StyleSheet.create({ - refreshControlContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, container: { flex: 1, }, momentsContainer: { backgroundColor: '#f2f2f2', paddingBottom: SCREEN_HEIGHT / 10, + flex: 1, + flexDirection: 'column', + }, + createCategoryButton: { + backgroundColor: TAGG_TEXT_LIGHT_BLUE, + justifyContent: 'center', + alignItems: 'center', + width: '70%', + height: 30, + marginTop: '15%', + alignSelf: 'center', + }, + createCategoryButtonLabel: { + fontSize: 16, + fontWeight: '500', + color: 'white', }, }); |