aboutsummaryrefslogtreecommitdiff
path: root/src/screens/profile
diff options
context:
space:
mode:
authorIvan Chen <ivan@tagg.id>2021-04-15 16:56:10 -0400
committerIvan Chen <ivan@tagg.id>2021-04-15 16:56:10 -0400
commitf8762fd3a0140bff92fe96985e3abdb9c2925dac (patch)
treef7cc54229a20ee0dc07ec1dada2f256bf4bfd981 /src/screens/profile
parente4a3fb8f1d45a83440454c2eefeea7d312cbe0c5 (diff)
moved CategorySelection and CreateCustomCategory to profile folder
Diffstat (limited to 'src/screens/profile')
-rw-r--r--src/screens/profile/CategorySelection.tsx302
-rw-r--r--src/screens/profile/CreateCustomCategory.tsx121
-rw-r--r--src/screens/profile/index.ts2
3 files changed, 425 insertions, 0 deletions
diff --git a/src/screens/profile/CategorySelection.tsx b/src/screens/profile/CategorySelection.tsx
new file mode 100644
index 00000000..d3958025
--- /dev/null
+++ b/src/screens/profile/CategorySelection.tsx
@@ -0,0 +1,302 @@
+import {RouteProp} from '@react-navigation/native';
+import {StackNavigationProp} from '@react-navigation/stack';
+import React, {useEffect, useState} from 'react';
+import {
+ Alert,
+ Platform,
+ 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} 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 {SCREEN_WIDTH} from '../../utils';
+
+type CategorySelectionRouteProps = RouteProp<
+ MainStackParams,
+ 'CategorySelection'
+>;
+
+type CategorySelectionNavigationProps = StackNavigationProp<
+ MainStackParams,
+ 'CategorySelection'
+>;
+
+interface CategorySelectionProps {
+ route: CategorySelectionRouteProps;
+ navigation: CategorySelectionNavigationProps;
+}
+
+const CategorySelection: React.FC<CategorySelectioProps> = ({
+ route,
+ navigation,
+}) => {
+ 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),
+ );
+
+ const dispatch = useDispatch();
+
+ 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>
+ <TouchableOpacity
+ onPress={handleButtonPress}
+ style={styles.finalAction}>
+ <Text style={styles.finalActionLabel}>Create</Text>
+ </TouchableOpacity>
+ </View>
+ </Background>
+ </ScrollView>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'space-around',
+ marginBottom: '10%',
+ },
+ wizard: {
+ ...Platform.select({
+ ios: {
+ top: 50,
+ },
+ android: {
+ bottom: 40,
+ },
+ }),
+ },
+ linkerContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ justifyContent: 'center',
+ alignContent: 'center',
+ marginBottom: '10%',
+ },
+ header: {
+ color: '#fff',
+ fontSize: 22,
+ fontWeight: '600',
+ textAlign: 'center',
+ marginBottom: '4%',
+ },
+ subtext: {
+ color: '#fff',
+ fontSize: 20,
+ fontWeight: '600',
+ textAlign: 'center',
+ marginVertical: '8%',
+ marginHorizontal: '10%',
+ marginTop: '15%',
+ },
+ finalAction: {
+ backgroundColor: 'white',
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: 150,
+ height: 40,
+ borderRadius: 5,
+ borderWidth: 1,
+ borderColor: 'white',
+ marginBottom: '25%',
+ },
+ finalActionLabel: {
+ fontSize: 16,
+ fontWeight: '500',
+ color: 'black',
+ },
+ 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',
+ },
+ plusIcon: {
+ color: 'white',
+ },
+ divider: {
+ borderColor: 'white',
+ borderBottomWidth: 1,
+ width: SCREEN_WIDTH * 0.9,
+ marginVertical: '2%',
+ },
+});
+
+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/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';