aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/assets/icons/added-border.svg1
-rw-r--r--src/assets/icons/delete-logo.pngbin0 -> 122662 bytes
-rw-r--r--src/assets/icons/delete-logo.svg1
-rw-r--r--src/assets/icons/plus-logo.pngbin0 -> 19640 bytes
-rw-r--r--src/assets/images/welcome.pngbin0 -> 304406 bytes
-rw-r--r--src/assets/moment-categories/adventure-icon.pngbin0 -> 2833 bytes
-rw-r--r--src/assets/moment-categories/art-icon.pngbin0 -> 2929 bytes
-rw-r--r--src/assets/moment-categories/beauty-icon.pngbin0 -> 3906 bytes
-rw-r--r--src/assets/moment-categories/diy-icon.pngbin0 -> 3113 bytes
-rw-r--r--src/assets/moment-categories/early-life-icon.pngbin0 -> 1995 bytes
-rw-r--r--src/assets/moment-categories/fashion-icon.pngbin0 -> 2132 bytes
-rw-r--r--src/assets/moment-categories/food-icon.pngbin0 -> 3465 bytes
-rw-r--r--src/assets/moment-categories/friends-icon.pngbin0 -> 3412 bytes
-rw-r--r--src/assets/moment-categories/music-icon.pngbin0 -> 2363 bytes
-rw-r--r--src/assets/moment-categories/nature-icon.pngbin0 -> 3016 bytes
-rw-r--r--src/assets/moment-categories/nightlife-icon.pngbin0 -> 4288 bytes
-rw-r--r--src/assets/moment-categories/pets-icon.pngbin0 -> 3307 bytes
-rw-r--r--src/assets/moment-categories/photo-dump-icon.pngbin0 -> 3935 bytes
-rw-r--r--src/assets/moment-categories/sports-icon.pngbin0 -> 4040 bytes
-rw-r--r--src/assets/moment-categories/travel-icon.pngbin0 -> 2662 bytes
-rw-r--r--src/components/common/ComingSoon.tsx5
-rw-r--r--src/components/common/TaggPopup.tsx133
-rw-r--r--src/components/common/index.ts1
-rw-r--r--src/components/moments/Moment.tsx32
-rw-r--r--src/components/onboarding/Background.tsx12
-rw-r--r--src/components/onboarding/MomentCategory.tsx175
-rw-r--r--src/components/onboarding/index.ts1
-rw-r--r--src/components/profile/Content.tsx96
-rw-r--r--src/constants/api.ts1
-rw-r--r--src/constants/constants.ts28
-rw-r--r--src/routes/onboarding/Onboarding.tsx44
-rw-r--r--src/routes/onboarding/OnboardingStack.tsx17
-rw-r--r--src/routes/profile/Profile.tsx12
-rw-r--r--src/routes/profile/ProfileStack.tsx6
-rw-r--r--src/screens/onboarding/CategorySelection.tsx241
-rw-r--r--src/screens/onboarding/Checkpoint.tsx5
-rw-r--r--src/screens/onboarding/InvitationCodeVerification.tsx10
-rw-r--r--src/screens/onboarding/Login.tsx18
-rw-r--r--src/screens/onboarding/PasswordReset.tsx5
-rw-r--r--src/screens/onboarding/PasswordResetRequest.tsx6
-rw-r--r--src/screens/onboarding/ProfileOnboarding.tsx3
-rw-r--r--src/screens/onboarding/RegistrationOne.tsx6
-rw-r--r--src/screens/onboarding/RegistrationThree.tsx5
-rw-r--r--src/screens/onboarding/RegistrationTwo.tsx5
-rw-r--r--src/screens/onboarding/SocialMedia.tsx42
-rw-r--r--src/screens/onboarding/Verification.tsx7
-rw-r--r--src/screens/onboarding/WelcomeScreen.tsx94
-rw-r--r--src/screens/onboarding/index.ts2
-rw-r--r--src/screens/profile/EditProfile.tsx4
-rw-r--r--src/services/MomentCategoryService.ts88
-rw-r--r--src/services/index.ts1
-rw-r--r--src/store/actions/index.ts1
-rw-r--r--src/store/actions/momentCategories.tsx63
-rw-r--r--src/store/actions/userX.ts10
-rw-r--r--src/store/initialStates.ts25
-rw-r--r--src/store/reducers/index.ts1
-rw-r--r--src/store/reducers/momentCategoryReducer.tsx22
-rw-r--r--src/store/reducers/userXReducer.ts17
-rw-r--r--src/store/rootReducer.ts2
-rw-r--r--src/types/types.ts46
-rw-r--r--src/utils/users.ts2
61 files changed, 1235 insertions, 61 deletions
diff --git a/src/assets/icons/added-border.svg b/src/assets/icons/added-border.svg
new file mode 100644
index 00000000..ee6a9da3
--- /dev/null
+++ b/src/assets/icons/added-border.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 990.61 1129.48"><defs><style>.cls-1{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="499.51" y1="1150.26" x2="491.94" y2="95.78" gradientUnits="userSpaceOnUse"><stop offset="0.05" stop-color="#6ee7e7"/><stop offset="1" stop-color="#8f00ff"/></linearGradient></defs><path class="cls-1" d="M924.24,0H66.37A66.37,66.37,0,0,0,0,66.37v996.74a66.38,66.38,0,0,0,66.37,66.37H924.24a66.37,66.37,0,0,0,66.37-66.37V66.37A66.36,66.36,0,0,0,924.24,0Zm38.5,1054a46.55,46.55,0,0,1-46.55,46.56H74.43A46.56,46.56,0,0,1,27.87,1054V75.06A46.57,46.57,0,0,1,74.43,28.5H916.19a46.56,46.56,0,0,1,46.55,46.56Z"/></svg> \ No newline at end of file
diff --git a/src/assets/icons/delete-logo.png b/src/assets/icons/delete-logo.png
new file mode 100644
index 00000000..54a7228b
--- /dev/null
+++ b/src/assets/icons/delete-logo.png
Binary files differ
diff --git a/src/assets/icons/delete-logo.svg b/src/assets/icons/delete-logo.svg
new file mode 100644
index 00000000..7e8e445e
--- /dev/null
+++ b/src/assets/icons/delete-logo.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 792 792"><defs><style>.cls-1,.cls-2{fill:none;stroke:#ed2224;stroke-miterlimit:10;stroke-width:56.39px;}.cls-2{stroke-linecap:round;}</style></defs><ellipse class="cls-1" cx="395.85" cy="395.85" rx="365.48" ry="365.55"/><line class="cls-2" x1="198.4" y1="395.51" x2="593.29" y2="395.51"/></svg> \ No newline at end of file
diff --git a/src/assets/icons/plus-logo.png b/src/assets/icons/plus-logo.png
new file mode 100644
index 00000000..195f28fc
--- /dev/null
+++ b/src/assets/icons/plus-logo.png
Binary files differ
diff --git a/src/assets/images/welcome.png b/src/assets/images/welcome.png
new file mode 100644
index 00000000..46ab4f9f
--- /dev/null
+++ b/src/assets/images/welcome.png
Binary files differ
diff --git a/src/assets/moment-categories/adventure-icon.png b/src/assets/moment-categories/adventure-icon.png
new file mode 100644
index 00000000..33f821ec
--- /dev/null
+++ b/src/assets/moment-categories/adventure-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/art-icon.png b/src/assets/moment-categories/art-icon.png
new file mode 100644
index 00000000..c43d941b
--- /dev/null
+++ b/src/assets/moment-categories/art-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/beauty-icon.png b/src/assets/moment-categories/beauty-icon.png
new file mode 100644
index 00000000..1df48648
--- /dev/null
+++ b/src/assets/moment-categories/beauty-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/diy-icon.png b/src/assets/moment-categories/diy-icon.png
new file mode 100644
index 00000000..2c339489
--- /dev/null
+++ b/src/assets/moment-categories/diy-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/early-life-icon.png b/src/assets/moment-categories/early-life-icon.png
new file mode 100644
index 00000000..91aca375
--- /dev/null
+++ b/src/assets/moment-categories/early-life-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/fashion-icon.png b/src/assets/moment-categories/fashion-icon.png
new file mode 100644
index 00000000..106b81ec
--- /dev/null
+++ b/src/assets/moment-categories/fashion-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/food-icon.png b/src/assets/moment-categories/food-icon.png
new file mode 100644
index 00000000..476521a8
--- /dev/null
+++ b/src/assets/moment-categories/food-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/friends-icon.png b/src/assets/moment-categories/friends-icon.png
new file mode 100644
index 00000000..6b595dd1
--- /dev/null
+++ b/src/assets/moment-categories/friends-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/music-icon.png b/src/assets/moment-categories/music-icon.png
new file mode 100644
index 00000000..8e5d82f9
--- /dev/null
+++ b/src/assets/moment-categories/music-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/nature-icon.png b/src/assets/moment-categories/nature-icon.png
new file mode 100644
index 00000000..2870694c
--- /dev/null
+++ b/src/assets/moment-categories/nature-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/nightlife-icon.png b/src/assets/moment-categories/nightlife-icon.png
new file mode 100644
index 00000000..1e473b6c
--- /dev/null
+++ b/src/assets/moment-categories/nightlife-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/pets-icon.png b/src/assets/moment-categories/pets-icon.png
new file mode 100644
index 00000000..91f65f3c
--- /dev/null
+++ b/src/assets/moment-categories/pets-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/photo-dump-icon.png b/src/assets/moment-categories/photo-dump-icon.png
new file mode 100644
index 00000000..ee5585c3
--- /dev/null
+++ b/src/assets/moment-categories/photo-dump-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/sports-icon.png b/src/assets/moment-categories/sports-icon.png
new file mode 100644
index 00000000..9edd76b8
--- /dev/null
+++ b/src/assets/moment-categories/sports-icon.png
Binary files differ
diff --git a/src/assets/moment-categories/travel-icon.png b/src/assets/moment-categories/travel-icon.png
new file mode 100644
index 00000000..5a913ac7
--- /dev/null
+++ b/src/assets/moment-categories/travel-icon.png
Binary files differ
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',
},
});
diff --git a/src/constants/api.ts b/src/constants/api.ts
index f9ac3d7c..890ef102 100644
--- a/src/constants/api.ts
+++ b/src/constants/api.ts
@@ -26,6 +26,7 @@ export const ALL_USERS_ENDPOINT: string = API_URL + 'users/';
export const REPORT_ISSUE_ENDPOINT: string = API_URL + 'report/';
export const BLOCK_USER_ENDPOINT: string = API_URL + 'block/';
export const PASSWORD_RESET_ENDPOINT: string = API_URL + 'password-reset/';
+export const MOMENT_CATEGORY_ENDPOINT: string = API_URL + 'moment-category/';
// Register Social Link (Non-integrated)
export const LINK_SNAPCHAT_ENDPOINT: string = API_URL + 'link-sc/';
diff --git a/src/constants/constants.ts b/src/constants/constants.ts
index 3fed8fe6..52a52de6 100644
--- a/src/constants/constants.ts
+++ b/src/constants/constants.ts
@@ -1,3 +1,5 @@
+import {ReactText} from 'react';
+import {BackgroundGradientType, MomentCategoryType} from './../types/';
import {SCREEN_WIDTH, SCREEN_HEIGHT, isIPhoneX} from '../utils';
export const CHIN_HEIGHT = 34;
@@ -102,3 +104,29 @@ export const BROWSABLE_SOCIAL_URLS: Record<string, string> = {
Instagram: 'https://instagram.com/',
Twitter: 'https://twitter.com/',
};
+
+export const MOMENT_CATEGORIES: Array<MomentCategoryType> = [
+ 'Friends',
+ 'Adventure',
+ 'Photo Dump',
+ 'Food',
+ 'Music',
+ 'Art',
+ 'Sports',
+ 'Fashion',
+ 'Travel',
+ 'Pets',
+ 'Nightlife',
+ 'DIY',
+ 'Nature',
+ 'Early Life',
+ 'Beauty',
+];
+
+export const BACKGROUND_GRADIENT_MAP: Record<
+ BackgroundGradientType,
+ Array<ReactText>
+> = {
+ [BackgroundGradientType.Light]: ['#9F00FF', '#27EAE9'],
+ [BackgroundGradientType.Dark]: ['#421566', '#385D5E'],
+};
diff --git a/src/routes/onboarding/Onboarding.tsx b/src/routes/onboarding/Onboarding.tsx
index 63a75934..a3d281f5 100644
--- a/src/routes/onboarding/Onboarding.tsx
+++ b/src/routes/onboarding/Onboarding.tsx
@@ -12,8 +12,11 @@ import {
SocialMedia,
PasswordResetRequest,
PasswordReset,
+ WelcomeScreen,
+ CategorySelection,
} from '../../screens';
import {StackCardInterpolationProps} from '@react-navigation/stack';
+import TaggPopup from '../../components/common/TaggPopup';
const forFade = ({current}: StackCardInterpolationProps) => ({
cardStyle: {
@@ -42,6 +45,47 @@ const Onboarding: React.FC = () => {
}}
/>
<OnboardingStack.Screen
+ name="WelcomeScreen"
+ component={WelcomeScreen}
+ options={{
+ gestureEnabled: false,
+ }}
+ />
+ <OnboardingStack.Screen
+ name="CategorySelection"
+ component={CategorySelection}
+ options={{
+ gestureEnabled: false,
+ }}
+ />
+ <OnboardingStack.Screen
+ name="TaggPopup"
+ component={TaggPopup}
+ options={{
+ gestureEnabled: false,
+ cardStyle: {
+ backgroundColor: 'transparent',
+ },
+ cardOverlayEnabled: true,
+ cardStyleInterpolator: ({current: {progress}}) => ({
+ cardStyle: {
+ opacity: progress.interpolate({
+ inputRange: [0, 0.5, 0.9, 1],
+ outputRange: [0, 0.25, 0.7, 1],
+ }),
+ },
+ overlayStyle: {
+ backgroundColor: '#505050',
+ opacity: progress.interpolate({
+ inputRange: [0, 1],
+ outputRange: [0, 0.9],
+ extrapolate: 'clamp',
+ }),
+ },
+ }),
+ }}
+ />
+ <OnboardingStack.Screen
name="PasswordReset"
component={PasswordReset}
options={{
diff --git a/src/routes/onboarding/OnboardingStack.tsx b/src/routes/onboarding/OnboardingStack.tsx
index 33ff51ea..7ff00271 100644
--- a/src/routes/onboarding/OnboardingStack.tsx
+++ b/src/routes/onboarding/OnboardingStack.tsx
@@ -1,7 +1,14 @@
import {createStackNavigator} from '@react-navigation/stack';
-import {VerificationScreenType} from '../../types';
+import {
+ CategorySelectionScreenType,
+ MomentCategoryType,
+ TaggPopupType,
+ UserType,
+ VerificationScreenType,
+} from '../../types';
export type OnboardingStackParams = {
+ WelcomeScreen: undefined;
Login: undefined;
PasswordResetRequest: undefined;
PasswordReset: {
@@ -20,6 +27,14 @@ export type OnboardingStackParams = {
Verification: {id: string; screenType: VerificationScreenType};
ProfileOnboarding: {username: string; userId: string};
SocialMedia: {username: string; userId: string};
+ CategorySelection: {
+ categories: Record<MomentCategoryType, boolean>;
+ screenType: CategorySelectionScreenType;
+ user: UserType;
+ };
+ TaggPopup: {
+ popupProps: TaggPopupType;
+ };
};
export const OnboardingStack = createStackNavigator<OnboardingStackParams>();
diff --git a/src/routes/profile/Profile.tsx b/src/routes/profile/Profile.tsx
index 3cb928e5..4c93b1ee 100644
--- a/src/routes/profile/Profile.tsx
+++ b/src/routes/profile/Profile.tsx
@@ -8,6 +8,7 @@ import {
MomentCommentsScreen,
FollowersListScreen,
EditProfile,
+ CategorySelection,
} from '../../screens';
import {ProfileStack, ProfileStackParams} from './ProfileStack';
import {RouteProp} from '@react-navigation/native';
@@ -90,6 +91,17 @@ const Profile: React.FC<ProfileStackProps> = ({route}) => {
}}
initialParams={{screenType}}
/>
+ <ProfileStack.Screen
+ name="CategorySelection"
+ component={CategorySelection}
+ options={{
+ headerShown: true,
+ headerTransparent: true,
+ headerBackTitleVisible: false,
+ headerTintColor: 'white',
+ headerTitle: '',
+ }}
+ />
{isProfileStack ? (
<ProfileStack.Screen name="CaptionScreen" component={CaptionScreen} />
) : (
diff --git a/src/routes/profile/ProfileStack.tsx b/src/routes/profile/ProfileStack.tsx
index e7db9f37..bc0a9560 100644
--- a/src/routes/profile/ProfileStack.tsx
+++ b/src/routes/profile/ProfileStack.tsx
@@ -2,7 +2,7 @@
* Note the name userXId here, it refers to the id of the user being visited
*/
import {createStackNavigator} from '@react-navigation/stack';
-import {MomentType, ScreenType} from '../../types';
+import {CategorySelectionScreenType, MomentType, ScreenType} from '../../types';
export type ProfileStackParams = {
Search: {
@@ -41,6 +41,10 @@ export type ProfileStackParams = {
userId: string;
username: string;
};
+ CategorySelection: {
+ categories: Array<string>;
+ screenType: CategorySelectionScreenType;
+ };
};
export const ProfileStack = createStackNavigator<ProfileStackParams>();
diff --git a/src/screens/onboarding/CategorySelection.tsx b/src/screens/onboarding/CategorySelection.tsx
new file mode 100644
index 00000000..f92b7e39
--- /dev/null
+++ b/src/screens/onboarding/CategorySelection.tsx
@@ -0,0 +1,241 @@
+import {RouteProp} from '@react-navigation/native';
+import React, {useCallback, useEffect, useState} from 'react';
+import {
+ Alert,
+ KeyboardAvoidingView,
+ Platform,
+ StatusBar,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+} from 'react-native';
+import {useDispatch} from 'react-redux';
+import {
+ BackgroundGradientType,
+ CategorySelectionScreenType,
+ MomentCategoryType,
+} from '../../types';
+import {Background, MomentCategory} from '../../components';
+import {MOMENT_CATEGORIES} from '../../constants';
+import {OnboardingStackParams} from '../../routes';
+import {StackNavigationProp} from '@react-navigation/stack';
+import {getTokenOrLogout, userLogin} from '../../utils';
+import {postMomentCategories} from '../../services';
+import {updateMomentCategories} from '../../store/actions/momentCategories';
+import {ScrollView} from 'react-native-gesture-handler';
+
+type CategorySelectionRouteProps = RouteProp<
+ OnboardingStackParams,
+ 'CategorySelection'
+>;
+
+type CategorySelectionNavigationProps = StackNavigationProp<
+ OnboardingStackParams,
+ 'CategorySelection'
+>;
+
+interface CategorySelectionProps {
+ route: CategorySelectionRouteProps;
+ navigation: CategorySelectionNavigationProps;
+}
+
+const CategorySelection: React.FC<CategorySelectionProps> = ({
+ route,
+ navigation,
+}) => {
+ /**
+ * Same component to be used for category selection while onboarding and while on profile
+ */
+ const {categories, screenType, user} = route.params;
+ const isOnBoarding: boolean =
+ screenType === CategorySelectionScreenType.Onboarding;
+ const {userId, username} = user;
+
+ const [selectedCategories, setSelectedCategories] = useState<
+ Array<MomentCategoryType>
+ >([]);
+
+ const dispatch = useDispatch();
+
+ /**
+ * Show the tutorial if a new user is OnBoarding
+ */
+ useEffect(() => {
+ if (isOnBoarding) {
+ navigation.navigate('TaggPopup', {
+ popupProps: {
+ messageHeader: 'Category And Moments',
+ messageBody:
+ 'Use pictures and videos to share different aspects of you',
+ next: {
+ messageHeader: 'Select Categories',
+ messageBody:
+ 'Select between 2 - 6 categories to begin creating moments!',
+ next: undefined,
+ },
+ },
+ });
+ }
+ }, [isOnBoarding]);
+
+ /**
+ * 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: MomentCategoryType,
+ isSelected: boolean,
+ isAdded: boolean,
+ ) => {
+ if (isAdded) return;
+ if (isSelected) {
+ setSelectedCategories((prev) => [...prev, category]);
+ } else {
+ setSelectedCategories(
+ selectedCategories.filter((item) => item !== category),
+ );
+ }
+ };
+
+ /**
+ * if onboarding
+ * Count of already added categories will always be 0
+ * else
+ * Calculate number of selected categories by iterating through the user's pre-selected categories
+ */
+ const addedLength = !isOnBoarding
+ ? Object.keys(categories).filter((key) => {
+ return categories[key as MomentCategoryType] === true;
+ }).length
+ : 0;
+
+ const handleButtonPress = async () => {
+ /**
+ * Check for lower and upper bound before creating new categories
+ */
+ const totalCategories = addedLength + selectedCategories.length;
+ if (totalCategories < 2) {
+ Alert.alert('Please select atleast 2 categories');
+ return;
+ } else if (totalCategories > 6) {
+ Alert.alert('You may not add more than 6 categories');
+ return;
+ } else if (selectedCategories.length === 0) {
+ Alert.alert('Please select some categories!');
+ return;
+ }
+ try {
+ if (isOnBoarding) {
+ const token = await getTokenOrLogout(dispatch);
+ await postMomentCategories(selectedCategories, token);
+ userLogin(dispatch, {userId: userId, username: username});
+ } else {
+ dispatch(updateMomentCategories(selectedCategories, true, userId));
+ navigation.goBack();
+ }
+ } catch (error) {
+ console.log(error);
+ Alert.alert('There was a problem');
+ }
+ };
+
+ /**
+ * 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 new categories</Text>
+ <View style={styles.container}>
+ <View style={styles.linkerContainer}>
+ {MOMENT_CATEGORIES.map((category, index) => (
+ <MomentCategory
+ key={index}
+ categoryType={category}
+ isSelected={selectedCategories.includes(category)}
+ isAdded={categories[category]}
+ onSelect={onSelect}
+ />
+ ))}
+ </View>
+ <TouchableOpacity
+ onPress={handleButtonPress}
+ style={styles.finalAction}>
+ <Text style={styles.finalActionLabel}>
+ {isOnBoarding ? 'Login' : '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: 16,
+ fontWeight: '600',
+ textAlign: 'center',
+ marginVertical: '8%',
+ marginHorizontal: '10%',
+ },
+ finalAction: {
+ backgroundColor: 'white',
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: 150,
+ height: 40,
+ borderRadius: 5,
+ borderWidth: 1,
+ borderColor: '#8F01FF',
+ marginBottom: '25%',
+ },
+ finalActionLabel: {
+ fontSize: 16,
+ fontWeight: '500',
+ color: 'black',
+ },
+});
+
+export default CategorySelection;
diff --git a/src/screens/onboarding/Checkpoint.tsx b/src/screens/onboarding/Checkpoint.tsx
index 83a8a2bc..b0b42203 100644
--- a/src/screens/onboarding/Checkpoint.tsx
+++ b/src/screens/onboarding/Checkpoint.tsx
@@ -12,6 +12,7 @@ import {
import {OnboardingStackParams} from '../../routes';
import {RegistrationWizard, Background} from '../../components';
+import {BackgroundGradientType} from '../../types';
type CheckpointRouteProp = RouteProp<OnboardingStackParams, 'Checkpoint'>;
type CheckpointNavigationProp = StackNavigationProp<
@@ -44,7 +45,9 @@ const Checkpoint: React.FC<CheckpointProps> = ({route, navigation}) => {
};
return (
- <Background style={styles.container}>
+ <Background
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
<StatusBar barStyle="light-content" />
<RegistrationWizard style={styles.wizard} step="six" />
<View style={styles.textContainer}>
diff --git a/src/screens/onboarding/InvitationCodeVerification.tsx b/src/screens/onboarding/InvitationCodeVerification.tsx
index 3f0ea124..afdf6d3f 100644
--- a/src/screens/onboarding/InvitationCodeVerification.tsx
+++ b/src/screens/onboarding/InvitationCodeVerification.tsx
@@ -26,7 +26,8 @@ import {
Alert,
Platform,
} from 'react-native';
-import {trackPromise} from 'react-promise-tracker';
+
+import {BackgroundGradientType} from '../../types';
type InvitationCodeVerificationScreenNavigationProp = StackNavigationProp<
OnboardingStackParams,
@@ -86,13 +87,16 @@ const InvitationCodeVerification: React.FC<InvitationCodeVerificationProps> = ({
<View style={styles.footer}>
<ArrowButton
direction="backward"
- onPress={() => navigation.navigate('Login')}
+ onPress={() => navigation.navigate('WelcomeScreen')}
/>
</View>
);
return (
- <Background centered style={styles.container}>
+ <Background
+ centered
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
<RegistrationWizard style={styles.wizard} step="one" />
<KeyboardAvoidingView behavior="padding" style={styles.form}>
<Text style={styles.formHeader}>Enter the code</Text>
diff --git a/src/screens/onboarding/Login.tsx b/src/screens/onboarding/Login.tsx
index cb550ef8..1315fdf5 100644
--- a/src/screens/onboarding/Login.tsx
+++ b/src/screens/onboarding/Login.tsx
@@ -17,10 +17,15 @@ import {OnboardingStackParams} from '../../routes/onboarding';
import {Background, TaggInput, SubmitButton} from '../../components';
import {usernameRegex, LOGIN_ENDPOINT} from '../../constants';
import AsyncStorage from '@react-native-community/async-storage';
-import {UserType} from '../../types';
+import {
+ BackgroundGradientType,
+ CategorySelectionScreenType,
+ UserType,
+} from '../../types';
import {useDispatch} from 'react-redux';
import {userLogin} from '../../utils';
import SplashScreen from 'react-native-splash-screen';
+import {MOMENT_CATEGORIES_MAP} from '../../store/initialStates';
type VerificationScreenRouteProp = RouteProp<OnboardingStackParams, 'Login'>;
type VerificationScreenNavigationProp = StackNavigationProp<
@@ -194,8 +199,8 @@ const Login: React.FC<LoginProps> = ({navigation}: LoginProps) => {
/*
* Handles tap on "Get Started" text by resetting fields & navigating to the registration page.
*/
- const goToRegistration = () => {
- navigation.navigate('InvitationCodeVerification');
+ const startRegistrationProcess = () => {
+ navigation.navigate('WelcomeScreen');
setForm({...form, attemptedSubmit: false});
};
@@ -244,7 +249,7 @@ const Login: React.FC<LoginProps> = ({navigation}: LoginProps) => {
accessible={true}
accessibilityLabel="Get started"
style={styles.getStarted}
- onPress={goToRegistration}>
+ onPress={startRegistrationProcess}>
Get started!
</Text>
</TouchableOpacity>
@@ -252,7 +257,10 @@ const Login: React.FC<LoginProps> = ({navigation}: LoginProps) => {
);
return (
- <Background centered style={styles.container}>
+ <Background
+ centered
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
<StatusBar barStyle="light-content" />
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
diff --git a/src/screens/onboarding/PasswordReset.tsx b/src/screens/onboarding/PasswordReset.tsx
index 25991d6e..11ca60d5 100644
--- a/src/screens/onboarding/PasswordReset.tsx
+++ b/src/screens/onboarding/PasswordReset.tsx
@@ -24,6 +24,7 @@ import {
import {trackPromise} from 'react-promise-tracker';
import {passwordRegex} from '../../constants';
import {handlePasswordReset} from '../../services';
+import {BackgroundGradientType} from '../../types';
type PasswordResetRequestRouteProp = RouteProp<
OnboardingStackParams,
@@ -141,7 +142,9 @@ const PasswordResetRequest: React.FC<PasswordResetRequestProps> = ({
);
return (
- <Background style={styles.container}>
+ <Background
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
<StatusBar barStyle="light-content" />
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
diff --git a/src/screens/onboarding/PasswordResetRequest.tsx b/src/screens/onboarding/PasswordResetRequest.tsx
index 5f67eb19..cf086f59 100644
--- a/src/screens/onboarding/PasswordResetRequest.tsx
+++ b/src/screens/onboarding/PasswordResetRequest.tsx
@@ -24,7 +24,7 @@ import {
import {trackPromise} from 'react-promise-tracker';
import {emailRegex, usernameRegex} from '../../constants';
import {handlePasswordResetRequest} from '../../services';
-import {VerificationScreenType} from '../../types';
+import {BackgroundGradientType, VerificationScreenType} from '../../types';
type PasswordResetRequestRouteProp = RouteProp<
OnboardingStackParams,
@@ -115,7 +115,9 @@ const PasswordResetRequest: React.FC<PasswordResetRequestProps> = ({
);
return (
- <Background style={styles.container}>
+ <Background
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
<StatusBar barStyle="light-content" />
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
diff --git a/src/screens/onboarding/ProfileOnboarding.tsx b/src/screens/onboarding/ProfileOnboarding.tsx
index 9d99f2b1..611f1598 100644
--- a/src/screens/onboarding/ProfileOnboarding.tsx
+++ b/src/screens/onboarding/ProfileOnboarding.tsx
@@ -27,6 +27,7 @@ import {
genderRegex,
} from '../../constants';
import AsyncStorage from '@react-native-community/async-storage';
+import {BackgroundGradientType} from '../../types';
type ProfileOnboardingScreenRouteProp = RouteProp<
OnboardingStackParams,
@@ -343,7 +344,7 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({
};
return (
- <Background centered>
+ <Background centered gradientType={BackgroundGradientType.Light}>
<StatusBar barStyle="light-content" />
<View style={styles.profile}>
<LargeProfilePic />
diff --git a/src/screens/onboarding/RegistrationOne.tsx b/src/screens/onboarding/RegistrationOne.tsx
index 3373b903..54c4e210 100644
--- a/src/screens/onboarding/RegistrationOne.tsx
+++ b/src/screens/onboarding/RegistrationOne.tsx
@@ -27,7 +27,7 @@ import {trackPromise} from 'react-promise-tracker';
import {SEND_OTP_ENDPOINT} from '../../constants';
import {phoneRegex} from '../../constants';
-import {VerificationScreenType} from '../../types';
+import {BackgroundGradientType, VerificationScreenType} from '../../types';
type RegistrationScreenOneRouteProp = RouteProp<
OnboardingStackParams,
@@ -138,7 +138,9 @@ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => {
);
return (
- <Background style={styles.container}>
+ <Background
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
<StatusBar barStyle="light-content" />
<RegistrationWizard style={styles.wizard} step="two" />
<KeyboardAvoidingView
diff --git a/src/screens/onboarding/RegistrationThree.tsx b/src/screens/onboarding/RegistrationThree.tsx
index 614795ca..52a6de84 100644
--- a/src/screens/onboarding/RegistrationThree.tsx
+++ b/src/screens/onboarding/RegistrationThree.tsx
@@ -29,6 +29,7 @@ import {
} from '../../components';
import {passwordRegex, usernameRegex, REGISTER_ENDPOINT} from '../../constants';
import AsyncStorage from '@react-native-community/async-storage';
+import {BackgroundGradientType} from '../../types';
type RegistrationScreenThreeRouteProp = RouteProp<
OnboardingStackParams,
@@ -241,7 +242,9 @@ const RegistrationThree: React.FC<RegistrationThreeProps> = ({
);
return (
- <Background style={styles.container}>
+ <Background
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
<StatusBar barStyle="light-content" />
<RegistrationWizard style={styles.wizard} step="five" />
<KeyboardAvoidingView
diff --git a/src/screens/onboarding/RegistrationTwo.tsx b/src/screens/onboarding/RegistrationTwo.tsx
index edefebaf..2f67d8c8 100644
--- a/src/screens/onboarding/RegistrationTwo.tsx
+++ b/src/screens/onboarding/RegistrationTwo.tsx
@@ -22,6 +22,7 @@ import {
} from '../../components';
import {nameRegex, emailRegex} from '../../constants';
+import {BackgroundGradientType} from '../../types';
type RegistrationScreenTwoRouteProp = RouteProp<
OnboardingStackParams,
@@ -170,7 +171,9 @@ const RegistrationTwo: React.FC<RegistrationTwoProps> = ({
);
return (
- <Background style={styles.container}>
+ <Background
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
<StatusBar barStyle="light-content" />
<RegistrationWizard style={styles.wizard} step="four" />
<KeyboardAvoidingView
diff --git a/src/screens/onboarding/SocialMedia.tsx b/src/screens/onboarding/SocialMedia.tsx
index ee2bed10..d2a43e7a 100644
--- a/src/screens/onboarding/SocialMedia.tsx
+++ b/src/screens/onboarding/SocialMedia.tsx
@@ -1,4 +1,5 @@
import {RouteProp} from '@react-navigation/native';
+import {StackNavigationProp} from '@react-navigation/stack';
import React from 'react';
import {
Alert,
@@ -11,7 +12,11 @@ import {
View,
} from 'react-native';
import {useDispatch} from 'react-redux';
-import {LinkerType} from 'src/types';
+import {
+ BackgroundGradientType,
+ CategorySelectionScreenType,
+ LinkerType,
+} from '../..//types';
import {
Background,
LinkSocialMedia,
@@ -19,7 +24,7 @@ import {
} from '../../components';
import {SOCIAL_LIST} from '../../constants/';
import {OnboardingStackParams} from '../../routes';
-import {userLogin} from '../../utils';
+import {MOMENT_CATEGORIES_MAP} from '../../store/initialStates';
/**
* Social Media Screen for displaying social media linkers
@@ -27,11 +32,17 @@ import {userLogin} from '../../utils';
type SocialMediaRouteProps = RouteProp<OnboardingStackParams, 'SocialMedia'>;
+type SocialMediaNavigationProps = StackNavigationProp<
+ OnboardingStackParams,
+ 'SocialMedia'
+>;
+
interface SocialMediaProps {
route: SocialMediaRouteProps;
+ navigation: SocialMediaNavigationProps;
}
-const SocialMedia: React.FC<SocialMediaProps> = ({route}) => {
+const SocialMedia: React.FC<SocialMediaProps> = ({route, navigation}) => {
const {userId, username} = route.params;
const linkers: Array<LinkerType> = [];
@@ -56,17 +67,18 @@ const SocialMedia: React.FC<SocialMediaProps> = ({route}) => {
// });
// };
- const handleLogin = () => {
- try {
- userLogin(dispatch, {userId: userId, username: username});
- } catch (error) {
- console.log(error);
- Alert.alert('There was a problem logging you in');
- }
+ const handleNext = () => {
+ navigation.navigate('CategorySelection', {
+ categories: MOMENT_CATEGORIES_MAP,
+ screenType: CategorySelectionScreenType.Onboarding,
+ user: {userId: userId, username: username},
+ });
};
return (
- <Background style={styles.container}>
+ <Background
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
<StatusBar barStyle="light-content" />
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
@@ -84,8 +96,8 @@ const SocialMedia: React.FC<SocialMediaProps> = ({route}) => {
))}
</View>
</KeyboardAvoidingView>
- <TouchableOpacity onPress={handleLogin} style={styles.loginButton}>
- <Text style={styles.loginButtonLabel}>Login</Text>
+ <TouchableOpacity onPress={handleNext} style={styles.nextButton}>
+ <Text style={styles.nextButtonLabel}>Next</Text>
</TouchableOpacity>
</Background>
);
@@ -133,7 +145,7 @@ const styles = StyleSheet.create({
marginBottom: '35%',
marginHorizontal: '10%',
},
- loginButton: {
+ nextButton: {
backgroundColor: '#8F01FF',
justifyContent: 'center',
alignItems: 'center',
@@ -144,7 +156,7 @@ const styles = StyleSheet.create({
borderColor: '#8F01FF',
marginBottom: '15%',
},
- loginButtonLabel: {
+ nextButtonLabel: {
fontSize: 16,
fontWeight: '500',
color: '#ddd',
diff --git a/src/screens/onboarding/Verification.tsx b/src/screens/onboarding/Verification.tsx
index 9fa1c12f..c808f30b 100644
--- a/src/screens/onboarding/Verification.tsx
+++ b/src/screens/onboarding/Verification.tsx
@@ -27,7 +27,7 @@ import {
Platform,
} from 'react-native';
import {trackPromise} from 'react-promise-tracker';
-import {VerificationScreenType} from '../../types';
+import {BackgroundGradientType, VerificationScreenType} from '../../types';
import {
handlePasswordCodeVerification,
sendOtp,
@@ -137,7 +137,10 @@ const Verification: React.FC<VerificationProps> = ({route, navigation}) => {
);
return (
- <Background centered style={styles.container}>
+ <Background
+ centered
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
{isPhoneVerification ? (
<RegistrationWizard style={styles.wizard} step="three" />
) : (
diff --git a/src/screens/onboarding/WelcomeScreen.tsx b/src/screens/onboarding/WelcomeScreen.tsx
new file mode 100644
index 00000000..fcdd9bc5
--- /dev/null
+++ b/src/screens/onboarding/WelcomeScreen.tsx
@@ -0,0 +1,94 @@
+import * as React from 'react';
+import {StyleSheet, View, Text, Image, TouchableOpacity} from 'react-native';
+import {SCREEN_WIDTH} from '../../utils';
+import {Background} from '../../components';
+import {OnboardingStackParams} from '../../routes';
+import {StackNavigationProp} from '@react-navigation/stack';
+import {BackgroundGradientType} from '../../types';
+
+type WelcomeScreenNavigationProps = StackNavigationProp<
+ OnboardingStackParams,
+ 'WelcomeScreen'
+>;
+
+interface WelcomeScreenProps {
+ navigation: WelcomeScreenNavigationProps;
+}
+
+const WelcomeScreen: React.FC<WelcomeScreenProps> = ({navigation}) => {
+ const handleNext = () => {
+ navigation.navigate('InvitationCodeVerification');
+ };
+ return (
+ <Background
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
+ <Image
+ source={require('../../assets/images/welcome.png')}
+ style={styles.image}
+ />
+
+ <View>
+ <Text style={styles.header}>Welcome to TAGG!</Text>
+ <Text style={styles.subtext}>
+ This is the new social networking platform for you! It will help you
+ create your own personalized digital space where you can express who
+ you are, along with all the moments that comprehensively define you!
+ </Text>
+ </View>
+ <TouchableOpacity onPress={handleNext} style={styles.nextButton}>
+ <Text style={styles.nextButtonLabel}>Next</Text>
+ </TouchableOpacity>
+ </Background>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ /**
+ * Set primary axis to column
+ * Align items to centre along that primary axis and the secondary axis as well
+ */
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ image: {
+ width: SCREEN_WIDTH,
+ height: SCREEN_WIDTH,
+ },
+ header: {
+ color: '#fff',
+ fontSize: 32,
+ fontWeight: '600',
+ textAlign: 'center',
+ marginBottom: '4%',
+ marginHorizontal: '10%',
+ },
+ subtext: {
+ color: '#fff',
+ fontSize: 16,
+ fontWeight: '600',
+ textAlign: 'center',
+ marginBottom: '15%',
+ marginHorizontal: '10%',
+ },
+ nextButton: {
+ backgroundColor: '#8F01FF',
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: '70%',
+ height: '10%',
+ borderRadius: 5,
+ borderWidth: 1,
+ borderColor: '#8F01FF',
+ marginBottom: '15%',
+ },
+ nextButtonLabel: {
+ fontSize: 30,
+ fontWeight: '500',
+ color: '#ddd',
+ },
+});
+export default WelcomeScreen;
diff --git a/src/screens/onboarding/index.ts b/src/screens/onboarding/index.ts
index 2411a7e7..ec833929 100644
--- a/src/screens/onboarding/index.ts
+++ b/src/screens/onboarding/index.ts
@@ -9,3 +9,5 @@ export {default as InvitationCodeVerification} from './InvitationCodeVerificatio
export {default as SocialMedia} from './SocialMedia';
export {default as PasswordResetRequest} from './PasswordResetRequest';
export {default as PasswordReset} from './PasswordReset';
+export {default as WelcomeScreen} from './WelcomeScreen';
+export {default as CategorySelection} from './CategorySelection';
diff --git a/src/screens/profile/EditProfile.tsx b/src/screens/profile/EditProfile.tsx
index 50e1c006..316ad5d4 100644
--- a/src/screens/profile/EditProfile.tsx
+++ b/src/screens/profile/EditProfile.tsx
@@ -39,6 +39,7 @@ import {HeaderHeight, SCREEN_HEIGHT} from '../../utils';
import {RootState} from '../../store/rootReducer';
import {useDispatch, useSelector} from 'react-redux';
import {loadUserData} from '../../store/actions';
+import {BackgroundGradientType} from '../../types';
type EditProfileNavigationProp = StackNavigationProp<
ProfileStackParams,
@@ -219,7 +220,6 @@ const EditProfile: React.FC<EditProfileProps> = ({route, navigation}) => {
});
};
-
const handleSnapchatUpdate = (newUsername: string) => {
// Allow any username, empty means to "un-link" it
// TODO: refresh taggs bar after
@@ -373,7 +373,7 @@ const EditProfile: React.FC<EditProfileProps> = ({route, navigation}) => {
}, [navigation, handleSubmit]);
return (
- <Background centered>
+ <Background centered gradientType={BackgroundGradientType.Light}>
<SafeAreaView>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
diff --git a/src/services/MomentCategoryService.ts b/src/services/MomentCategoryService.ts
new file mode 100644
index 00000000..8bdb70d2
--- /dev/null
+++ b/src/services/MomentCategoryService.ts
@@ -0,0 +1,88 @@
+import {Alert} from 'react-native';
+import {MomentCategoryType} from './../types/types';
+import {MOMENT_CATEGORY_ENDPOINT} from '../constants';
+
+export const loadMomentCategories: (
+ userId: string,
+ token: string,
+) => Promise<MomentCategoryType[]> = async (userId, token) => {
+ let categories: MomentCategoryType[] = [];
+ try {
+ const response = await fetch(MOMENT_CATEGORY_ENDPOINT + `${userId}/`, {
+ method: 'GET',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
+ });
+ const status = response.status;
+ if (status === 200) {
+ const data = await response.json();
+ categories = data['categories'];
+ } else {
+ console.log('Could not load categories!');
+ return [];
+ }
+ } catch (err) {
+ console.log(err);
+ return [];
+ }
+ return categories;
+};
+
+export const postMomentCategories: (
+ categories: Array<MomentCategoryType>,
+ token: string,
+) => Promise<boolean> = async (categories, token) => {
+ let success = false;
+ try {
+ const response = await fetch(MOMENT_CATEGORY_ENDPOINT, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: 'Token ' + token,
+ },
+ body: JSON.stringify({categories}),
+ });
+ const status = response.status;
+ if (status === 200) {
+ success = true;
+ } else {
+ Alert.alert('There was a problem creating categories!');
+ console.log('Could not post categories!');
+ }
+ } catch (err) {
+ console.log(err);
+ return success;
+ }
+ return success;
+};
+
+export const deleteMomentCategories: (
+ categories: Array<MomentCategoryType>,
+ userId: string,
+ token: string,
+) => Promise<boolean> = async (categories, userId, token) => {
+ let success = false;
+ try {
+ const response = await fetch(MOMENT_CATEGORY_ENDPOINT + `${userId}/`, {
+ method: 'DELETE',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: 'Token ' + token,
+ },
+ body: JSON.stringify({categories}),
+ });
+ const status = response.status;
+ if (status === 200) {
+ Alert.alert(`The category was successfully deleted!`);
+ success = true;
+ } else {
+ Alert.alert('There was a problem while deleteing category!');
+ console.log('Could not delete category!');
+ }
+ } catch (err) {
+ console.log(err);
+ return success;
+ }
+ return success;
+};
diff --git a/src/services/index.ts b/src/services/index.ts
index bce3a75a..d98996ba 100644
--- a/src/services/index.ts
+++ b/src/services/index.ts
@@ -5,3 +5,4 @@ export * from './ExploreServices';
export * from './UserFollowServices';
export * from './ReportingService';
export * from './BlockUserService';
+export * from './MomentCategoryService';
diff --git a/src/store/actions/index.ts b/src/store/actions/index.ts
index 04fa9767..f9fd5e9c 100644
--- a/src/store/actions/index.ts
+++ b/src/store/actions/index.ts
@@ -1,6 +1,7 @@
export * from './user';
export * from './userFollow';
export * from './userMoments';
+export * from './momentCategories';
export * from './socials';
export * from './taggUsers';
export * from './userBlock';
diff --git a/src/store/actions/momentCategories.tsx b/src/store/actions/momentCategories.tsx
new file mode 100644
index 00000000..a522c3e0
--- /dev/null
+++ b/src/store/actions/momentCategories.tsx
@@ -0,0 +1,63 @@
+import {RootState} from '../rootReducer';
+import {
+ deleteMomentCategories,
+ loadMomentCategories,
+ postMomentCategories,
+} from '../../services';
+import {Action, ThunkAction} from '@reduxjs/toolkit';
+import {momentCategoriesFetched} from '../reducers';
+import {getTokenOrLogout} from '../../utils';
+import {MomentCategoryType} from '../../types';
+
+/**
+ * Load all categories for user
+ * @param userId id of the user for whom categories should be loaded
+ */
+export const loadUserMomentCategories = (
+ userId: string,
+): ThunkAction<Promise<void>, RootState, unknown, Action<string>> => async (
+ dispatch,
+) => {
+ try {
+ const token = await getTokenOrLogout(dispatch);
+ const categories = await loadMomentCategories(userId, token);
+ dispatch({
+ type: momentCategoriesFetched.type,
+ payload: {categories, add: true},
+ });
+ } catch (error) {
+ console.log(error);
+ }
+};
+
+/**
+ * Handle addition / deletion of categories for a user
+ * @param categories List of categories
+ * @param add boolean, if true, we add new categories, else we delete
+ * @param userId id of the user for whom categories should be updated
+ */
+export const updateMomentCategories = (
+ categories: Array<MomentCategoryType>,
+ add: boolean,
+ userId: string,
+): ThunkAction<Promise<void>, RootState, unknown, Action<string>> => async (
+ dispatch,
+) => {
+ try {
+ const token = await getTokenOrLogout(dispatch);
+ let success = false;
+ if (add) {
+ success = await postMomentCategories(categories, token);
+ } else {
+ success = await deleteMomentCategories(categories, userId, token);
+ }
+ if (success) {
+ dispatch({
+ type: momentCategoriesFetched.type,
+ payload: {categories, add},
+ });
+ }
+ } catch (error) {
+ console.log(error);
+ }
+};
diff --git a/src/store/actions/userX.ts b/src/store/actions/userX.ts
index 5468f762..87162eb1 100644
--- a/src/store/actions/userX.ts
+++ b/src/store/actions/userX.ts
@@ -1,6 +1,7 @@
+import {loadMomentCategories} from './../../services/MomentCategoryService';
import {userXInStore} from './../../utils/';
import {getTokenOrLogout, loadAllSocialsForUser} from './../../utils';
-import {UserType, ScreenType, ProfilePreviewType} from '../../types/types';
+import {UserType, ScreenType} from '../../types/types';
import {RootState} from '../rootReducer';
import {Action, ThunkAction} from '@reduxjs/toolkit';
import {
@@ -13,6 +14,7 @@ import {
userXProfileFetched,
userXSocialsFetched,
userXUserFetched,
+ userXMomentCategoriesFetched,
resetScreen,
} from '../reducers';
import {
@@ -80,6 +82,12 @@ export const loadUserX = (
payload: {screenType, userId, data},
}),
);
+ loadMomentCategories(userId, token).then((data) => {
+ dispatch({
+ type: userXMomentCategoriesFetched.type,
+ payload: {screenType, userId, data},
+ });
+ });
} catch (error) {
console.log(error);
}
diff --git a/src/store/initialStates.ts b/src/store/initialStates.ts
index 817af86b..8f4a2e84 100644
--- a/src/store/initialStates.ts
+++ b/src/store/initialStates.ts
@@ -1,4 +1,4 @@
-import {MomentType} from 'src/types';
+import {MomentCategoryType, MomentType} from '../types';
import {
ProfileType,
SocialAccountType,
@@ -62,6 +62,24 @@ export const NO_BLOCKED_USERS = {
blockedUsers: EMPTY_PROFILE_PREVIEW_LIST,
};
+export const MOMENT_CATEGORIES_MAP: Record<MomentCategoryType, boolean> = {
+ Friends: false,
+ Adventure: false,
+ 'Photo Dump': false,
+ Food: false,
+ Music: false,
+ Art: false,
+ Sports: false,
+ Fashion: false,
+ Travel: false,
+ Pets: false,
+ Nightlife: false,
+ DIY: false,
+ Nature: false,
+ 'Early Life': false,
+ Beauty: false,
+};
+
/**
* The dummy userId and username serve the purpose of preventing app crash
* For instance, if it may happen that data in our store is not loaded yet for the userXId being visited.
@@ -74,6 +92,7 @@ export const EMPTY_USER_X = <UserXType>{
followers: EMPTY_PROFILE_PREVIEW_LIST,
following: EMPTY_PROFILE_PREVIEW_LIST,
moments: EMPTY_MOMENTS_LIST,
+ momentCategories: MOMENT_CATEGORIES_MAP,
socialAccounts: NO_SOCIAL_ACCOUNTS,
user: NO_USER,
profile: NO_PROFILE,
@@ -95,3 +114,7 @@ export const EMPTY_SCREEN_TO_USERS_LIST: Record<
[ScreenType.Profile]: EMPTY_USERX_LIST,
[ScreenType.Search]: EMPTY_USERX_LIST,
};
+
+export const INITIAL_CATEGORIES_STATE = {
+ momentCategories: MOMENT_CATEGORIES_MAP,
+};
diff --git a/src/store/reducers/index.ts b/src/store/reducers/index.ts
index 0e378bc5..e09b41ee 100644
--- a/src/store/reducers/index.ts
+++ b/src/store/reducers/index.ts
@@ -5,3 +5,4 @@ export * from './userSocialsReducer';
export * from './taggUsersReducer';
export * from './userBlockReducer';
export * from './userXReducer';
+export * from './momentCategoryReducer';
diff --git a/src/store/reducers/momentCategoryReducer.tsx b/src/store/reducers/momentCategoryReducer.tsx
new file mode 100644
index 00000000..d1f448f9
--- /dev/null
+++ b/src/store/reducers/momentCategoryReducer.tsx
@@ -0,0 +1,22 @@
+import {createSlice} from '@reduxjs/toolkit';
+import {INITIAL_CATEGORIES_STATE} from '../initialStates';
+import {MomentCategoryType} from '../../types';
+
+const momentCategoriesSlice = createSlice({
+ name: 'momentCategories',
+ initialState: INITIAL_CATEGORIES_STATE,
+ reducers: {
+ /**
+ * One stop to add / delete / update categories for a user
+ */
+ momentCategoriesFetched: (state, action) => {
+ const categories: Array<MomentCategoryType> = action.payload.categories;
+ for (let category of categories) {
+ state.momentCategories[category] = action.payload.add;
+ }
+ },
+ },
+});
+
+export const {momentCategoriesFetched} = momentCategoriesSlice.actions;
+export const momentCategoriesReducer = momentCategoriesSlice.reducer;
diff --git a/src/store/reducers/userXReducer.ts b/src/store/reducers/userXReducer.ts
index 154dd7dc..bb142864 100644
--- a/src/store/reducers/userXReducer.ts
+++ b/src/store/reducers/userXReducer.ts
@@ -1,4 +1,4 @@
-import {ScreenType} from '../../types/types';
+import {MomentCategoryType, ScreenType} from '../../types/types';
import {EMPTY_SCREEN_TO_USERS_LIST, EMPTY_USER_X} from '../initialStates';
import {createSlice} from '@reduxjs/toolkit';
@@ -23,31 +23,45 @@ const userXSlice = createSlice({
action.payload.user;
},
+ userXMomentCategoriesFetched: (state, action) => {
+ const categories: Array<MomentCategoryType> = action.payload.data;
+ for (let category of categories) {
+ state[<ScreenType>action.payload.screenType][
+ action.payload.userId
+ ].momentCategories[category] = true;
+ }
+ },
+
userXMomentsFetched: (state, action) => {
state[<ScreenType>action.payload.screenType][
action.payload.userId
].moments = action.payload.data;
},
+
userXFollowersFetched: (state, action) => {
state[<ScreenType>action.payload.screenType][
action.payload.userId
].followers = action.payload.data;
},
+
userXFollowingFetched: (state, action) => {
state[<ScreenType>action.payload.screenType][
action.payload.userId
].following = action.payload.data;
},
+
userXAvatarFetched: (state, action) => {
state[<ScreenType>action.payload.screenType][
action.payload.userId
].avatar = action.payload.data;
},
+
userXCoverFetched: (state, action) => {
state[<ScreenType>action.payload.screenType][
action.payload.userId
].cover = action.payload.data;
},
+
userXSocialsFetched: (state, action) => {
state[<ScreenType>action.payload.screenType][
action.payload.userId
@@ -72,6 +86,7 @@ export const {
userXMomentsFetched,
userXProfileFetched,
userXSocialsFetched,
+ userXMomentCategoriesFetched,
resetScreen,
} = userXSlice.actions;
export const userXReducer = userXSlice.reducer;
diff --git a/src/store/rootReducer.ts b/src/store/rootReducer.ts
index 695ed8c7..8f002de0 100644
--- a/src/store/rootReducer.ts
+++ b/src/store/rootReducer.ts
@@ -7,6 +7,7 @@ import {
taggUsersReducer,
userBlockReducer,
userXReducer,
+ momentCategoriesReducer,
} from './reducers';
/**
@@ -20,6 +21,7 @@ const rootReducer = combineReducers({
socialAccounts: userSocialsReducer,
taggUsers: taggUsersReducer,
blocked: userBlockReducer,
+ momentCategories: momentCategoriesReducer,
userX: userXReducer,
});
diff --git a/src/types/types.ts b/src/types/types.ts
index e25d1ca7..25160d34 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -112,6 +112,7 @@ export interface UserXType {
following: ProfilePreviewType[];
moments: MomentType[];
socialAccounts: Record<string, SocialAccountType>;
+ momentCategories: Record<MomentCategoryType, boolean>;
user: UserType;
profile: ProfileType;
avatar: string;
@@ -125,3 +126,48 @@ export enum VerificationScreenType {
Phone,
Password,
}
+
+/**
+ * Default moment categories
+ */
+export type MomentCategoryType =
+ | 'Friends'
+ | 'Adventure'
+ | 'Photo Dump'
+ | 'Food'
+ | 'Music'
+ | 'Art'
+ | 'Sports'
+ | 'Fashion'
+ | 'Travel'
+ | 'Pets'
+ | 'Nightlife'
+ | 'DIY'
+ | 'Nature'
+ | 'Early Life'
+ | 'Beauty';
+
+/**
+ * Two types for category selection screen
+ */
+export enum CategorySelectionScreenType {
+ Onboarding,
+ Profile,
+}
+
+/**
+ * Gradient type to accomodate new g background gradients for Tagg
+ */
+export enum BackgroundGradientType {
+ Light,
+ Dark,
+}
+
+/**
+ * Linked List style type to accomodate for reusable TaggPopup for displaying popups or running a tutorial
+ */
+export type TaggPopupType = {
+ messageHeader: string;
+ messageBody: string;
+ next?: TaggPopupType;
+};
diff --git a/src/utils/users.ts b/src/utils/users.ts
index 0ed490c7..4f93347d 100644
--- a/src/utils/users.ts
+++ b/src/utils/users.ts
@@ -1,3 +1,4 @@
+import {loadUserMomentCategories} from './../store/actions/momentCategories';
import {loadUserX} from './../store/actions/userX';
import {RootState} from './../store/rootReducer';
import AsyncStorage from '@react-native-community/async-storage';
@@ -20,6 +21,7 @@ const loadData = async (dispatch: AppDispatch, user: UserType) => {
await Promise.all([
dispatch(loadUserData(user)),
dispatch(loadFollowData(user.userId)),
+ dispatch(loadUserMomentCategories(user.userId)),
dispatch(loadUserMoments(user.userId)),
dispatch(loadAllSocials(user.userId)),
dispatch(loadBlockedList(user.userId)),