From a954d6b6b88485dddc0ccfda634ffd102cb34ccd Mon Sep 17 00:00:00 2001 From: Ashm Walia <40498934+ashmgarv@users.noreply.github.com> Date: Tue, 22 Dec 2020 08:50:27 -0800 Subject: [TMA 446] Create category (#144) * Added welcome page * Working code * Small fix * Some more cleanup * Fixes * Cleanup * Fix again * Use gradient for white bg as well * Fixed type --- src/screens/onboarding/CategorySelection.tsx | 241 +++++++++++++++++++++ src/screens/onboarding/Checkpoint.tsx | 5 +- .../onboarding/InvitationCodeVerification.tsx | 10 +- src/screens/onboarding/Login.tsx | 18 +- src/screens/onboarding/PasswordReset.tsx | 5 +- src/screens/onboarding/PasswordResetRequest.tsx | 6 +- src/screens/onboarding/ProfileOnboarding.tsx | 3 +- src/screens/onboarding/RegistrationOne.tsx | 6 +- src/screens/onboarding/RegistrationThree.tsx | 5 +- src/screens/onboarding/RegistrationTwo.tsx | 5 +- src/screens/onboarding/SocialMedia.tsx | 42 ++-- src/screens/onboarding/Verification.tsx | 7 +- src/screens/onboarding/WelcomeScreen.tsx | 94 ++++++++ src/screens/onboarding/index.ts | 2 + src/screens/profile/EditProfile.tsx | 4 +- 15 files changed, 417 insertions(+), 36 deletions(-) create mode 100644 src/screens/onboarding/CategorySelection.tsx create mode 100644 src/screens/onboarding/WelcomeScreen.tsx (limited to 'src/screens') 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 = ({ + 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 + >([]); + + 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 ( + + + + Create new categories + + + {MOMENT_CATEGORIES.map((category, index) => ( + + ))} + + + + {isOnBoarding ? 'Login' : 'Create'} + + + + + + ); +}; + +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; type CheckpointNavigationProp = StackNavigationProp< @@ -44,7 +45,9 @@ const Checkpoint: React.FC = ({route, navigation}) => { }; return ( - + 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 = ({ navigation.navigate('Login')} + onPress={() => navigation.navigate('WelcomeScreen')} /> ); return ( - + Enter the code 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; type VerificationScreenNavigationProp = StackNavigationProp< @@ -194,8 +199,8 @@ const Login: React.FC = ({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 = ({navigation}: LoginProps) => { accessible={true} accessibilityLabel="Get started" style={styles.getStarted} - onPress={goToRegistration}> + onPress={startRegistrationProcess}> Get started! @@ -252,7 +257,10 @@ const Login: React.FC = ({navigation}: LoginProps) => { ); return ( - + = ({ ); return ( - + = ({ ); return ( - + = ({ }; return ( - + 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 = ({navigation}) => { ); return ( - + = ({ ); return ( - + = ({ ); return ( - + ; +type SocialMediaNavigationProps = StackNavigationProp< + OnboardingStackParams, + 'SocialMedia' +>; + interface SocialMediaProps { route: SocialMediaRouteProps; + navigation: SocialMediaNavigationProps; } -const SocialMedia: React.FC = ({route}) => { +const SocialMedia: React.FC = ({route, navigation}) => { const {userId, username} = route.params; const linkers: Array = []; @@ -56,17 +67,18 @@ const SocialMedia: React.FC = ({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 ( - + = ({route}) => { ))} - - Login + + Next ); @@ -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 = ({route, navigation}) => { ); return ( - + {isPhoneVerification ? ( ) : ( 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 = ({navigation}) => { + const handleNext = () => { + navigation.navigate('InvitationCodeVerification'); + }; + return ( + + + + + Welcome to TAGG! + + 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! + + + + Next + + + ); +}; + +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 = ({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 = ({route, navigation}) => { }, [navigation, handleSubmit]); return ( - + -- cgit v1.2.3-70-g09d2