diff options
| author | Ivan Chen <ivan@tagg.id> | 2021-04-21 13:57:09 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-21 13:57:09 -0400 |
| commit | 4e8e1c0d58424e6b63cfb8470fc0a73c0e6b102b (patch) | |
| tree | 5b2dcc2daffc5b6f4fa2ae2b55c5548a12b6210c /src/screens/profile | |
| parent | b556f97a7eae3f9d9fce2d9c5c25f419ba77fa14 (diff) | |
| parent | f902c4938344dd25d82d7d01035476f488db2b24 (diff) | |
Merge pull request #370 from IvanIFChen/tma795-move-create-button
[TMA-795/796] Move create button
Diffstat (limited to 'src/screens/profile')
| -rw-r--r-- | src/screens/profile/AccountType.tsx | 3 | ||||
| -rw-r--r-- | src/screens/profile/CaptionScreen.tsx | 5 | ||||
| -rw-r--r-- | src/screens/profile/CategorySelection.tsx | 273 | ||||
| -rw-r--r-- | src/screens/profile/CreateCustomCategory.tsx | 121 | ||||
| -rw-r--r-- | src/screens/profile/IndividualMoment.tsx | 4 | ||||
| -rw-r--r-- | src/screens/profile/index.ts | 2 |
6 files changed, 401 insertions, 7 deletions
diff --git a/src/screens/profile/AccountType.tsx b/src/screens/profile/AccountType.tsx index 60ed0668..8a3f7775 100644 --- a/src/screens/profile/AccountType.tsx +++ b/src/screens/profile/AccountType.tsx @@ -10,6 +10,7 @@ import { import {SafeAreaView} from 'react-native-safe-area-context'; import {useDispatch, useSelector} from 'react-redux'; import {Background} from '../../components'; +import {TAGG_LIGHT_BLUE_2} from '../../constants'; import {updateProfileVisibility} from '../../services'; import {NO_PROFILE} from '../../store/initialStates'; import {RootState} from '../../store/rootReducer'; @@ -60,7 +61,7 @@ const AccountType: React.FC = () => { /> {!isLoading && ( <Switch - trackColor={{false: 'red', true: '#6EE7E7'}} + trackColor={{false: 'red', true: TAGG_LIGHT_BLUE_2}} thumbColor={'white'} ios_backgroundColor="transparent" style={styles.switchStyles} diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 998897e2..282857d6 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -13,11 +13,12 @@ import { } from 'react-native'; import {Button} from 'react-native-elements'; import {useDispatch, useSelector} from 'react-redux'; -import {MainStackParams} from '../../routes'; import {SearchBackground, TaggBigInput} from '../../components'; import {CaptionScreenHeader} from '../../components/'; import TaggLoadingIndicator from '../../components/common/TaggLoadingIndicator'; +import {TAGG_LIGHT_BLUE_2} from '../../constants'; import {ERROR_UPLOAD, SUCCESS_PIC_UPLOAD} from '../../constants/strings'; +import {MainStackParams} from '../../routes'; import {postMoment} from '../../services'; import { loadUserMoments, @@ -137,7 +138,7 @@ const styles = StyleSheet.create({ }, shareButtonTitle: { fontWeight: 'bold', - color: '#6EE7E7', + color: TAGG_LIGHT_BLUE_2, }, header: { marginVertical: 20, diff --git a/src/screens/profile/CategorySelection.tsx b/src/screens/profile/CategorySelection.tsx new file mode 100644 index 00000000..c02eef0d --- /dev/null +++ b/src/screens/profile/CategorySelection.tsx @@ -0,0 +1,273 @@ +import {RouteProp} from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; +import React, {useEffect, useState} from 'react'; +import { + Alert, + StatusBar, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import {ScrollView} from 'react-native-gesture-handler'; +import {useDispatch, useSelector} from 'react-redux'; +import PlusIcon from '../../assets/icons/plus_icon-01.svg'; +import {Background, MomentCategory} from '../../components'; +import {MOMENT_CATEGORIES, TAGG_LIGHT_BLUE_2} from '../../constants'; +import {ERROR_SOMETHING_WENT_WRONG} from '../../constants/strings'; +import {MainStackParams} from '../../routes'; +import {updateMomentCategories} from '../../store/actions'; +import {RootState} from '../../store/rootReducer'; +import {BackgroundGradientType} from '../../types'; +import {normalize, SCREEN_WIDTH} from '../../utils'; + +type CategorySelectionRouteProps = RouteProp< + MainStackParams, + 'CategorySelection' +>; + +type CategorySelectionNavigationProps = StackNavigationProp< + MainStackParams, + 'CategorySelection' +>; + +interface CategorySelectionProps { + route: CategorySelectionRouteProps; + navigation: CategorySelectionNavigationProps; +} + +const CategorySelection: React.FC<CategorySelectionProps> = ({ + route, + navigation, +}) => { + const dispatch = useDispatch(); + const {newCustomCategory} = route.params; + const {momentCategories = []} = useSelector( + (state: RootState) => state.momentCategories, + ); + + // Stores all the categories that will be saved to the store + const [selectedCategories, setSelectedCategories] = useState<string[]>([]); + + /** + * Stores all the custom categories for the UI, allow easier logic for + * unchecking a custom category. + * + * Each uncommited custom category should also have a copy in selectedCategories + * since that's the final value that will be stored in the store. + */ + const [uncommitedCustomCategories, setUncommitedCustomCategories] = useState< + string[] + >([]); + + const customCategories = momentCategories.filter( + (mc) => !MOMENT_CATEGORIES.includes(mc), + ); + + navigation.setOptions({ + headerRight: () => ( + <TouchableOpacity onPress={handleButtonPress}> + <Text style={styles.createLabel}>Create</Text> + </TouchableOpacity> + ), + }); + + useEffect(() => { + if (newCustomCategory) { + setUncommitedCustomCategories([ + ...uncommitedCustomCategories, + newCustomCategory, + ]); + selectedCategories.push(newCustomCategory); + } + }, [newCustomCategory]); + + /** + * Handle selection of a new category + * case isAdded: + * Return without doing anything + * case isSelected: + * Add to the selected categories + * case not isSelected: + * Remove from the selected categories + */ + const onSelect = ( + category: string, + isSelected: boolean, + isAdded: boolean, + ) => { + if (isAdded) { + return; + } + if (isSelected) { + setSelectedCategories((prev) => [...prev, category]); + } else { + setSelectedCategories( + selectedCategories.filter((item) => item !== category), + ); + } + }; + + /** + * Handle deselection of custom category. + * + * Custom categories is "added" and "selected" by CreateCustomCategory screen. + * User can only "deselect" an uncommited custom category. + * + * case isAdded || isSelected: + * Return without doing anything + * default: + * Remove from selected categories AND uncommitedCustomCategories + */ + const onDeselectCustomCategory = ( + category: string, + isSelected: boolean, + isAdded: boolean, + ) => { + if (isAdded || isSelected) { + return; + } + setSelectedCategories( + selectedCategories.filter((item) => item !== category), + ); + setUncommitedCustomCategories( + uncommitedCustomCategories.filter((item) => item !== category), + ); + }; + + const handleButtonPress = async () => { + if (momentCategories.length + selectedCategories.length === 0) { + Alert.alert('Please select at least 1 category'); + return; + } + try { + dispatch( + updateMomentCategories( + momentCategories.concat(selectedCategories), + true, + ), + ); + navigation.goBack(); + } catch (error) { + console.log(error); + Alert.alert(ERROR_SOMETHING_WENT_WRONG); + } + }; + + /** + * Using a scroll view to accomodate dynamic category creation later on + */ + return ( + <ScrollView bounces={false}> + <Background + style={styles.container} + gradientType={BackgroundGradientType.Dark}> + <StatusBar barStyle="light-content" /> + <Text style={styles.subtext}>Create Categories</Text> + <View style={styles.container}> + <TouchableOpacity + style={styles.createCategory} + onPress={() => { + navigation.push('CreateCustomCategory', { + existingCategories: momentCategories.concat(selectedCategories), + }); + }}> + <PlusIcon width={30} height={30} color="white" /> + <Text style={styles.createCategoryLabel}> + Create your own category + </Text> + </TouchableOpacity> + <View style={styles.linkerContainer}> + {/* commited custom categories */} + {customCategories.map((category, index) => ( + <MomentCategory + key={index} + categoryType={category} + isSelected={false} + isAdded={true} + onSelect={onDeselectCustomCategory} + /> + ))} + {/* uncommited custom categroies */} + {uncommitedCustomCategories.map((category, index) => ( + <MomentCategory + key={index} + categoryType={category} + isSelected={selectedCategories.includes(category)} + isAdded={false} + onSelect={onDeselectCustomCategory} + /> + ))} + {customCategories.length + uncommitedCustomCategories.length !== + 0 && <View style={styles.divider} />} + {MOMENT_CATEGORIES.map((category, index) => ( + <MomentCategory + key={index} + categoryType={category} + isSelected={selectedCategories.includes(category)} + isAdded={momentCategories.includes(category)} + onSelect={onSelect} + /> + ))} + </View> + </View> + </Background> + </ScrollView> + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'space-around', + marginBottom: '10%', + }, + linkerContainer: { + flex: 1, + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'center', + alignContent: 'center', + marginBottom: '10%', + }, + subtext: { + color: '#fff', + fontSize: 20, + fontWeight: '600', + textAlign: 'center', + marginVertical: '8%', + marginHorizontal: '10%', + marginTop: '15%', + }, + createCategory: { + backgroundColor: '#53329B', + width: SCREEN_WIDTH * 0.9, + height: 70, + alignItems: 'center', + justifyContent: 'center', + borderRadius: 10, + flexDirection: 'row', + marginBottom: '5%', + }, + createCategoryLabel: { + color: 'white', + marginLeft: '3%', + fontSize: 18, + fontWeight: '500', + }, + divider: { + borderColor: 'white', + borderBottomWidth: 1, + width: SCREEN_WIDTH * 0.9, + marginVertical: '2%', + }, + createLabel: { + color: TAGG_LIGHT_BLUE_2, + marginRight: 20, + fontSize: normalize(15), + fontWeight: '800', + }, +}); + +export default CategorySelection; diff --git a/src/screens/profile/CreateCustomCategory.tsx b/src/screens/profile/CreateCustomCategory.tsx new file mode 100644 index 00000000..c4b17b1e --- /dev/null +++ b/src/screens/profile/CreateCustomCategory.tsx @@ -0,0 +1,121 @@ +import {RouteProp} from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; +import React, {useState} from 'react'; +import { + Alert, + KeyboardAvoidingView, + StatusBar, + StyleSheet, + Text, + TextInput, + TouchableOpacity, +} from 'react-native'; +import {Background} from '../../components'; +import {MainStackParams} from '../../routes'; +import {BackgroundGradientType} from '../../types'; +import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; + +type CreateCustomCategoryRouteProps = RouteProp< + MainStackParams, + 'CreateCustomCategory' +>; + +type CreateCustomCategoryNavigationProps = StackNavigationProp< + MainStackParams, + 'CreateCustomCategory' +>; + +interface CreateCustomCategoryProps { + route: CreateCustomCategoryRouteProps; + navigation: CreateCustomCategoryNavigationProps; +} + +const CreateCustomCategory: React.FC<CreateCustomCategoryProps> = ({ + route, + navigation, +}) => { + /** + * Same component to be used for category selection while onboarding and while on profile + */ + const {existingCategories} = 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', { + newCustomCategory: newCategory, + }); + } + }; + + return ( + <> + <StatusBar barStyle="light-content" /> + <Background + style={styles.container} + gradientType={BackgroundGradientType.Dark}> + <KeyboardAvoidingView + style={styles.innerContainer} + behavior={'padding'}> + <Text style={styles.title}>Give your category a name</Text> + <TextInput + style={styles.input} + selectionColor={'white'} + onChangeText={setNewCategory} + autoFocus={true} + /> + <TouchableOpacity + onPress={handleButtonPress} + style={styles.finalAction}> + <Text style={styles.finalActionLabel}>{'Create'}</Text> + </TouchableOpacity> + </KeyboardAvoidingView> + </Background> + </> + ); +}; + +const styles = StyleSheet.create({ + container: { + alignItems: 'center', + minHeight: SCREEN_HEIGHT, + }, + innerContainer: { + height: '40%', + top: '20%', + justifyContent: 'space-around', + alignItems: 'center', + }, + title: { + color: 'white', + fontSize: 20, + fontWeight: '600', + }, + input: { + width: SCREEN_WIDTH * 0.75, + fontSize: 30, + color: 'white', + textAlign: 'center', + borderBottomWidth: 1, + borderBottomColor: 'white', + }, + finalAction: { + backgroundColor: 'white', + justifyContent: 'center', + alignItems: 'center', + width: 150, + height: 40, + borderRadius: 5, + borderWidth: 1, + borderColor: '#8F01FF', + }, + finalActionLabel: { + fontSize: 16, + fontWeight: '500', + color: 'black', + }, +}); + +export default CreateCustomCategory; diff --git a/src/screens/profile/IndividualMoment.tsx b/src/screens/profile/IndividualMoment.tsx index 871d62bf..515cbacf 100644 --- a/src/screens/profile/IndividualMoment.tsx +++ b/src/screens/profile/IndividualMoment.tsx @@ -114,10 +114,6 @@ const styles = StyleSheet.create({ flex: 1, paddingBottom: 0, }, - shareButtonTitle: { - fontWeight: 'bold', - color: '#6EE7E7', - }, content: { flex: 9, }, diff --git a/src/screens/profile/index.ts b/src/screens/profile/index.ts index b7efdd3b..d5377494 100644 --- a/src/screens/profile/index.ts +++ b/src/screens/profile/index.ts @@ -10,3 +10,5 @@ export {default as InviteFriendsScreen} from './InviteFriendsScreen'; export {default as SettingsScreen} from './SettingsScreen'; export {default as PrivacyScreen} from './PrivacyScreen'; export {default as AccountType} from './AccountType'; +export {default as CategorySelection} from './CategorySelection'; +export {default as CreateCustomCategory} from './CreateCustomCategory'; |
