From b3241dcb2e39350ae5192b9ddd2b865c1c105062 Mon Sep 17 00:00:00 2001 From: George Rusu Date: Wed, 5 May 2021 17:54:14 -0700 Subject: consolidated old onboarding screens into one screen and route --- src/routes/onboarding/OnboardingStackNavigator.tsx | 3 +- src/routes/onboarding/OnboardingStackScreen.tsx | 2 + src/screens/onboarding/OnboardingStepOne.tsx | 127 +++++---- src/screens/onboarding/OnboardingStepTwo.tsx | 38 +-- src/screens/onboarding/PhoneVerification.tsx | 18 +- src/screens/onboarding/RevampedOnboarding.tsx | 298 +++++++++++++++++++++ src/screens/onboarding/WelcomeScreen.tsx | 2 +- src/screens/onboarding/index.ts | 2 + 8 files changed, 397 insertions(+), 93 deletions(-) create mode 100644 src/screens/onboarding/RevampedOnboarding.tsx (limited to 'src') diff --git a/src/routes/onboarding/OnboardingStackNavigator.tsx b/src/routes/onboarding/OnboardingStackNavigator.tsx index 4726a47d..761efc91 100644 --- a/src/routes/onboarding/OnboardingStackNavigator.tsx +++ b/src/routes/onboarding/OnboardingStackNavigator.tsx @@ -1,11 +1,12 @@ import {createStackNavigator} from '@react-navigation/stack'; export type OnboardingStackParams = { + RevampedOnboarding: {isPhoneVerified: boolean}; InvitationCodeVerification: {userId: string; username: string}; Login: undefined; OnboardingStepOne: undefined; OnboardingStepThree: {userId: string; username: string}; - OnboardingStepTwo: {firstName: string; lastName: string; phone: string}; + OnboardingStepTwo: {firstName: string}; PasswordReset: {value: string}; PasswordResetRequest: undefined; PasswordVerification: {id: string}; diff --git a/src/routes/onboarding/OnboardingStackScreen.tsx b/src/routes/onboarding/OnboardingStackScreen.tsx index 339c3d0d..458697ad 100644 --- a/src/routes/onboarding/OnboardingStackScreen.tsx +++ b/src/routes/onboarding/OnboardingStackScreen.tsx @@ -10,6 +10,7 @@ import { PasswordVerification, PhoneVerification, WelcomeScreen, + RevampedOnboarding } from '../../screens'; import OnboardingStepOne from '../../screens/onboarding/OnboardingStepOne'; import {modalStyle} from '../main'; @@ -43,6 +44,7 @@ const Onboarding: React.FC = () => { }} /> + = ({navigation}) => { }); } try { - const {isValidFname, isValidLname, isValidPhone} = form; - if (isValidFname && isValidLname && isValidPhone) { - const {phone} = form; - const code = await sendOtpStatusCode(phone); - if (code) { - switch (code) { - case 200: - const {fname, lname} = form; - navigation.navigate('PhoneVerification', { - firstName: fname, - lastName: lname, - phone, - }); - break; - case 409: - Alert.alert(ERROR_PHONE_IN_USE); - break; - default: - Alert.alert(ERROR_TWILIO_SERVER_ERROR); - } - } - } else { + const {isValidFname} = form; + // const {isValidFname, isValidLname, isValidPhone} = form; + if (isValidFname) { + const {fname} = form; + navigation.navigate('OnboardingStepTwo', { + firstName: fname + }); + // const {phone} = form; + // const code = await sendOtpStatusCode(phone); + // if (code) { + // switch (code) { + // case 200: + // const {fname, lname} = form; + + // break; + // case 409: + // Alert.alert(ERROR_PHONE_IN_USE); + // break; + // default: + // Alert.alert(ERROR_TWILIO_SERVER_ERROR); + } else { setForm({...form, attemptedSubmit: false}); setTimeout(() => setForm({...form, attemptedSubmit: true})); } @@ -144,29 +145,36 @@ const OnboardingStepOne: React.FC = ({navigation}) => { const footer = useMemo( () => ( - - navigation.navigate('Login')} - /> - - - - + + // // + // {/* navigation.navigate('Login')} + // /> */} + // {/* */} + // {/* */} + // {/* */} + // // ), [ form.fname, - form.lname, - form.phone, + // form.lname, + // form.phone, form.isValidFname, - form.isValidLname, - form.isValidPhone, + // form.isValidLname, + // form.isValidPhone, ], ); @@ -175,13 +183,14 @@ const OnboardingStepOne: React.FC = ({navigation}) => { style={styles.container} gradientType={BackgroundGradientType.Light}> - + {/* getting rid of registration progress in onboarding*/} + {/* */} - - ENTER NAME - + {/* */} + SIGN UP + {/* */} = ({navigation}) => { attemptedSubmit={form.attemptedSubmit} width={280} /> - = ({navigation}) => { invalidWarning="Please enter a valid last name." attemptedSubmit={form.attemptedSubmit} width={280} - /> - */} + {/* = ({navigation}) => { attemptedSubmit={form.attemptedSubmit} width={280} onSubmitEditing={goNext} - /> + /> */} + + {footer} + - {footer} ); }; const styles = StyleSheet.create({ container: { - flex: 1, + height: SCREEN_HEIGHT, + width: SCREEN_WIDTH, alignItems: 'center', justifyContent: 'center', }, - wizard: { - position: 'absolute', - top: SCREEN_HEIGHT * 0.1, - }, + button: { + width: 40, + aspectRatio: 10, + }, formHeader: { color: '#fff', fontSize: 30, fontWeight: '600', marginBottom: '16%', + position: 'absolute', top: 0, marginTop: '45%', }, load: { top: '5%', diff --git a/src/screens/onboarding/OnboardingStepTwo.tsx b/src/screens/onboarding/OnboardingStepTwo.tsx index fc89b887..5c474c14 100644 --- a/src/screens/onboarding/OnboardingStepTwo.tsx +++ b/src/screens/onboarding/OnboardingStepTwo.tsx @@ -16,8 +16,9 @@ import { ArrowButton, Background, LoadingIndicator, - RegistrationWizard, + // RegistrationWizard, TaggInput, + TaggSquareButton, TermsConditions, } from '../../components'; import {emailRegex, passwordRegex, usernameRegex} from '../../constants'; @@ -211,25 +212,14 @@ const OnboardingStepTwo: React.FC = ({ const footer = useMemo( () => ( - - navigation.navigate('PhoneVerification', {...route.params}) - } - /> - + ), @@ -251,7 +241,7 @@ const OnboardingStepTwo: React.FC = ({ style={styles.container} gradientType={BackgroundGradientType.Light}> - + {/* */} @@ -347,10 +337,10 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, - wizard: { - position: 'absolute', - top: SCREEN_HEIGHT * 0.1, - }, + // wizard: { + // position: 'absolute', + // top: SCREEN_HEIGHT * 0.1, + // }, formHeader: { color: '#fff', fontSize: 30, diff --git a/src/screens/onboarding/PhoneVerification.tsx b/src/screens/onboarding/PhoneVerification.tsx index 6ec511b3..a242b44b 100644 --- a/src/screens/onboarding/PhoneVerification.tsx +++ b/src/screens/onboarding/PhoneVerification.tsx @@ -21,7 +21,7 @@ import { ArrowButton, Background, LoadingIndicator, - RegistrationWizard, + // RegistrationWizard, SubmitButton, } from '../../components'; import {codeRegex} from '../../constants'; @@ -67,9 +67,7 @@ const PhoneVerification: React.FC = ({ try { const success = await trackPromise(verifyOtp(phone, value)); if (success) { - navigation.navigate('OnboardingStepTwo', { - ...route.params, - }); + navigation.navigate('RevampedOnboarding', {isPhoneVerified: true}); } } catch (error) { console.log(error); @@ -82,7 +80,7 @@ const PhoneVerification: React.FC = ({ navigation.navigate('OnboardingStepOne')} + onPress={() => navigation.navigate('RevampedOnboarding', {isPhoneVerified: false})} /> ), @@ -94,7 +92,7 @@ const PhoneVerification: React.FC = ({ centered style={styles.container} gradientType={BackgroundGradientType.Light}> - + {/* */} Enter 6 digit code @@ -144,10 +142,10 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, - wizard: { - position: 'absolute', - top: SCREEN_HEIGHT * 0.1, - }, + // wizard: { + // position: 'absolute', + // top: SCREEN_HEIGHT * 0.1, + // }, form: { top: '20%', alignItems: 'center', diff --git a/src/screens/onboarding/RevampedOnboarding.tsx b/src/screens/onboarding/RevampedOnboarding.tsx new file mode 100644 index 00000000..bae2777c --- /dev/null +++ b/src/screens/onboarding/RevampedOnboarding.tsx @@ -0,0 +1,298 @@ +import { useNavigation } from '@react-navigation/core'; +import { RouteProp } from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; +import { current } from 'immer'; +import React, {useEffect, useMemo, useRef, useState} from 'react'; +import { + Alert, + KeyboardAvoidingView, + Platform, + StatusBar, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import { + ArrowButton, + Background, + BasicButton, + // RegistrationWizard, + TaggInput, + TaggSquareButton, +} from '../../components'; +import {emailRegex, nameRegex, phoneRegex, usernameRegex} from '../../constants'; +import { + ERROR_NEXT_PAGE, + ERROR_PHONE_IN_USE, + ERROR_TWILIO_SERVER_ERROR, +} from '../../constants/strings'; +import {OnboardingStackParams} from '../../routes'; +import {sendOtpStatusCode} from '../../services'; +import {BackgroundGradientType} from '../../types'; +import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; + +type RevampedOnboardingRouteProp = RouteProp; +type RevampedOnboardingNavigationProp = StackNavigationProp< + OnboardingStackParams, + 'RevampedOnboarding' +>; +interface RevampedOnboardingProps { + route: RevampedOnboardingRouteProp; + navigation: RevampedOnboardingNavigationProp; +} + +const RevampedOnboarding: React.FC = ({route}) => { + const {isPhoneVerified} = route.params; + const navigation = useNavigation(); + type renderStatusType = 'firstName' | 'lastName' | 'username' | 'email'; + const [renderStatus, setStatus] = useState('firstName'); + const [currentStep, setCurrentStep] = useState(0); + const [form, setForm] = useState({ + fname: '', + lname: '', + phone: '', + email: '', + isValidFname: false, + isValidLname: false, + isValidPhone: false, + isValidEmail: false, + attemptedSubmit: false, + token: '', + }); + + + + const goNext = async () => { + if (!form.attemptedSubmit) { + setForm({ + ...form, + attemptedSubmit: true, + }); + } + try { + const {isValidPhone} = form; + // const {isValidFname, isValidLname, isValidPhone} = form; + if (isValidPhone) { + const {phone} = form; + const code = await sendOtpStatusCode(phone); + if (code) { + switch (code) { + case 200: + const {fname, lname} = form; + navigation.navigate('PhoneVerification', { + firstName: fname, + lastName: lname, + phone, + }); + break; + case 409: + Alert.alert(ERROR_PHONE_IN_USE); + break; + default: + Alert.alert(ERROR_TWILIO_SERVER_ERROR); + } + } else { + setForm({...form, attemptedSubmit: false}); + setTimeout(() => setForm({...form, attemptedSubmit: true})); + } + } + } catch (error) { + Alert.alert(ERROR_NEXT_PAGE); + return { + name: 'Navigation error', + description: error, + }; + } + }; + + // 0 = first name, 1 = last name, 2 = username, 3 = phone # + const handleNameUpdate= (name: string, nameType: number) => { + name = name.trim(); + let isValidName: boolean = nameRegex.test(name); + switch(nameType) { + case 0: + setForm({ + ...form, + fname: name, + isValidFname: isValidName, + }); + case 1: + setForm({ + ...form, + lname: name, + isValidLname: isValidName, + }); + case 2: + let isvalidUserName = usernameRegex.test(name); + setForm({ + ...form, + fname: name, + isValidFname: isValidName, + }); + // case 3: + // let isValidPhone: boolean = phoneRegex.test(name); + // setForm({ + // ...form, + // phone: name, + // isValidPhone, + // }); + } + + }; + const handlePhoneUpdate = (phone: string) => { + phone = phone.trim(); + let isValidPhone: boolean = phoneRegex.test(phone); + setForm({ + ...form, + phone, + isValidPhone, + }); + }; + const handleEmailUpdate = (email: string) => { + email = email.trim(); + let isValidEmail: boolean = emailRegex.test(email); + setForm({ + ...form, + email, + isValidEmail, + }); + }; + + const formSteps: { + placeholder: string, + valid: boolean, + onChangeText: (text: string, nameType: number) => void, + }[] = [ + { + placeholder: 'First Name', + valid: form.isValidFname, + onChangeText: handleNameUpdate, + }, + { + placeholder: 'Last Name', + valid: form.isValidLname, + onChangeText: handleNameUpdate, + }, + { + placeholder: 'Phone', + valid: form.isValidPhone, + onChangeText: handlePhoneUpdate, + }, + { + placeholder: 'Email', + valid: form.isValidEmail, + onChangeText: handleEmailUpdate, + }, + { + placeholder: 'Username', + valid: form.isValidLname, + onChangeText: handleNameUpdate, + }, + // ... + ] + useEffect( + () => { + console.log("before: " + currentStep + " " + step.placeholder) + if(isPhoneVerified) { + setCurrentStep(currentStep + 1) + } + console.log("afgter: " + currentStep + " " + step.placeholder) + }, [isPhoneVerified]) + const step = formSteps[currentStep] + return ( + + + {/* getting rid of registration progress in onboarding*/} + {/* */} + + {/* */} + {(step.placeholder === "Phone" && !isPhoneVerified) ? ( + + ) : ( + <> + SIGN UP + + setCurrentStep(currentStep + 1)} + blurOnSubmit={false} + valid={step.valid} + invalidWarning={`Please enter a valid ${step.placeholder.toLowerCase()}`} + attemptedSubmit={form.attemptedSubmit} + width={280} + /> + {/* {footer('username')} */} + + )} + + + + + )}; +// in return listen for button click, if fname valid, then change state to return the next component in the list +// conditionally display subsequent components. + +const styles = StyleSheet.create({ + container: { + height: SCREEN_HEIGHT, + width: SCREEN_WIDTH, + alignItems: 'center', + justifyContent: 'center', + }, + button: { + width: 40, + aspectRatio: 10, + }, + formHeader: { + color: '#fff', + fontSize: 30, + fontWeight: '600', + marginBottom: '16%', + position: 'absolute', top: 0, marginTop: '45%', + }, + load: { + top: '5%', + }, + footer: { + width: '100%', + flexDirection: 'row', + justifyContent: 'space-around', + ...Platform.select({ + ios: { + bottom: '20%', + }, + android: { + bottom: '10%', + }, + }), + }, + }); +export default RevampedOnboarding; diff --git a/src/screens/onboarding/WelcomeScreen.tsx b/src/screens/onboarding/WelcomeScreen.tsx index c36a6e05..358661f7 100644 --- a/src/screens/onboarding/WelcomeScreen.tsx +++ b/src/screens/onboarding/WelcomeScreen.tsx @@ -35,7 +35,7 @@ const WelcomeScreen: React.FC = ({navigation}) => { { - navigation.navigate('OnboardingStepOne'); + navigation.navigate('RevampedOnboarding', {isPhoneVerified: false}); }} title={'Next'} buttonStyle={'large'} diff --git a/src/screens/onboarding/index.ts b/src/screens/onboarding/index.ts index 7eb0587f..c0d99dd2 100644 --- a/src/screens/onboarding/index.ts +++ b/src/screens/onboarding/index.ts @@ -8,3 +8,5 @@ export {default as PasswordResetRequest} from './PasswordResetRequest'; export {default as PhoneVerification} from './PhoneVerification'; export {default as PasswordVerification} from './PasswordVerification'; export {default as WelcomeScreen} from './WelcomeScreen'; +export {default as RevampedOnboarding} from './RevampedOnboarding'; + -- cgit v1.2.3-70-g09d2 From 830db377e4cc99299c1e9f7d7e5095c05cff92b7 Mon Sep 17 00:00:00 2001 From: George Rusu Date: Thu, 6 May 2021 13:08:34 -0700 Subject: debugging with Ivan --- src/screens/onboarding/RevampedOnboarding.tsx | 131 +++++++++++++++++++------- 1 file changed, 98 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/screens/onboarding/RevampedOnboarding.tsx b/src/screens/onboarding/RevampedOnboarding.tsx index bae2777c..e7ad9e41 100644 --- a/src/screens/onboarding/RevampedOnboarding.tsx +++ b/src/screens/onboarding/RevampedOnboarding.tsx @@ -1,6 +1,7 @@ import { useNavigation } from '@react-navigation/core'; import { RouteProp } from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; +import { Input } from 'react-native-elements'; import { current } from 'immer'; import React, {useEffect, useMemo, useRef, useState} from 'react'; import { @@ -21,7 +22,7 @@ import { TaggInput, TaggSquareButton, } from '../../components'; -import {emailRegex, nameRegex, phoneRegex, usernameRegex} from '../../constants'; +import {emailRegex, nameRegex, passwordRegex, phoneRegex, usernameRegex} from '../../constants'; import { ERROR_NEXT_PAGE, ERROR_PHONE_IN_USE, @@ -31,6 +32,7 @@ import {OnboardingStackParams} from '../../routes'; import {sendOtpStatusCode} from '../../services'; import {BackgroundGradientType} from '../../types'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +import { createIconSetFromFontello } from 'react-native-vector-icons'; type RevampedOnboardingRouteProp = RouteProp; type RevampedOnboardingNavigationProp = StackNavigationProp< @@ -51,12 +53,18 @@ const RevampedOnboarding: React.FC = ({route}) => { const [form, setForm] = useState({ fname: '', lname: '', + username: '', phone: '', email: '', + password: '', + confirm: '', isValidFname: false, isValidLname: false, isValidPhone: false, + isValidUser: false, isValidEmail: false, + isValidPassword: false, + passwordsMatch: false, attemptedSubmit: false, token: '', }); @@ -112,11 +120,13 @@ const RevampedOnboarding: React.FC = ({route}) => { let isValidName: boolean = nameRegex.test(name); switch(nameType) { case 0: + console.log("First name") setForm({ ...form, fname: name, isValidFname: isValidName, }); + console.log(isValidName) case 1: setForm({ ...form, @@ -127,8 +137,8 @@ const RevampedOnboarding: React.FC = ({route}) => { let isvalidUserName = usernameRegex.test(name); setForm({ ...form, - fname: name, - isValidFname: isValidName, + username: name, + isValidUser: isValidName, }); // case 3: // let isValidPhone: boolean = phoneRegex.test(name); @@ -158,36 +168,51 @@ const RevampedOnboarding: React.FC = ({route}) => { isValidEmail, }); }; + const handlePasswordUpdate = (password: string) => { + let isValidPassword: boolean = passwordRegex.test(password); + let passwordsMatch: boolean = form.password === form.confirm; + setForm({ + ...form, + password, + isValidPassword, + passwordsMatch, + }); + }; const formSteps: { placeholder: string, - valid: boolean, + valid: () => boolean, onChangeText: (text: string, nameType: number) => void, }[] = [ { placeholder: 'First Name', - valid: form.isValidFname, - onChangeText: handleNameUpdate, + valid: () => form.isValidFname, + onChangeText: (text, _) => handleNameUpdate(text, 0), }, { placeholder: 'Last Name', - valid: form.isValidLname, - onChangeText: handleNameUpdate, + valid: () => form.isValidLname, + onChangeText: (text, _) => handleNameUpdate(text, 1), }, { placeholder: 'Phone', - valid: form.isValidPhone, + valid: () => form.isValidPhone, onChangeText: handlePhoneUpdate, }, { - placeholder: 'Email', - valid: form.isValidEmail, + placeholder: 'School Email', + valid: () => form.isValidEmail, onChangeText: handleEmailUpdate, }, { placeholder: 'Username', - valid: form.isValidLname, - onChangeText: handleNameUpdate, + valid: () => form.isValidLname, + onChangeText: (text, _) => handleNameUpdate(text, 1), + }, + { + placeholder: 'Password', + valid: () => form.isValidLname, + onChangeText: handlePasswordUpdate, }, // ... ] @@ -205,51 +230,80 @@ const RevampedOnboarding: React.FC = ({route}) => { style={styles.container} gradientType={BackgroundGradientType.Light}> + {/* getting rid of registration progress in onboarding*/} {/* */} + + {(currentStep != 0) && + setCurrentStep(currentStep - 1)} + /> + } + {/* */} {(step.placeholder === "Phone" && !isPhoneVerified) ? ( + <> + maxLength={10} // currently only support US phone numbers + accessibilityHint="Enter your phone number." + accessibilityLabel="Phone number input field." + placeholder="Phone Number" + autoCompleteType="tel" + textContentType="telephoneNumber" + autoCapitalize="none" + keyboardType="number-pad" + onChangeText={handlePhoneUpdate} + autoFocus={true} + blurOnSubmit={false} + valid={form.isValidPhone} + invalidWarning={'Please enter a valid 10 digit number.'} + attemptedSubmit={form.attemptedSubmit} + width={280} + onSubmitEditing={goNext} + /> + + ) : ( <> SIGN UP setCurrentStep(currentStep + 1)} + onSubmitEditing={() => + { console.log("step bool:" + step.placeholder + " " + form.isValidFname) + if (step.valid()) {setCurrentStep(currentStep + 1)}}} + autoFocus= {true} blurOnSubmit={false} - valid={step.valid} + valid={step.valid()} invalidWarning={`Please enter a valid ${step.placeholder.toLowerCase()}`} attemptedSubmit={form.attemptedSubmit} width={280} /> - {/* {footer('username')} */} + {if (step.valid()) {setCurrentStep(currentStep + 1)}}} + autoFocus= {true} + title={'Next'} + buttonStyle={'normal'} + buttonColor={'white'} + labelColor={'blue'} + /> )} @@ -267,6 +321,17 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, + input: { + width: '100%', + minWidth: '60%', + height: 40, + fontSize: 16, + fontWeight: '600', + color: '#fff', + paddingLeft: 13, + borderBottomWidth: 1, + borderBottomColor: '#fff' + }, button: { width: 40, aspectRatio: 10, -- cgit v1.2.3-70-g09d2 From 552ea67bedc3f07bbf0f48906109e780565dcf2d Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 6 May 2021 16:50:57 -0400 Subject: cleaned up code, basic step 1 working --- src/screens/onboarding/RevampedOnboarding.tsx | 595 +++++++++++++------------- 1 file changed, 306 insertions(+), 289 deletions(-) (limited to 'src') diff --git a/src/screens/onboarding/RevampedOnboarding.tsx b/src/screens/onboarding/RevampedOnboarding.tsx index e7ad9e41..0d7f5c3c 100644 --- a/src/screens/onboarding/RevampedOnboarding.tsx +++ b/src/screens/onboarding/RevampedOnboarding.tsx @@ -1,9 +1,7 @@ -import { useNavigation } from '@react-navigation/core'; -import { RouteProp } from '@react-navigation/native'; +import {useNavigation} from '@react-navigation/core'; +import {RouteProp} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; -import { Input } from 'react-native-elements'; -import { current } from 'immer'; -import React, {useEffect, useMemo, useRef, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import { Alert, KeyboardAvoidingView, @@ -11,18 +9,21 @@ import { StatusBar, StyleSheet, Text, - TouchableOpacity, View, } from 'react-native'; import { ArrowButton, Background, - BasicButton, - // RegistrationWizard, TaggInput, TaggSquareButton, } from '../../components'; -import {emailRegex, nameRegex, passwordRegex, phoneRegex, usernameRegex} from '../../constants'; +import { + emailRegex, + nameRegex, + passwordRegex, + phoneRegex, + usernameRegex, +} from '../../constants'; import { ERROR_NEXT_PAGE, ERROR_PHONE_IN_USE, @@ -32,9 +33,11 @@ import {OnboardingStackParams} from '../../routes'; import {sendOtpStatusCode} from '../../services'; import {BackgroundGradientType} from '../../types'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; -import { createIconSetFromFontello } from 'react-native-vector-icons'; -type RevampedOnboardingRouteProp = RouteProp; +type RevampedOnboardingRouteProp = RouteProp< + OnboardingStackParams, + 'RevampedOnboarding' +>; type RevampedOnboardingNavigationProp = StackNavigationProp< OnboardingStackParams, 'RevampedOnboarding' @@ -45,188 +48,185 @@ interface RevampedOnboardingProps { } const RevampedOnboarding: React.FC = ({route}) => { - const {isPhoneVerified} = route.params; - const navigation = useNavigation(); - type renderStatusType = 'firstName' | 'lastName' | 'username' | 'email'; - const [renderStatus, setStatus] = useState('firstName'); - const [currentStep, setCurrentStep] = useState(0); - const [form, setForm] = useState({ - fname: '', - lname: '', - username: '', - phone: '', - email: '', - password: '', - confirm: '', - isValidFname: false, - isValidLname: false, - isValidPhone: false, - isValidUser: false, - isValidEmail: false, - isValidPassword: false, - passwordsMatch: false, - attemptedSubmit: false, - token: '', - }); - + const {isPhoneVerified} = route.params; + const navigation = useNavigation(); + type renderStatusType = 'firstName' | 'lastName' | 'username' | 'email'; + const [attemptedSubmit, setAttemptedSubmit] = useState(false); + // TODO: maybe use this? + const [valid, setValid] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [form, setForm] = useState({ + fname: '', + lname: '', + username: '', + phone: '', + email: '', + password: '', + confirm: '', + isValidFname: false, + isValidLname: false, + isValidPhone: false, + isValidUser: false, + isValidEmail: false, + isValidPassword: false, + passwordsMatch: false, + token: '', + }); + const goNext = async () => { + if (!attemptedSubmit) { + setAttemptedSubmit(true); + } + try { + const {isValidPhone} = form; + if (isValidPhone) { + const {phone} = form; + const code = await sendOtpStatusCode(phone); + if (code) { + switch (code) { + case 200: + const {fname, lname} = form; + navigation.navigate('PhoneVerification', { + firstName: fname, + lastName: lname, + phone, + }); + break; + case 409: + Alert.alert(ERROR_PHONE_IN_USE); + break; + default: + Alert.alert(ERROR_TWILIO_SERVER_ERROR); + } + } else { + setAttemptedSubmit(false); + setTimeout(() => { + setAttemptedSubmit(true); + }); + } + } + } catch (error) { + Alert.alert(ERROR_NEXT_PAGE); + return { + name: 'Navigation error', + description: error, + }; + } + }; - const goNext = async () => { - if (!form.attemptedSubmit) { + // 0 = first name, 1 = last name, 2 = username, 3 = phone # + const handleNameUpdate = (name: string, nameType: number) => { + name = name.trim(); + let isValidName: boolean = nameRegex.test(name); + switch (nameType) { + case 0: setForm({ ...form, - attemptedSubmit: true, + fname: name, + isValidFname: isValidName, }); - } - try { - const {isValidPhone} = form; - // const {isValidFname, isValidLname, isValidPhone} = form; - if (isValidPhone) { - const {phone} = form; - const code = await sendOtpStatusCode(phone); - if (code) { - switch (code) { - case 200: - const {fname, lname} = form; - navigation.navigate('PhoneVerification', { - firstName: fname, - lastName: lname, - phone, - }); - break; - case 409: - Alert.alert(ERROR_PHONE_IN_USE); - break; - default: - Alert.alert(ERROR_TWILIO_SERVER_ERROR); - } - } else { - setForm({...form, attemptedSubmit: false}); - setTimeout(() => setForm({...form, attemptedSubmit: true})); - } - } - } catch (error) { - Alert.alert(ERROR_NEXT_PAGE); - return { - name: 'Navigation error', - description: error, - }; - } - }; + break; + case 1: + setForm({ + ...form, + lname: name, + isValidLname: isValidName, + }); + break; + case 2: + setForm({ + ...form, + username: name, + isValidUser: usernameRegex.test(name), + }); + break; + case 3: + setForm({ + ...form, + phone: name, + isValidPhone: phoneRegex.test(name), + }); + break; + } + }; + const handlePhoneUpdate = (phone: string) => { + phone = phone.trim(); + let isValidPhone: boolean = phoneRegex.test(phone); + setForm({ + ...form, + phone, + isValidPhone, + }); + }; + const handleEmailUpdate = (email: string) => { + email = email.trim(); + let isValidEmail: boolean = emailRegex.test(email); + setForm({ + ...form, + email, + isValidEmail, + }); + }; + const handlePasswordUpdate = (password: string) => { + let isValidPassword: boolean = passwordRegex.test(password); + let passwordsMatch: boolean = form.password === form.confirm; + setForm({ + ...form, + password, + isValidPassword, + passwordsMatch, + }); + }; - // 0 = first name, 1 = last name, 2 = username, 3 = phone # - const handleNameUpdate= (name: string, nameType: number) => { - name = name.trim(); - let isValidName: boolean = nameRegex.test(name); - switch(nameType) { - case 0: - console.log("First name") - setForm({ - ...form, - fname: name, - isValidFname: isValidName, - }); - console.log(isValidName) - case 1: - setForm({ - ...form, - lname: name, - isValidLname: isValidName, - }); - case 2: - let isvalidUserName = usernameRegex.test(name); - setForm({ - ...form, - username: name, - isValidUser: isValidName, - }); - // case 3: - // let isValidPhone: boolean = phoneRegex.test(name); - // setForm({ - // ...form, - // phone: name, - // isValidPhone, - // }); - } - - }; - const handlePhoneUpdate = (phone: string) => { - phone = phone.trim(); - let isValidPhone: boolean = phoneRegex.test(phone); - setForm({ - ...form, - phone, - isValidPhone, - }); - }; - const handleEmailUpdate = (email: string) => { - email = email.trim(); - let isValidEmail: boolean = emailRegex.test(email); - setForm({ - ...form, - email, - isValidEmail, - }); - }; - const handlePasswordUpdate = (password: string) => { - let isValidPassword: boolean = passwordRegex.test(password); - let passwordsMatch: boolean = form.password === form.confirm; - setForm({ - ...form, - password, - isValidPassword, - passwordsMatch, - }); - }; + const formSteps: { + placeholder: string; + valid: () => boolean; + onChangeText: (text: string) => void; + }[] = [ + { + placeholder: 'First Name', + valid: () => form.isValidFname, + onChangeText: (text) => handleNameUpdate(text, 0), + }, + { + placeholder: 'Last Name', + valid: () => form.isValidLname, + onChangeText: (text) => handleNameUpdate(text, 1), + }, + { + placeholder: 'Phone', + valid: () => form.isValidPhone, + onChangeText: handlePhoneUpdate, + }, + { + placeholder: 'School Email', + valid: () => form.isValidEmail, + onChangeText: handleEmailUpdate, + }, + { + placeholder: 'Username', + valid: () => form.isValidLname, + onChangeText: (text) => handleNameUpdate(text, 2), + }, + { + placeholder: 'Password', + valid: () => form.isValidLname, + onChangeText: handlePasswordUpdate, + }, + // ... + ]; + const step = formSteps[currentStep]; - const formSteps: { - placeholder: string, - valid: () => boolean, - onChangeText: (text: string, nameType: number) => void, - }[] = [ - { - placeholder: 'First Name', - valid: () => form.isValidFname, - onChangeText: (text, _) => handleNameUpdate(text, 0), - }, - { - placeholder: 'Last Name', - valid: () => form.isValidLname, - onChangeText: (text, _) => handleNameUpdate(text, 1), - }, - { - placeholder: 'Phone', - valid: () => form.isValidPhone, - onChangeText: handlePhoneUpdate, - }, - { - placeholder: 'School Email', - valid: () => form.isValidEmail, - onChangeText: handleEmailUpdate, - }, - { - placeholder: 'Username', - valid: () => form.isValidLname, - onChangeText: (text, _) => handleNameUpdate(text, 1), - }, - { - placeholder: 'Password', - valid: () => form.isValidLname, - onChangeText: handlePasswordUpdate, - }, - // ... - ] - useEffect( - () => { - console.log("before: " + currentStep + " " + step.placeholder) - if(isPhoneVerified) { - setCurrentStep(currentStep + 1) - } - console.log("afgter: " + currentStep + " " + step.placeholder) - }, [isPhoneVerified]) - const step = formSteps[currentStep] - return ( - { + // console.log('before: ' + currentStep + ' ' + step.placeholder); + if (isPhoneVerified) { + setCurrentStep(currentStep + 1); + } + // console.log('afgter: ' + currentStep + ' ' + step.placeholder); + }, [isPhoneVerified]); + + return ( + @@ -236,18 +236,27 @@ const RevampedOnboarding: React.FC = ({route}) => { - - {(currentStep != 0) && + + {currentStep !== 0 && ( + { + console.log('fooo'); + setCurrentStep(currentStep - 1); + }} + /> + )} + setCurrentStep(currentStep - 1)} + onPress={() => { + console.log('fooo'); + setCurrentStep(currentStep - 1); + }} /> - } - - {/* */} - {(step.placeholder === "Phone" && !isPhoneVerified) ? ( - <> - + = ({route}) => { blurOnSubmit={false} valid={form.isValidPhone} invalidWarning={'Please enter a valid 10 digit number.'} - attemptedSubmit={form.attemptedSubmit} + attemptedSubmit={attemptedSubmit} width={280} - onSubmitEditing={goNext} - /> - - + onSubmitEditing={goNext} + /> + + ) : ( - <> - SIGN UP - - - { console.log("step bool:" + step.placeholder + " " + form.isValidFname) - if (step.valid()) {setCurrentStep(currentStep + 1)}}} - autoFocus= {true} - blurOnSubmit={false} - valid={step.valid()} - invalidWarning={`Please enter a valid ${step.placeholder.toLowerCase()}`} - attemptedSubmit={form.attemptedSubmit} - width={280} - /> - {if (step.valid()) {setCurrentStep(currentStep + 1)}}} - autoFocus= {true} - title={'Next'} - buttonStyle={'normal'} - buttonColor={'white'} - labelColor={'blue'} - /> - - )} - - + <> + SIGN UP + + { + setAttemptedSubmit(true); + if (step.valid()) { + setCurrentStep(currentStep + 1); + setAttemptedSubmit(false); + } + }} + autoFocus={true} + blurOnSubmit={false} + valid={step.valid()} + invalidWarning={`Please enter a valid ${step.placeholder.toLowerCase()}`} + attemptedSubmit={attemptedSubmit} + width={280} + /> + {/* { + if (step.valid()) { + setCurrentStep(currentStep + 1); + } + }} + autoFocus={true} + title={'Next'} + buttonStyle={'normal'} + buttonColor={'white'} + labelColor={'blue'} + /> */} + + + )} + - - )}; -// in return listen for button click, if fname valid, then change state to return the next component in the list -// conditionally display subsequent components. + + ); +}; const styles = StyleSheet.create({ - container: { - height: SCREEN_HEIGHT, - width: SCREEN_WIDTH, - alignItems: 'center', - justifyContent: 'center', - }, - input: { - width: '100%', - minWidth: '60%', - height: 40, - fontSize: 16, - fontWeight: '600', - color: '#fff', - paddingLeft: 13, - borderBottomWidth: 1, - borderBottomColor: '#fff' - }, - button: { - width: 40, - aspectRatio: 10, - }, - formHeader: { - color: '#fff', - fontSize: 30, - fontWeight: '600', - marginBottom: '16%', - position: 'absolute', top: 0, marginTop: '45%', - }, - load: { - top: '5%', - }, - footer: { - width: '100%', - flexDirection: 'row', - justifyContent: 'space-around', - ...Platform.select({ - ios: { - bottom: '20%', - }, - android: { - bottom: '10%', - }, - }), - }, - }); + container: { + height: SCREEN_HEIGHT, + width: SCREEN_WIDTH, + alignItems: 'center', + justifyContent: 'center', + }, + input: { + width: '100%', + minWidth: '60%', + height: 40, + fontSize: 16, + fontWeight: '600', + color: '#fff', + paddingLeft: 13, + borderBottomWidth: 1, + borderBottomColor: '#fff', + }, + button: { + width: 40, + aspectRatio: 10, + }, + formHeader: { + color: '#fff', + fontSize: 30, + fontWeight: '600', + position: 'absolute', + top: '20%', + }, + load: { + top: '5%', + }, + footer: { + width: '100%', + flexDirection: 'row', + justifyContent: 'space-around', + ...Platform.select({ + ios: { + bottom: '20%', + }, + android: { + bottom: '10%', + }, + }), + }, +}); export default RevampedOnboarding; -- cgit v1.2.3-70-g09d2 From 14bc026d21e4ee9692ddbea4343b2c57c6e3cab4 Mon Sep 17 00:00:00 2001 From: George Rusu Date: Thu, 6 May 2021 20:34:25 -0700 Subject: onboarding part 1 and 2 done, need to register and style --- src/components/onboarding/TermsConditions.tsx | 4 +- src/screens/onboarding/PhoneVerification.tsx | 6 - src/screens/onboarding/RevampedOnboarding.tsx | 320 ++++++++++++++++---------- 3 files changed, 201 insertions(+), 129 deletions(-) (limited to 'src') diff --git a/src/components/onboarding/TermsConditions.tsx b/src/components/onboarding/TermsConditions.tsx index 08cd8228..9bd0ee3b 100644 --- a/src/components/onboarding/TermsConditions.tsx +++ b/src/components/onboarding/TermsConditions.tsx @@ -49,11 +49,11 @@ const TermsConditions: React.FC = (props) => { - I accept the + Accept setModalVisible(true)}> - EULA & Terms of Service + Terms and Conditions diff --git a/src/screens/onboarding/PhoneVerification.tsx b/src/screens/onboarding/PhoneVerification.tsx index a242b44b..9b517d9d 100644 --- a/src/screens/onboarding/PhoneVerification.tsx +++ b/src/screens/onboarding/PhoneVerification.tsx @@ -21,7 +21,6 @@ import { ArrowButton, Background, LoadingIndicator, - // RegistrationWizard, SubmitButton, } from '../../components'; import {codeRegex} from '../../constants'; @@ -92,7 +91,6 @@ const PhoneVerification: React.FC = ({ centered style={styles.container} gradientType={BackgroundGradientType.Light}> - {/* */} Enter 6 digit code @@ -142,10 +140,6 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, - // wizard: { - // position: 'absolute', - // top: SCREEN_HEIGHT * 0.1, - // }, form: { top: '20%', alignItems: 'center', diff --git a/src/screens/onboarding/RevampedOnboarding.tsx b/src/screens/onboarding/RevampedOnboarding.tsx index 0d7f5c3c..782df6a1 100644 --- a/src/screens/onboarding/RevampedOnboarding.tsx +++ b/src/screens/onboarding/RevampedOnboarding.tsx @@ -1,7 +1,7 @@ -import {useNavigation} from '@react-navigation/core'; -import {RouteProp} from '@react-navigation/native'; -import {StackNavigationProp} from '@react-navigation/stack'; -import React, {useEffect, useState} from 'react'; +import { useNavigation } from '@react-navigation/core'; +import { RouteProp } from '@react-navigation/native'; +import { StackNavigationProp } from '@react-navigation/stack'; +import React, { useEffect, useState } from 'react'; import { Alert, KeyboardAvoidingView, @@ -9,13 +9,16 @@ import { StatusBar, StyleSheet, Text, + TouchableOpacity, View, } from 'react-native'; import { ArrowButton, Background, + LoadingIndicator, TaggInput, TaggSquareButton, + TermsConditions, } from '../../components'; import { emailRegex, @@ -29,10 +32,10 @@ import { ERROR_PHONE_IN_USE, ERROR_TWILIO_SERVER_ERROR, } from '../../constants/strings'; -import {OnboardingStackParams} from '../../routes'; -import {sendOtpStatusCode} from '../../services'; -import {BackgroundGradientType} from '../../types'; -import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +import { OnboardingStackParams } from '../../routes'; +import { sendOtpStatusCode } from '../../services'; +import { BackgroundGradientType } from '../../types'; +import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../utils'; type RevampedOnboardingRouteProp = RouteProp< OnboardingStackParams, @@ -47,14 +50,17 @@ interface RevampedOnboardingProps { navigation: RevampedOnboardingNavigationProp; } -const RevampedOnboarding: React.FC = ({route}) => { - const {isPhoneVerified} = route.params; +const RevampedOnboarding: React.FC = ({ route }) => { + const { isPhoneVerified } = route.params; const navigation = useNavigation(); type renderStatusType = 'firstName' | 'lastName' | 'username' | 'email'; const [attemptedSubmit, setAttemptedSubmit] = useState(false); // TODO: maybe use this? const [valid, setValid] = useState(false); + const [paswordsMatch, setPassMatch] = useState(false) const [currentStep, setCurrentStep] = useState(0); + const [tcAccepted, setTCAccepted] = useState(false); + const [passVisibility, setPassVisibility] = useState(false); const [form, setForm] = useState({ fname: '', lname: '', @@ -63,13 +69,13 @@ const RevampedOnboarding: React.FC = ({route}) => { email: '', password: '', confirm: '', - isValidFname: false, - isValidLname: false, - isValidPhone: false, - isValidUser: false, - isValidEmail: false, - isValidPassword: false, - passwordsMatch: false, + // isValidFname: false, + // isValidLname: false, + // isValidPhone: false, + // isValidUser: false, + // isValidEmail: false, + // isValidPassword: false, + // passwordsMatch: false, token: '', }); @@ -78,14 +84,13 @@ const RevampedOnboarding: React.FC = ({route}) => { setAttemptedSubmit(true); } try { - const {isValidPhone} = form; - if (isValidPhone) { - const {phone} = form; + if (valid) { + const { phone } = form; const code = await sendOtpStatusCode(phone); if (code) { switch (code) { case 200: - const {fname, lname} = form; + const { fname, lname } = form; navigation.navigate('PhoneVerification', { firstName: fname, lastName: lname, @@ -113,7 +118,6 @@ const RevampedOnboarding: React.FC = ({route}) => { }; } }; - // 0 = first name, 1 = last name, 2 = username, 3 = phone # const handleNameUpdate = (name: string, nameType: number) => { name = name.trim(); @@ -123,106 +127,160 @@ const RevampedOnboarding: React.FC = ({route}) => { setForm({ ...form, fname: name, - isValidFname: isValidName, }); + setValid(isValidName) break; case 1: setForm({ ...form, lname: name, - isValidLname: isValidName, }); + setValid(isValidName) break; case 2: setForm({ ...form, username: name, - isValidUser: usernameRegex.test(name), - }); - break; - case 3: - setForm({ - ...form, - phone: name, - isValidPhone: phoneRegex.test(name), }); + setValid(usernameRegex.test(name)) break; } }; const handlePhoneUpdate = (phone: string) => { phone = phone.trim(); - let isValidPhone: boolean = phoneRegex.test(phone); setForm({ ...form, phone, - isValidPhone, }); + setValid(phoneRegex.test(phone)) }; const handleEmailUpdate = (email: string) => { email = email.trim(); - let isValidEmail: boolean = emailRegex.test(email); setForm({ ...form, email, - isValidEmail, }); + setValid(emailRegex.test(email)) }; const handlePasswordUpdate = (password: string) => { - let isValidPassword: boolean = passwordRegex.test(password); - let passwordsMatch: boolean = form.password === form.confirm; setForm({ ...form, password, - isValidPassword, - passwordsMatch, }); + setValid(passwordRegex.test(password)); + setPassMatch(form.password === form.confirm) + }; + const handleConfirmUpdate = (confirm: string) => { + let passwordsMatch: boolean = form.password === confirm; + setForm({ + ...form, + confirm, + }); + setPassMatch(form.password === form.confirm) + }; + const handleTcUpdate = (tcAccepted: boolean) => { + setTCAccepted(tcAccepted); }; - const formSteps: { placeholder: string; - valid: () => boolean; + valid: boolean; onChangeText: (text: string) => void; }[] = [ - { - placeholder: 'First Name', - valid: () => form.isValidFname, - onChangeText: (text) => handleNameUpdate(text, 0), - }, - { - placeholder: 'Last Name', - valid: () => form.isValidLname, - onChangeText: (text) => handleNameUpdate(text, 1), - }, - { - placeholder: 'Phone', - valid: () => form.isValidPhone, - onChangeText: handlePhoneUpdate, - }, - { - placeholder: 'School Email', - valid: () => form.isValidEmail, - onChangeText: handleEmailUpdate, - }, - { - placeholder: 'Username', - valid: () => form.isValidLname, - onChangeText: (text) => handleNameUpdate(text, 2), - }, - { - placeholder: 'Password', - valid: () => form.isValidLname, - onChangeText: handlePasswordUpdate, - }, - // ... - ]; + { + placeholder: 'First Name', + valid: valid, + onChangeText: (text) => handleNameUpdate(text, 0), + }, + { + placeholder: 'Last Name', + valid: valid, + onChangeText: (text) => handleNameUpdate(text, 1), + }, + { + placeholder: 'Phone', + valid: valid, + onChangeText: handlePhoneUpdate, + }, + { + placeholder: 'School Email', + valid: valid, + onChangeText: handleEmailUpdate, + }, + { + placeholder: 'Username', + valid: valid, + onChangeText: (text) => handleNameUpdate(text, 2), + }, + { + placeholder: 'Password', + valid: valid, + onChangeText: handlePasswordUpdate, + }, + // ... + ]; + const resetForm = (formStep: String) => { + console.log(step.placeholder) + console.log(valid) + setValid(false); + console.log("after " + step.valid) + switch (formStep) { + case 'First Name': + setForm({ + ...form, + fname: '' + }) + break; + case 'Last Name': + setForm({ + ...form, + lname: '' + }) + break; + case 'Email': + setForm({ + ...form, + email: '' + }) + break; + case 'Password': + setForm({ + ...form, + password: '' + }) + break; + case 'School Email': + setForm({ + ...form, + email: '' + }) + break; + case 'Username': + setForm({ + ...form, + username: '' + }) + break; + } + } + const advance = () => { + setAttemptedSubmit(true); + console.log("valid? before: " + valid) + if (step.valid) { + setCurrentStep(currentStep + 1); + setAttemptedSubmit(false); + setValid(false); + } + console.log("valid? after: " + valid) + } + const togglePassVisibility = (passBool: boolean) => { + setPassVisibility(passBool); + } const step = formSteps[currentStep]; useEffect(() => { - // console.log('before: ' + currentStep + ' ' + step.placeholder); if (isPhoneVerified) { - setCurrentStep(currentStep + 1); + advance() } - // console.log('afgter: ' + currentStep + ' ' + step.placeholder); }, [isPhoneVerified]); return ( @@ -230,30 +288,31 @@ const RevampedOnboarding: React.FC = ({route}) => { style={styles.container} gradientType={BackgroundGradientType.Light}> - {/* getting rid of registration progress in onboarding*/} {/* */} - {currentStep !== 0 && ( + {(currentStep !== 0 && currentStep !== 3) && ( { - console.log('fooo'); + // if I go back do I want to reset the previous form? setCurrentStep(currentStep - 1); + resetForm(step.placeholder); + setAttemptedSubmit(false); }} /> )} - { console.log('fooo'); setCurrentStep(currentStep - 1); - }} - /> + }} */} + {/* /> */} {step.placeholder === 'Phone' && !isPhoneVerified ? ( <> = ({route}) => { onChangeText={handlePhoneUpdate} autoFocus={true} blurOnSubmit={false} - valid={form.isValidPhone} + valid={valid} invalidWarning={'Please enter a valid 10 digit number.'} attemptedSubmit={attemptedSubmit} width={280} @@ -284,48 +343,64 @@ const RevampedOnboarding: React.FC = ({route}) => { ) : ( <> - SIGN UP - - SIGN UP + + + + ) : ( + <> { - setAttemptedSubmit(true); - if (step.valid()) { - setCurrentStep(currentStep + 1); - setAttemptedSubmit(false); - } - }} - autoFocus={true} + onChangeText={handlePasswordUpdate} + onSubmitEditing={advance} blurOnSubmit={false} - valid={step.valid()} - invalidWarning={`Please enter a valid ${step.placeholder.toLowerCase()}`} + secureTextEntry={passVisibility} + valid={valid} + invalidWarning={'Password must be at least 8 characters & contain at least one of a-z, A-Z, 0-9, and a special character.'} attemptedSubmit={attemptedSubmit} - width={280} - /> - {/* { - if (step.valid()) { - setCurrentStep(currentStep + 1); - } - }} - autoFocus={true} - title={'Next'} - buttonStyle={'normal'} - buttonColor={'white'} - labelColor={'blue'} - /> */} - + width={280} /> + togglePassVisibility(!passVisibility)}> + + Show Password + + + + + )} )} - + ); @@ -363,6 +438,9 @@ const styles = StyleSheet.create({ load: { top: '5%', }, + tc: { + marginVertical: '5%', + }, footer: { width: '100%', flexDirection: 'row', -- cgit v1.2.3-70-g09d2 From fe9d3917b474eeefd026926609f29d2344ad8c53 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 7 May 2021 13:58:18 -0400 Subject: removed step three wizard --- src/screens/onboarding/OnboardingStepThree.tsx | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/screens/onboarding/OnboardingStepThree.tsx b/src/screens/onboarding/OnboardingStepThree.tsx index 34173b39..6421c396 100644 --- a/src/screens/onboarding/OnboardingStepThree.tsx +++ b/src/screens/onboarding/OnboardingStepThree.tsx @@ -224,7 +224,6 @@ const OnboardingStepThree: React.FC = ({ gradientType={BackgroundGradientType.Light} style={styles.container}> - Date: Fri, 7 May 2021 14:21:22 -0400 Subject: renamed onboarding screens, fixed various minor issue, basic info onboarding working --- src/constants/strings.ts | 5 +- src/routes/onboarding/OnboardingStackNavigator.tsx | 6 +- src/routes/onboarding/OnboardingStackScreen.tsx | 24 +- src/screens/onboarding/BasicInfoOnboarding.tsx | 460 +++++++++++++++++++++ src/screens/onboarding/Login.tsx | 2 +- src/screens/onboarding/OnboardingStepOne.tsx | 286 ------------- src/screens/onboarding/OnboardingStepThree.tsx | 380 ----------------- src/screens/onboarding/OnboardingStepTwo.tsx | 371 ----------------- src/screens/onboarding/PhoneVerification.tsx | 7 +- src/screens/onboarding/ProfileInfoOnboarding.tsx | 380 +++++++++++++++++ src/screens/onboarding/RevampedOnboarding.tsx | 458 -------------------- src/screens/onboarding/WelcomeScreen.tsx | 2 +- src/screens/onboarding/index.ts | 7 +- .../onboarding/legacy/OnboardingStepOne.tsx | 286 +++++++++++++ .../onboarding/legacy/OnboardingStepTwo.tsx | 368 +++++++++++++++++ 15 files changed, 1515 insertions(+), 1527 deletions(-) create mode 100644 src/screens/onboarding/BasicInfoOnboarding.tsx delete mode 100644 src/screens/onboarding/OnboardingStepOne.tsx delete mode 100644 src/screens/onboarding/OnboardingStepThree.tsx delete mode 100644 src/screens/onboarding/OnboardingStepTwo.tsx create mode 100644 src/screens/onboarding/ProfileInfoOnboarding.tsx delete mode 100644 src/screens/onboarding/RevampedOnboarding.tsx create mode 100644 src/screens/onboarding/legacy/OnboardingStepOne.tsx create mode 100644 src/screens/onboarding/legacy/OnboardingStepTwo.tsx (limited to 'src') diff --git a/src/constants/strings.ts b/src/constants/strings.ts index e8f2725d..56d54d39 100644 --- a/src/constants/strings.ts +++ b/src/constants/strings.ts @@ -45,6 +45,7 @@ export const ERROR_SERVER_DOWN = 'mhm, looks like our servers are down, please r export const ERROR_SOMETHING_WENT_WRONG = 'Oh dear, don’t worry someone will be held responsible for this error, In the meantime refresh the app'; export const ERROR_SOMETHING_WENT_WRONG_REFRESH = "Ha, looks like this one's on us, please refresh and try again"; export const ERROR_SOMETHING_WENT_WRONG_RELOAD = "You broke it, Just kidding! we don't know what happened... Please reload the app and try again"; +export const ERROR_T_AND_C_NOT_ACCEPTED = 'You must first agree to the terms and conditions.'; export const ERROR_TWILIO_SERVER_ERROR = 'mhm, looks like that is an invalid phone number or our servers are down, please try again in a few mins'; export const ERROR_UNABLE_CONNECT_CHAT = 'Unable to connect chat'; export const ERROR_UNABLE_TO_FIND_PROFILE = 'We were unable to find this profile. Please check username and try again'; @@ -57,17 +58,17 @@ export const ERROR_UPLOAD_SMALL_PROFILE_PIC = "Can't have a profile without a pi export const ERROR_UPLOAD_SP_PHOTO = 'Unable to update suggested people photo. Please retry!'; export const ERROR_VERIFICATION_FAILED_SHORT = 'Verification failed šŸ˜“'; export const FIRST_MESSAGE = 'How about sending your first message to your friend'; -export const START_CHATTING = 'Let’s Start Chatting!'; export const MARKED_AS_MSG = (str: string) => `Marked as ${str}`; export const MOMENT_DELETED_MSG = 'Moment deleted....Some moments have to go, to create space for greater ones'; export const NO_NEW_NOTIFICATIONS = 'You have no new notifications'; export const NO_RESULTS_FOUND = 'No Results Found!'; export const PRIVATE_ACCOUNT = 'This account is private'; +export const START_CHATTING = 'Let’s Start Chatting!'; export const SUCCESS_BADGES_UPDATE = 'Badges updated successfully!' export const SUCCESS_CATEGORY_DELETE = 'Category successfully deleted, but its memory will live on'; -export const SUCCESS_LAST_CONTACT_INVITE = 'Done! That was your last invite, hope you used it wisely!'; export const SUCCESS_INVITATION_CODE = 'Welcome to Tagg!'; export const SUCCESS_INVITE_CONTACT = (str: string) => `Success! You now have ${str} invites left!`; +export const SUCCESS_LAST_CONTACT_INVITE = 'Done! That was your last invite, hope you used it wisely!'; export const SUCCESS_LINK = (str: string) => `Successfully linked ${str} šŸŽ‰`; export const SUCCESS_PIC_UPLOAD = 'Beautiful, the picture was uploaded successfully!'; export const SUCCESS_PWD_RESET = 'Your password was reset successfully!'; diff --git a/src/routes/onboarding/OnboardingStackNavigator.tsx b/src/routes/onboarding/OnboardingStackNavigator.tsx index 761efc91..7a74988b 100644 --- a/src/routes/onboarding/OnboardingStackNavigator.tsx +++ b/src/routes/onboarding/OnboardingStackNavigator.tsx @@ -1,12 +1,10 @@ import {createStackNavigator} from '@react-navigation/stack'; export type OnboardingStackParams = { - RevampedOnboarding: {isPhoneVerified: boolean}; InvitationCodeVerification: {userId: string; username: string}; Login: undefined; - OnboardingStepOne: undefined; - OnboardingStepThree: {userId: string; username: string}; - OnboardingStepTwo: {firstName: string}; + BasicInfoOnboarding: {isPhoneVerified: boolean}; + ProfileInfoOnboarding: {userId: string; username: string}; PasswordReset: {value: string}; PasswordResetRequest: undefined; PasswordVerification: {id: string}; diff --git a/src/routes/onboarding/OnboardingStackScreen.tsx b/src/routes/onboarding/OnboardingStackScreen.tsx index 458697ad..50fd52d6 100644 --- a/src/routes/onboarding/OnboardingStackScreen.tsx +++ b/src/routes/onboarding/OnboardingStackScreen.tsx @@ -1,18 +1,16 @@ import {StackCardInterpolationProps} from '@react-navigation/stack'; import React from 'react'; import { + BasicInfoOnboarding, InvitationCodeVerification, Login, - OnboardingStepThree, - OnboardingStepTwo, PasswordReset, PasswordResetRequest, PasswordVerification, PhoneVerification, + ProfileInfoOnboarding, WelcomeScreen, - RevampedOnboarding } from '../../screens'; -import OnboardingStepOne from '../../screens/onboarding/OnboardingStepOne'; import {modalStyle} from '../main'; import {OnboardingStack} from './OnboardingStackNavigator'; @@ -44,7 +42,10 @@ const Onboarding: React.FC = () => { }} /> - + { ...modalStyle, }} /> - - ; +type BasicInfoOnboardingNavigationProp = StackNavigationProp< + OnboardingStackParams, + 'BasicInfoOnboarding' +>; +interface BasicInfoOnboardingProps { + route: BasicInfoOnboardingRouteProp; + navigation: BasicInfoOnboardingNavigationProp; +} + +const BasicInfoOnboarding: React.FC = ({route}) => { + const {isPhoneVerified} = route.params; + const navigation = useNavigation(); + const [attemptedSubmit, setAttemptedSubmit] = useState(false); + const [valid, setValid] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [tcAccepted, setTCAccepted] = useState(false); + const [passVisibility, setPassVisibility] = useState(false); + const [form, setForm] = useState({ + fname: '', + lname: '', + username: '', + phone: '', + email: '', + password: '', + }); + + const goNext = async () => { + if (!attemptedSubmit) { + setAttemptedSubmit(true); + } + try { + if (valid) { + const {phone} = form; + const code = await sendOtpStatusCode(phone); + if (code) { + switch (code) { + case 200: + const {fname, lname} = form; + navigation.navigate('PhoneVerification', { + firstName: fname, + lastName: lname, + phone, + }); + break; + case 409: + Alert.alert(ERROR_PHONE_IN_USE); + break; + default: + Alert.alert(ERROR_TWILIO_SERVER_ERROR); + } + } else { + setAttemptedSubmit(false); + setTimeout(() => { + setAttemptedSubmit(true); + }); + } + } + } catch (error) { + Alert.alert(ERROR_NEXT_PAGE); + return { + name: 'Navigation error', + description: error, + }; + } + }; + // 0 = first name, 1 = last name, 2 = username, 3 = phone # + const handleNameUpdate = (name: string, nameType: number) => { + name = name.trim(); + let isValidName: boolean = nameRegex.test(name); + switch (nameType) { + case 0: + setForm({ + ...form, + fname: name, + }); + setValid(isValidName); + break; + case 1: + setForm({ + ...form, + lname: name, + }); + setValid(isValidName); + break; + case 2: + setForm({ + ...form, + username: name, + }); + setValid(usernameRegex.test(name)); + break; + } + }; + const handlePhoneUpdate = (phone: string) => { + phone = phone.trim(); + setForm({ + ...form, + phone, + }); + setValid(phoneRegex.test(phone)); + }; + const handleEmailUpdate = (email: string) => { + email = email.trim(); + setForm({ + ...form, + email, + }); + setValid(emailRegex.test(email)); + }; + const handlePasswordUpdate = (password: string) => { + setForm({ + ...form, + password, + }); + setValid(passwordRegex.test(password)); + }; + const formSteps: { + placeholder: string; + onChangeText: (text: string) => void; + }[] = [ + { + placeholder: 'First Name', + onChangeText: (text) => handleNameUpdate(text, 0), + }, + { + placeholder: 'Last Name', + onChangeText: (text) => handleNameUpdate(text, 1), + }, + { + placeholder: 'Phone', + onChangeText: handlePhoneUpdate, + }, + { + placeholder: 'School Email', + onChangeText: handleEmailUpdate, + }, + { + placeholder: 'Username', + onChangeText: (text) => handleNameUpdate(text, 2), + }, + { + placeholder: 'Password', + onChangeText: handlePasswordUpdate, + }, + ]; + const resetForm = (formStep: String) => { + setValid(false); + switch (formStep) { + case 'First Name': + setForm({ + ...form, + fname: '', + }); + break; + case 'Last Name': + setForm({ + ...form, + lname: '', + }); + break; + case 'Email': + setForm({ + ...form, + email: '', + }); + break; + case 'Password': + setForm({ + ...form, + password: '', + }); + break; + case 'School Email': + setForm({ + ...form, + email: '', + }); + break; + case 'Username': + setForm({ + ...form, + username: '', + }); + break; + } + }; + const step = formSteps[currentStep]; + const advance = () => { + setAttemptedSubmit(true); + if (valid) { + setCurrentStep(currentStep + 1); + setAttemptedSubmit(false); + setValid(false); + } + }; + const advanceRegistration = async () => { + setAttemptedSubmit(true); + if (!valid) { + return; + } + if (!tcAccepted) { + Alert.alert('Terms and conditions', ERROR_T_AND_C_NOT_ACCEPTED); + return; + } + const {fname, lname, phone, email, username, password} = form; + const response = await sendRegister( + fname, + lname, + phone, + email, + username, + password, + ); + if (response) { + const data = await response.json(); + const {token, UserID} = data; + switch (response.status) { + case 201: + await AsyncStorage.setItem('token', token); + navigation.navigate('ProfileInfoOnboarding', { + userId: UserID, + username: username, + }); + break; + case 400: + Alert.alert(ERROR_REGISTRATION(data.toLowerCase())); + break; + default: + Alert.alert(ERROR_SOMETHING_WENT_WRONG_REFRESH); + break; + } + } else { + Alert.alert(ERROR_SOMETHING_WENT_WRONG_REFRESH); + } + }; + + useEffect(() => { + if (isPhoneVerified) { + advance(); + } + }, [isPhoneVerified]); + + return ( + + + {/* getting rid of registration progress in onboarding*/} + {/* */} + + + {currentStep !== 0 && currentStep !== 3 && ( + { + // if I go back do I want to reset the previous form? + setCurrentStep(currentStep - 1); + resetForm(step.placeholder); + setAttemptedSubmit(false); + }} + /> + )} + + {step.placeholder === 'Phone' && !isPhoneVerified ? ( + <> + + + + ) : ( + <> + {step.placeholder !== 'Password' ? ( + <> + SIGN UP + + + + + + ) : ( + <> + + setPassVisibility(!passVisibility)}> + Show Password + + + + + )} + + )} + + + ); +}; + +const styles = StyleSheet.create({ + container: { + height: SCREEN_HEIGHT, + width: SCREEN_WIDTH, + alignItems: 'center', + justifyContent: 'center', + }, + input: { + minWidth: '60%', + height: 40, + fontSize: 16, + fontWeight: '600', + color: '#fff', + paddingLeft: 13, + borderBottomWidth: 1, + borderBottomColor: '#fff', + }, + button: { + width: 40, + aspectRatio: 10, + }, + formHeader: { + color: '#fff', + fontSize: 30, + fontWeight: '600', + position: 'absolute', + top: '20%', + }, + load: { + top: '5%', + }, + tc: { + marginVertical: '5%', + }, + footer: { + width: '100%', + flexDirection: 'row', + justifyContent: 'space-around', + ...Platform.select({ + ios: { + bottom: '20%', + }, + android: { + bottom: '10%', + }, + }), + }, +}); +export default BasicInfoOnboarding; diff --git a/src/screens/onboarding/Login.tsx b/src/screens/onboarding/Login.tsx index 3b970864..6922bbc7 100644 --- a/src/screens/onboarding/Login.tsx +++ b/src/screens/onboarding/Login.tsx @@ -182,7 +182,7 @@ const Login: React.FC = ({navigation}: LoginProps) => { * finish step 3, thus does not have a universtiy. * Redirecting user back to onboarding to finish the process */ - navigation.navigate('OnboardingStepThree', { + navigation.navigate('ProfileInfoOnboarding', { userId: data.UserID, username: username, }); diff --git a/src/screens/onboarding/OnboardingStepOne.tsx b/src/screens/onboarding/OnboardingStepOne.tsx deleted file mode 100644 index abec050e..00000000 --- a/src/screens/onboarding/OnboardingStepOne.tsx +++ /dev/null @@ -1,286 +0,0 @@ -import {StackNavigationProp} from '@react-navigation/stack'; -import React, {useMemo, useRef, useState} from 'react'; -import { - Alert, - KeyboardAvoidingView, - Platform, - StatusBar, - StyleSheet, - Text, - TouchableOpacity, - View, -} from 'react-native'; -import { - ArrowButton, - Background, - BasicButton, - // RegistrationWizard, - TaggInput, - TaggSquareButton, -} from '../../components'; -import {nameRegex, phoneRegex} from '../../constants'; -import { - ERROR_NEXT_PAGE, - ERROR_PHONE_IN_USE, - ERROR_TWILIO_SERVER_ERROR, -} from '../../constants/strings'; -import {OnboardingStackParams} from '../../routes'; -import {sendOtpStatusCode} from '../../services'; -import {BackgroundGradientType} from '../../types'; -import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; - -type OnboardingStepOneNavigationProp = StackNavigationProp< - OnboardingStackParams, - 'OnboardingStepOne' ->; -interface OnboardingStepOneProps { - navigation: OnboardingStepOneNavigationProp; -} - -const OnboardingStepOne: React.FC = ({navigation}) => { - const lnameRef = useRef(); - const emailRef = useRef(); - const phoneRef = useRef(); - - const handleFocusChange = (field: string): void => { - switch (field) { - case 'lname': - const lnameField: any = lnameRef.current; - lnameField.focus(); - break; - case 'email': - const emailField: any = emailRef.current; - emailField.focus(); - break; - case 'phone': - const phoneField: any = phoneRef.current; - phoneField.focus(); - break; - default: - return; - } - }; - - const [form, setForm] = useState({ - fname: '', - lname: '', - phone: '', - isValidFname: false, - isValidLname: false, - isValidPhone: false, - attemptedSubmit: false, - token: '', - }); - - const handleFnameUpdate = (fname: string) => { - fname = fname.trim(); - let isValidFname: boolean = nameRegex.test(fname); - setForm({ - ...form, - fname, - isValidFname, - }); - }; - - const handleLnameUpdate = (lname: string) => { - lname = lname.trim(); - let isValidLname: boolean = nameRegex.test(lname); - setForm({ - ...form, - lname, - isValidLname, - }); - }; - - const handlePhoneUpdate = (phone: string) => { - phone = phone.trim(); - let isValidPhone: boolean = phoneRegex.test(phone); - setForm({ - ...form, - phone, - isValidPhone, - }); - }; - - const goNext = async () => { - if (!form.attemptedSubmit) { - setForm({ - ...form, - attemptedSubmit: true, - }); - } - try { - const {isValidFname} = form; - // const {isValidFname, isValidLname, isValidPhone} = form; - if (isValidFname) { - const {fname} = form; - navigation.navigate('OnboardingStepTwo', { - firstName: fname - }); - // const {phone} = form; - // const code = await sendOtpStatusCode(phone); - // if (code) { - // switch (code) { - // case 200: - // const {fname, lname} = form; - - // break; - // case 409: - // Alert.alert(ERROR_PHONE_IN_USE); - // break; - // default: - // Alert.alert(ERROR_TWILIO_SERVER_ERROR); - } else { - setForm({...form, attemptedSubmit: false}); - setTimeout(() => setForm({...form, attemptedSubmit: true})); - } - } catch (error) { - Alert.alert(ERROR_NEXT_PAGE); - return { - name: 'Navigation error', - description: error, - }; - } - }; - - const footer = useMemo( - () => ( - - // // - // {/* navigation.navigate('Login')} - // /> */} - // {/* */} - // {/* */} - // {/* */} - // // - ), - [ - form.fname, - // form.lname, - // form.phone, - form.isValidFname, - // form.isValidLname, - // form.isValidPhone, - ], - ); - - return ( - - - {/* getting rid of registration progress in onboarding*/} - {/* */} - - {/* */} - SIGN UP - {/* */} - handleFocusChange('lname')} - blurOnSubmit={false} - valid={form.isValidFname} - invalidWarning="Please enter a valid first name." - attemptedSubmit={form.attemptedSubmit} - width={280} - /> - {/* */} - {/* */} - - {footer} - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - height: SCREEN_HEIGHT, - width: SCREEN_WIDTH, - alignItems: 'center', - justifyContent: 'center', - }, - button: { - width: 40, - aspectRatio: 10, - }, - formHeader: { - color: '#fff', - fontSize: 30, - fontWeight: '600', - marginBottom: '16%', - position: 'absolute', top: 0, marginTop: '45%', - }, - load: { - top: '5%', - }, - footer: { - width: '100%', - flexDirection: 'row', - justifyContent: 'space-around', - ...Platform.select({ - ios: { - bottom: '20%', - }, - android: { - bottom: '10%', - }, - }), - }, -}); - -export default OnboardingStepOne; diff --git a/src/screens/onboarding/OnboardingStepThree.tsx b/src/screens/onboarding/OnboardingStepThree.tsx deleted file mode 100644 index 6421c396..00000000 --- a/src/screens/onboarding/OnboardingStepThree.tsx +++ /dev/null @@ -1,380 +0,0 @@ -import {RouteProp} from '@react-navigation/native'; -import {StackNavigationProp} from '@react-navigation/stack'; -import moment from 'moment'; -import React from 'react'; -import { - Alert, - Image, - StatusBar, - StyleSheet, - Text, - TouchableOpacity, - View, -} from 'react-native'; -import ImagePicker from 'react-native-image-crop-picker'; -import Animated from 'react-native-reanimated'; -import { - Background, - BirthDatePicker, - RegistrationWizard, - TaggDropDown, - TaggInput, - UniversitySelection, -} from '../../components'; -import {CLASS_YEAR_LIST, genderRegex, TAGG_PURPLE} from '../../constants'; -import { - ERROR_SELECT_BIRTHDAY, - ERROR_SELECT_CLASS_YEAR, - ERROR_SELECT_GENDER, - ERROR_SELECT_UNIVERSITY, -} from '../../constants/strings'; -import {OnboardingStackParams} from '../../routes/onboarding'; -import {patchEditProfile} from '../../services'; -import {BackgroundGradientType, UniversityType} from '../../types'; -import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; - -type OnboardingStepThreeRouteProp = RouteProp< - OnboardingStackParams, - 'OnboardingStepThree' ->; -type OnboardingStepThreeNavigationProp = StackNavigationProp< - OnboardingStackParams, - 'OnboardingStepThree' ->; -interface OnboardingStepThreeProps { - route: OnboardingStepThreeRouteProp; - navigation: OnboardingStepThreeNavigationProp; -} - -type FormType = { - smallPic: string; - university: UniversityType; - birthdate: string | undefined; - gender: string; - isValidGender: boolean; - classYear: number; - attemptedSubmit: boolean; -}; - -const OnboardingStepThree: React.FC = ({ - route, - navigation, -}) => { - const {userId} = route.params; - const [form, setForm] = React.useState({ - smallPic: '', - university: UniversityType.Empty, - birthdate: undefined, - gender: '', - isValidGender: true, - classYear: -1, - attemptedSubmit: false, - }); - const [customGender, setCustomGender] = React.useState(false); - - const classYearList = CLASS_YEAR_LIST.map((value) => ({ - label: value, - value, - })); - - /** - * Profile screen "Add profile picture" button - */ - const SmallProfilePic = () => ( - - {form.smallPic ? ( - - ) : ( - ADD PROFILE PICTURE - )} - - ); - - const goToGallerySmallPic = () => { - ImagePicker.openPicker({ - smartAlbums: [ - 'Favorites', - 'RecentlyAdded', - 'SelfPortraits', - 'Screenshots', - 'UserLibrary', - ], - width: 580, - height: 580, - cropping: true, - cropperToolbarTitle: 'Select Profile Picture', - mediaType: 'photo', - cropperCircleOverlay: true, - }).then((picture) => { - if ('path' in picture) { - setForm({ - ...form, - smallPic: picture.path, - }); - } - }); - }; - - const handleGenderUpdate = (gender: string) => { - if (gender === 'custom') { - setCustomGender(true); - } else { - setCustomGender(false); - let isValidGender: boolean = true; - setForm({ - ...form, - gender, - isValidGender, - }); - } - }; - - const handleClassYearUpdate = (value: string) => { - const classYear = parseInt(value, 10); - setForm({ - ...form, - classYear, - }); - }; - - const handleCustomGenderUpdate = (gender: string) => { - let isValidGender: boolean = genderRegex.test(gender); - gender = gender.replace(' ', '-'); - setForm({ - ...form, - gender, - isValidGender, - }); - }; - - const handleBirthdateUpdate = (birthdate: Date) => { - setForm({ - ...form, - birthdate: birthdate && moment(birthdate).format('YYYY-MM-DD'), - }); - }; - - const handleSubmit = async () => { - if (form.classYear === -1) { - Alert.alert(ERROR_SELECT_CLASS_YEAR); - return; - } - if (form.university === UniversityType.Empty) { - Alert.alert(ERROR_SELECT_UNIVERSITY); - return; - } - if (!form.birthdate) { - Alert.alert(ERROR_SELECT_BIRTHDAY); - return; - } - if (form.gender === '') { - Alert.alert(ERROR_SELECT_GENDER); - return; - } - if (!form.attemptedSubmit) { - setForm({ - ...form, - attemptedSubmit: true, - }); - } - const request = new FormData(); - if (form.smallPic) { - request.append('smallProfilePicture', { - uri: form.smallPic, - name: 'small_profile_pic.jpg', - type: 'image/jpg', - }); - } - - if (customGender) { - if (form.isValidGender) { - request.append('gender', form.gender); - } else { - setForm({...form, attemptedSubmit: false}); - setTimeout(() => setForm({...form, attemptedSubmit: true})); - return; - } - } else { - if (form.isValidGender) { - request.append('gender', form.gender); - } - } - - request.append('birthday', form.birthdate); - request.append('university_class', form.classYear); - request.append('university', form.university); - - patchEditProfile(request, userId) - .then((_) => - navigation.navigate('InvitationCodeVerification', route.params), - ) - .catch((error) => { - Alert.alert(error); - }); - }; - - return ( - - - - - - - - - { - setForm({ - ...form, - university: selected, - }); - }} - /> - handleClassYearUpdate(value)} - items={classYearList} - placeholder={{ - label: 'Class Year', - value: null, - color: '#ddd', - }} - /> - - {customGender && ( - handleSubmit()} - valid={form.isValidGender} - attemptedSubmit={form.attemptedSubmit} - invalidWarning={ - 'Custom field can only contain letters and hyphens' - } - width={280} - /> - )} - handleGenderUpdate(value)} - items={[ - {label: 'Male', value: 'male'}, - {label: 'Female', value: 'female'}, - {label: 'Custom', value: 'custom'}, - ]} - placeholder={{ - label: 'Gender', - value: null, - color: '#ddd', - }} - /> - - - - Let's start! - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - height: SCREEN_HEIGHT, - }, - profile: { - marginTop: '10%', - marginBottom: '5%', - }, - contentContainer: { - position: 'relative', - width: 280, - }, - smallProfileUploader: { - justifyContent: 'center', - alignItems: 'center', - padding: 20, - backgroundColor: '#E1F0FF', - height: normalize(150), - width: normalize(150), - borderRadius: normalize(150), - }, - smallProfileText: { - textAlign: 'center', - fontSize: 14, - fontWeight: 'bold', - color: '#806DF4', - }, - smallProfilePic: { - height: normalize(150), - width: normalize(150), - borderRadius: normalize(150), - borderWidth: 2, - borderColor: 'white', - }, - submitBtn: { - backgroundColor: TAGG_PURPLE, - justifyContent: 'center', - alignItems: 'center', - width: SCREEN_WIDTH / 2.5, - height: SCREEN_WIDTH / 10, - borderRadius: 5, - marginTop: '5%', - alignSelf: 'center', - }, - submitBtnLabel: { - fontSize: 16, - fontWeight: '500', - color: '#fff', - }, - goBack: { - textDecorationLine: 'underline', - color: '#fff', - fontSize: 15, - fontWeight: '600', - }, - footer: { - marginTop: '3%', - alignItems: 'center', - justifyContent: 'space-around', - height: SCREEN_HEIGHT * 0.15, - }, - wizard: { - position: 'absolute', - top: SCREEN_HEIGHT * 0.1, - }, - purplePlus: { - position: 'absolute', - height: normalize(40), - width: normalize(40), - bottom: 0, - right: 0, - }, -}); - -export default OnboardingStepThree; diff --git a/src/screens/onboarding/OnboardingStepTwo.tsx b/src/screens/onboarding/OnboardingStepTwo.tsx deleted file mode 100644 index 5c474c14..00000000 --- a/src/screens/onboarding/OnboardingStepTwo.tsx +++ /dev/null @@ -1,371 +0,0 @@ -import AsyncStorage from '@react-native-community/async-storage'; -import {RouteProp} from '@react-navigation/native'; -import {StackNavigationProp} from '@react-navigation/stack'; -import React, {useMemo, useRef, useState} from 'react'; -import { - Alert, - KeyboardAvoidingView, - Platform, - StatusBar, - StyleSheet, - Text, - TouchableOpacity, - View, -} from 'react-native'; -import { - ArrowButton, - Background, - LoadingIndicator, - // RegistrationWizard, - TaggInput, - TaggSquareButton, - TermsConditions, -} from '../../components'; -import {emailRegex, passwordRegex, usernameRegex} from '../../constants'; -import { - ERROR_DOUBLE_CHECK_CONNECTION, - ERROR_REGISTRATION, - ERROR_SOMETHING_WENT_WRONG_REFRESH, -} from '../../constants/strings'; -import {OnboardingStackParams} from '../../routes'; -import {sendRegister} from '../../services'; -import {BackgroundGradientType} from '../../types'; -import {SCREEN_HEIGHT} from '../../utils'; - -type OnboardingStepTwoRouteProp = RouteProp< - OnboardingStackParams, - 'OnboardingStepTwo' ->; -type OnboardingStepTwoNavigationProp = StackNavigationProp< - OnboardingStackParams, - 'OnboardingStepTwo' ->; -interface OnboardingStepTwoProps { - route: OnboardingStepTwoRouteProp; - navigation: OnboardingStepTwoNavigationProp; -} - -const OnboardingStepTwo: React.FC = ({ - route, - navigation, -}) => { - const emailRef = useRef(); - const usernameRef = useRef(); - const passwordRef = useRef(); - const confirmRef = useRef(); - - const handleFocusChange = (field: string): void => { - switch (field) { - case 'email': - const emailField: any = emailRef.current; - emailField.focus(); - break; - case 'username': - const usernameField: any = usernameRef.current; - usernameField.focus(); - break; - case 'password': - const passwordField: any = passwordRef.current; - passwordField.focus(); - break; - case 'confirm': - const confirmField: any = confirmRef.current; - confirmField.focus(); - break; - default: - return; - } - }; - - // registration form state - const [form, setForm] = useState({ - email: '', - username: '', - password: '', - confirm: '', - isValidEmail: false, - isValidUsername: false, - isValidPassword: false, - passwordsMatch: false, - tcAccepted: false, - attemptedSubmit: false, - }); - - const handleEmailUpdate = (email: string) => { - email = email.trim(); - let isValidEmail: boolean = emailRegex.test(email); - setForm({ - ...form, - email, - isValidEmail, - }); - }; - - const handleUsernameUpdate = (username: string) => { - let isValidUsername: boolean = usernameRegex.test(username); - setForm({ - ...form, - username, - isValidUsername, - }); - }; - - const handlePasswordUpdate = (password: string) => { - let isValidPassword: boolean = passwordRegex.test(password); - let passwordsMatch: boolean = form.password === form.confirm; - setForm({ - ...form, - password, - isValidPassword, - passwordsMatch, - }); - }; - - const handleConfirmUpdate = (confirm: string) => { - let passwordsMatch: boolean = form.password === confirm; - setForm({ - ...form, - confirm, - passwordsMatch, - }); - }; - - const handleTcUpdate = (tcAccepted: boolean) => { - setForm({ - ...form, - tcAccepted, - }); - }; - - const handleRegister = async () => { - if (!form.attemptedSubmit) { - setForm({ - ...form, - attemptedSubmit: true, - }); - } - const { - isValidEmail, - isValidUsername, - isValidPassword, - passwordsMatch, - tcAccepted, - } = form; - try { - if ( - isValidEmail && - isValidUsername && - isValidPassword && - passwordsMatch - ) { - if (tcAccepted) { - const {email, username, password} = form; - const {firstName, lastName, phone} = route.params; - const response = await sendRegister( - firstName, - lastName, - phone, - email, - username, - password, - ); - if (response) { - const data = await response.json(); - const {token, UserID} = data; - switch (response.status) { - case 201: - await AsyncStorage.setItem('token', token); - navigation.navigate('OnboardingStepThree', { - userId: UserID, - username: username, - }); - break; - case 400: - Alert.alert(ERROR_REGISTRATION(data.toLowerCase())); - break; - default: - Alert.alert(ERROR_SOMETHING_WENT_WRONG_REFRESH); - break; - } - } else { - Alert.alert(ERROR_SOMETHING_WENT_WRONG_REFRESH); - } - } else { - Alert.alert( - 'Terms and conditions', - 'You must first agree to the terms and conditions.', - ); - } - } else { - setForm({...form, attemptedSubmit: false}); - setTimeout(() => setForm({...form, attemptedSubmit: true})); - } - } catch (error) { - Alert.alert(ERROR_REGISTRATION(ERROR_DOUBLE_CHECK_CONNECTION)); - return { - name: 'Registration error', - description: error, - }; - } - }; - - const footer = useMemo( - () => ( - - - - - - ), - [ - form.email, - form.username, - form.password, - form.confirm, - form.isValidEmail, - form.isValidUsername, - form.isValidPassword, - form.passwordsMatch, - form.tcAccepted, - ], - ); - - return ( - - - {/* */} - - - SIGN UP - - handleFocusChange('password')} - blurOnSubmit={false} - ref={usernameRef} - valid={form.isValidUsername} - invalidWarning={ - 'Username must beĀ at least 6 characters and contain only alphanumerics.' - } - attemptedSubmit={form.attemptedSubmit} - width={280} - /> - - handleFocusChange('confirm')} - blurOnSubmit={false} - secureTextEntry - ref={passwordRef} - valid={form.isValidPassword} - invalidWarning={ - 'Password must be at least 8 characters & contain at least one of a-z, A-Z, 0-9, and a special character.' - } - attemptedSubmit={form.attemptedSubmit} - width={280} - /> - - - - - {footer} - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - // wizard: { - // position: 'absolute', - // top: SCREEN_HEIGHT * 0.1, - // }, - formHeader: { - color: '#fff', - fontSize: 30, - fontWeight: '600', - marginBottom: '16%', - }, - tc: { - marginVertical: '5%', - }, - load: { - top: '5%', - }, - footer: { - width: '100%', - flexDirection: 'row', - justifyContent: 'space-around', - ...Platform.select({ - ios: { - bottom: '20%', - }, - android: { - bottom: '10%', - }, - }), - }, -}); - -export default OnboardingStepTwo; diff --git a/src/screens/onboarding/PhoneVerification.tsx b/src/screens/onboarding/PhoneVerification.tsx index 9b517d9d..93ae8fdc 100644 --- a/src/screens/onboarding/PhoneVerification.tsx +++ b/src/screens/onboarding/PhoneVerification.tsx @@ -31,7 +31,6 @@ import { import {OnboardingStackParams} from '../../routes'; import {sendOtp, verifyOtp} from '../../services'; import {BackgroundGradientType} from '../../types'; -import {SCREEN_HEIGHT} from '../../utils'; type PhoneVerificationRouteProp = RouteProp< OnboardingStackParams, @@ -66,7 +65,7 @@ const PhoneVerification: React.FC = ({ try { const success = await trackPromise(verifyOtp(phone, value)); if (success) { - navigation.navigate('RevampedOnboarding', {isPhoneVerified: true}); + navigation.navigate('BasicInfoOnboarding', {isPhoneVerified: true}); } } catch (error) { console.log(error); @@ -79,7 +78,9 @@ const PhoneVerification: React.FC = ({ navigation.navigate('RevampedOnboarding', {isPhoneVerified: false})} + onPress={() => + navigation.navigate('BasicInfoOnboarding', {isPhoneVerified: false}) + } /> ), diff --git a/src/screens/onboarding/ProfileInfoOnboarding.tsx b/src/screens/onboarding/ProfileInfoOnboarding.tsx new file mode 100644 index 00000000..a481b0c0 --- /dev/null +++ b/src/screens/onboarding/ProfileInfoOnboarding.tsx @@ -0,0 +1,380 @@ +import {RouteProp} from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; +import moment from 'moment'; +import React from 'react'; +import { + Alert, + Image, + StatusBar, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import ImagePicker from 'react-native-image-crop-picker'; +import Animated from 'react-native-reanimated'; +import { + Background, + BirthDatePicker, + RegistrationWizard, + TaggDropDown, + TaggInput, + UniversitySelection, +} from '../../components'; +import {CLASS_YEAR_LIST, genderRegex, TAGG_PURPLE} from '../../constants'; +import { + ERROR_SELECT_BIRTHDAY, + ERROR_SELECT_CLASS_YEAR, + ERROR_SELECT_GENDER, + ERROR_SELECT_UNIVERSITY, +} from '../../constants/strings'; +import {OnboardingStackParams} from '../../routes/onboarding'; +import {patchEditProfile} from '../../services'; +import {BackgroundGradientType, UniversityType} from '../../types'; +import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; + +type ProfileInfoOnboardingRouteProp = RouteProp< + OnboardingStackParams, + 'ProfileInfoOnboarding' +>; +type ProfileInfoOnboardingNavigationProp = StackNavigationProp< + OnboardingStackParams, + 'ProfileInfoOnboarding' +>; +interface ProfileInfoOnboardingProps { + route: ProfileInfoOnboardingRouteProp; + navigation: ProfileInfoOnboardingNavigationProp; +} + +type FormType = { + smallPic: string; + university: UniversityType; + birthdate: string | undefined; + gender: string; + isValidGender: boolean; + classYear: number; + attemptedSubmit: boolean; +}; + +const ProfileInfoOnboarding: React.FC = ({ + route, + navigation, +}) => { + const {userId} = route.params; + const [form, setForm] = React.useState({ + smallPic: '', + university: UniversityType.Empty, + birthdate: undefined, + gender: '', + isValidGender: true, + classYear: -1, + attemptedSubmit: false, + }); + const [customGender, setCustomGender] = React.useState(false); + + const classYearList = CLASS_YEAR_LIST.map((value) => ({ + label: value, + value, + })); + + /** + * Profile screen "Add profile picture" button + */ + const SmallProfilePic = () => ( + + {form.smallPic ? ( + + ) : ( + ADD PROFILE PICTURE + )} + + ); + + const goToGallerySmallPic = () => { + ImagePicker.openPicker({ + smartAlbums: [ + 'Favorites', + 'RecentlyAdded', + 'SelfPortraits', + 'Screenshots', + 'UserLibrary', + ], + width: 580, + height: 580, + cropping: true, + cropperToolbarTitle: 'Select Profile Picture', + mediaType: 'photo', + cropperCircleOverlay: true, + }).then((picture) => { + if ('path' in picture) { + setForm({ + ...form, + smallPic: picture.path, + }); + } + }); + }; + + const handleGenderUpdate = (gender: string) => { + if (gender === 'custom') { + setCustomGender(true); + } else { + setCustomGender(false); + let isValidGender: boolean = true; + setForm({ + ...form, + gender, + isValidGender, + }); + } + }; + + const handleClassYearUpdate = (value: string) => { + const classYear = parseInt(value, 10); + setForm({ + ...form, + classYear, + }); + }; + + const handleCustomGenderUpdate = (gender: string) => { + let isValidGender: boolean = genderRegex.test(gender); + gender = gender.replace(' ', '-'); + setForm({ + ...form, + gender, + isValidGender, + }); + }; + + const handleBirthdateUpdate = (birthdate: Date) => { + setForm({ + ...form, + birthdate: birthdate && moment(birthdate).format('YYYY-MM-DD'), + }); + }; + + const handleSubmit = async () => { + if (form.classYear === -1) { + Alert.alert(ERROR_SELECT_CLASS_YEAR); + return; + } + if (form.university === UniversityType.Empty) { + Alert.alert(ERROR_SELECT_UNIVERSITY); + return; + } + if (!form.birthdate) { + Alert.alert(ERROR_SELECT_BIRTHDAY); + return; + } + if (form.gender === '') { + Alert.alert(ERROR_SELECT_GENDER); + return; + } + if (!form.attemptedSubmit) { + setForm({ + ...form, + attemptedSubmit: true, + }); + } + const request = new FormData(); + if (form.smallPic) { + request.append('smallProfilePicture', { + uri: form.smallPic, + name: 'small_profile_pic.jpg', + type: 'image/jpg', + }); + } + + if (customGender) { + if (form.isValidGender) { + request.append('gender', form.gender); + } else { + setForm({...form, attemptedSubmit: false}); + setTimeout(() => setForm({...form, attemptedSubmit: true})); + return; + } + } else { + if (form.isValidGender) { + request.append('gender', form.gender); + } + } + + request.append('birthday', form.birthdate); + request.append('university_class', form.classYear); + request.append('university', form.university); + + patchEditProfile(request, userId) + .then((_) => + navigation.navigate('InvitationCodeVerification', route.params), + ) + .catch((error) => { + Alert.alert(error); + }); + }; + + return ( + + + + + + + + + { + setForm({ + ...form, + university: selected, + }); + }} + /> + handleClassYearUpdate(value)} + items={classYearList} + placeholder={{ + label: 'Class Year', + value: null, + color: '#ddd', + }} + /> + + {customGender && ( + handleSubmit()} + valid={form.isValidGender} + attemptedSubmit={form.attemptedSubmit} + invalidWarning={ + 'Custom field can only contain letters and hyphens' + } + width={280} + /> + )} + handleGenderUpdate(value)} + items={[ + {label: 'Male', value: 'male'}, + {label: 'Female', value: 'female'}, + {label: 'Custom', value: 'custom'}, + ]} + placeholder={{ + label: 'Gender', + value: null, + color: '#ddd', + }} + /> + + + + Let's start! + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + height: SCREEN_HEIGHT, + }, + profile: { + marginTop: '10%', + marginBottom: '5%', + }, + contentContainer: { + position: 'relative', + width: 280, + }, + smallProfileUploader: { + justifyContent: 'center', + alignItems: 'center', + padding: 20, + backgroundColor: '#E1F0FF', + height: normalize(150), + width: normalize(150), + borderRadius: normalize(150), + }, + smallProfileText: { + textAlign: 'center', + fontSize: 14, + fontWeight: 'bold', + color: '#806DF4', + }, + smallProfilePic: { + height: normalize(150), + width: normalize(150), + borderRadius: normalize(150), + borderWidth: 2, + borderColor: 'white', + }, + submitBtn: { + backgroundColor: TAGG_PURPLE, + justifyContent: 'center', + alignItems: 'center', + width: SCREEN_WIDTH / 2.5, + height: SCREEN_WIDTH / 10, + borderRadius: 5, + marginTop: '5%', + alignSelf: 'center', + }, + submitBtnLabel: { + fontSize: 16, + fontWeight: '500', + color: '#fff', + }, + goBack: { + textDecorationLine: 'underline', + color: '#fff', + fontSize: 15, + fontWeight: '600', + }, + footer: { + marginTop: '3%', + alignItems: 'center', + justifyContent: 'space-around', + height: SCREEN_HEIGHT * 0.15, + }, + wizard: { + position: 'absolute', + top: SCREEN_HEIGHT * 0.1, + }, + purplePlus: { + position: 'absolute', + height: normalize(40), + width: normalize(40), + bottom: 0, + right: 0, + }, +}); + +export default ProfileInfoOnboarding; diff --git a/src/screens/onboarding/RevampedOnboarding.tsx b/src/screens/onboarding/RevampedOnboarding.tsx deleted file mode 100644 index 782df6a1..00000000 --- a/src/screens/onboarding/RevampedOnboarding.tsx +++ /dev/null @@ -1,458 +0,0 @@ -import { useNavigation } from '@react-navigation/core'; -import { RouteProp } from '@react-navigation/native'; -import { StackNavigationProp } from '@react-navigation/stack'; -import React, { useEffect, useState } from 'react'; -import { - Alert, - KeyboardAvoidingView, - Platform, - StatusBar, - StyleSheet, - Text, - TouchableOpacity, - View, -} from 'react-native'; -import { - ArrowButton, - Background, - LoadingIndicator, - TaggInput, - TaggSquareButton, - TermsConditions, -} from '../../components'; -import { - emailRegex, - nameRegex, - passwordRegex, - phoneRegex, - usernameRegex, -} from '../../constants'; -import { - ERROR_NEXT_PAGE, - ERROR_PHONE_IN_USE, - ERROR_TWILIO_SERVER_ERROR, -} from '../../constants/strings'; -import { OnboardingStackParams } from '../../routes'; -import { sendOtpStatusCode } from '../../services'; -import { BackgroundGradientType } from '../../types'; -import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../utils'; - -type RevampedOnboardingRouteProp = RouteProp< - OnboardingStackParams, - 'RevampedOnboarding' ->; -type RevampedOnboardingNavigationProp = StackNavigationProp< - OnboardingStackParams, - 'RevampedOnboarding' ->; -interface RevampedOnboardingProps { - route: RevampedOnboardingRouteProp; - navigation: RevampedOnboardingNavigationProp; -} - -const RevampedOnboarding: React.FC = ({ route }) => { - const { isPhoneVerified } = route.params; - const navigation = useNavigation(); - type renderStatusType = 'firstName' | 'lastName' | 'username' | 'email'; - const [attemptedSubmit, setAttemptedSubmit] = useState(false); - // TODO: maybe use this? - const [valid, setValid] = useState(false); - const [paswordsMatch, setPassMatch] = useState(false) - const [currentStep, setCurrentStep] = useState(0); - const [tcAccepted, setTCAccepted] = useState(false); - const [passVisibility, setPassVisibility] = useState(false); - const [form, setForm] = useState({ - fname: '', - lname: '', - username: '', - phone: '', - email: '', - password: '', - confirm: '', - // isValidFname: false, - // isValidLname: false, - // isValidPhone: false, - // isValidUser: false, - // isValidEmail: false, - // isValidPassword: false, - // passwordsMatch: false, - token: '', - }); - - const goNext = async () => { - if (!attemptedSubmit) { - setAttemptedSubmit(true); - } - try { - if (valid) { - const { phone } = form; - const code = await sendOtpStatusCode(phone); - if (code) { - switch (code) { - case 200: - const { fname, lname } = form; - navigation.navigate('PhoneVerification', { - firstName: fname, - lastName: lname, - phone, - }); - break; - case 409: - Alert.alert(ERROR_PHONE_IN_USE); - break; - default: - Alert.alert(ERROR_TWILIO_SERVER_ERROR); - } - } else { - setAttemptedSubmit(false); - setTimeout(() => { - setAttemptedSubmit(true); - }); - } - } - } catch (error) { - Alert.alert(ERROR_NEXT_PAGE); - return { - name: 'Navigation error', - description: error, - }; - } - }; - // 0 = first name, 1 = last name, 2 = username, 3 = phone # - const handleNameUpdate = (name: string, nameType: number) => { - name = name.trim(); - let isValidName: boolean = nameRegex.test(name); - switch (nameType) { - case 0: - setForm({ - ...form, - fname: name, - }); - setValid(isValidName) - break; - case 1: - setForm({ - ...form, - lname: name, - }); - setValid(isValidName) - break; - case 2: - setForm({ - ...form, - username: name, - }); - setValid(usernameRegex.test(name)) - break; - } - }; - const handlePhoneUpdate = (phone: string) => { - phone = phone.trim(); - setForm({ - ...form, - phone, - }); - setValid(phoneRegex.test(phone)) - }; - const handleEmailUpdate = (email: string) => { - email = email.trim(); - setForm({ - ...form, - email, - }); - setValid(emailRegex.test(email)) - }; - const handlePasswordUpdate = (password: string) => { - setForm({ - ...form, - password, - }); - setValid(passwordRegex.test(password)); - setPassMatch(form.password === form.confirm) - }; - const handleConfirmUpdate = (confirm: string) => { - let passwordsMatch: boolean = form.password === confirm; - setForm({ - ...form, - confirm, - }); - setPassMatch(form.password === form.confirm) - }; - const handleTcUpdate = (tcAccepted: boolean) => { - setTCAccepted(tcAccepted); - }; - const formSteps: { - placeholder: string; - valid: boolean; - onChangeText: (text: string) => void; - }[] = [ - { - placeholder: 'First Name', - valid: valid, - onChangeText: (text) => handleNameUpdate(text, 0), - }, - { - placeholder: 'Last Name', - valid: valid, - onChangeText: (text) => handleNameUpdate(text, 1), - }, - { - placeholder: 'Phone', - valid: valid, - onChangeText: handlePhoneUpdate, - }, - { - placeholder: 'School Email', - valid: valid, - onChangeText: handleEmailUpdate, - }, - { - placeholder: 'Username', - valid: valid, - onChangeText: (text) => handleNameUpdate(text, 2), - }, - { - placeholder: 'Password', - valid: valid, - onChangeText: handlePasswordUpdate, - }, - // ... - ]; - const resetForm = (formStep: String) => { - console.log(step.placeholder) - console.log(valid) - setValid(false); - console.log("after " + step.valid) - switch (formStep) { - case 'First Name': - setForm({ - ...form, - fname: '' - }) - break; - case 'Last Name': - setForm({ - ...form, - lname: '' - }) - break; - case 'Email': - setForm({ - ...form, - email: '' - }) - break; - case 'Password': - setForm({ - ...form, - password: '' - }) - break; - case 'School Email': - setForm({ - ...form, - email: '' - }) - break; - case 'Username': - setForm({ - ...form, - username: '' - }) - break; - } - } - const advance = () => { - setAttemptedSubmit(true); - console.log("valid? before: " + valid) - if (step.valid) { - setCurrentStep(currentStep + 1); - setAttemptedSubmit(false); - setValid(false); - } - console.log("valid? after: " + valid) - } - const togglePassVisibility = (passBool: boolean) => { - setPassVisibility(passBool); - } - const step = formSteps[currentStep]; - - useEffect(() => { - if (isPhoneVerified) { - advance() - } - }, [isPhoneVerified]); - - return ( - - - {/* getting rid of registration progress in onboarding*/} - {/* */} - - - {(currentStep !== 0 && currentStep !== 3) && ( - { - // if I go back do I want to reset the previous form? - setCurrentStep(currentStep - 1); - resetForm(step.placeholder); - setAttemptedSubmit(false); - }} - /> - )} - - {/* { - console.log('fooo'); - setCurrentStep(currentStep - 1); - }} */} - {/* /> */} - {step.placeholder === 'Phone' && !isPhoneVerified ? ( - <> - - - - ) : ( - <> - {step.placeholder !== 'Password' ? ( - <>SIGN UP - - - - ) : ( - <> - togglePassVisibility(!passVisibility)}> - - Show Password - - - - - )} - - )} - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - height: SCREEN_HEIGHT, - width: SCREEN_WIDTH, - alignItems: 'center', - justifyContent: 'center', - }, - input: { - width: '100%', - minWidth: '60%', - height: 40, - fontSize: 16, - fontWeight: '600', - color: '#fff', - paddingLeft: 13, - borderBottomWidth: 1, - borderBottomColor: '#fff', - }, - button: { - width: 40, - aspectRatio: 10, - }, - formHeader: { - color: '#fff', - fontSize: 30, - fontWeight: '600', - position: 'absolute', - top: '20%', - }, - load: { - top: '5%', - }, - tc: { - marginVertical: '5%', - }, - footer: { - width: '100%', - flexDirection: 'row', - justifyContent: 'space-around', - ...Platform.select({ - ios: { - bottom: '20%', - }, - android: { - bottom: '10%', - }, - }), - }, -}); -export default RevampedOnboarding; diff --git a/src/screens/onboarding/WelcomeScreen.tsx b/src/screens/onboarding/WelcomeScreen.tsx index 358661f7..f0089206 100644 --- a/src/screens/onboarding/WelcomeScreen.tsx +++ b/src/screens/onboarding/WelcomeScreen.tsx @@ -35,7 +35,7 @@ const WelcomeScreen: React.FC = ({navigation}) => { { - navigation.navigate('RevampedOnboarding', {isPhoneVerified: false}); + navigation.navigate('BasicInfoOnboarding', {isPhoneVerified: false}); }} title={'Next'} buttonStyle={'large'} diff --git a/src/screens/onboarding/index.ts b/src/screens/onboarding/index.ts index c0d99dd2..5423c46e 100644 --- a/src/screens/onboarding/index.ts +++ b/src/screens/onboarding/index.ts @@ -1,12 +1,9 @@ export {default as InvitationCodeVerification} from './InvitationCodeVerification'; export {default as Login} from './Login'; -export {default as OnboardingStepOne} from './OnboardingStepOne'; -export {default as OnboardingStepThree} from './OnboardingStepThree'; -export {default as OnboardingStepTwo} from './OnboardingStepTwo'; +export {default as ProfileInfoOnboarding} from './ProfileInfoOnboarding'; +export {default as BasicInfoOnboarding} from './BasicInfoOnboarding'; export {default as PasswordReset} from './PasswordReset'; export {default as PasswordResetRequest} from './PasswordResetRequest'; export {default as PhoneVerification} from './PhoneVerification'; export {default as PasswordVerification} from './PasswordVerification'; export {default as WelcomeScreen} from './WelcomeScreen'; -export {default as RevampedOnboarding} from './RevampedOnboarding'; - diff --git a/src/screens/onboarding/legacy/OnboardingStepOne.tsx b/src/screens/onboarding/legacy/OnboardingStepOne.tsx new file mode 100644 index 00000000..5e88d004 --- /dev/null +++ b/src/screens/onboarding/legacy/OnboardingStepOne.tsx @@ -0,0 +1,286 @@ +import {StackNavigationProp} from '@react-navigation/stack'; +import React, {useMemo, useRef, useState} from 'react'; +import { + Alert, + KeyboardAvoidingView, + Platform, + StatusBar, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import { + ArrowButton, + Background, + BasicButton, + // RegistrationWizard, + TaggInput, + TaggSquareButton, +} from '../../components'; +import {nameRegex, phoneRegex} from '../../constants'; +import { + ERROR_NEXT_PAGE, + ERROR_PHONE_IN_USE, + ERROR_TWILIO_SERVER_ERROR, +} from '../../constants/strings'; +import {OnboardingStackParams} from '../../routes'; +import {sendOtpStatusCode} from '../../services'; +import {BackgroundGradientType} from '../../types'; +import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; + +type OnboardingStepOneNavigationProp = StackNavigationProp< + OnboardingStackParams, + 'OnboardingStepOne' +>; +interface OnboardingStepOneProps { + navigation: OnboardingStepOneNavigationProp; +} + +const OnboardingStepOne: React.FC = ({navigation}) => { + const lnameRef = useRef(); + const emailRef = useRef(); + const phoneRef = useRef(); + + const handleFocusChange = (field: string): void => { + switch (field) { + case 'lname': + const lnameField: any = lnameRef.current; + lnameField.focus(); + break; + case 'email': + const emailField: any = emailRef.current; + emailField.focus(); + break; + case 'phone': + const phoneField: any = phoneRef.current; + phoneField.focus(); + break; + default: + return; + } + }; + + const [form, setForm] = useState({ + fname: '', + lname: '', + phone: '', + isValidFname: false, + isValidLname: false, + isValidPhone: false, + attemptedSubmit: false, + token: '', + }); + + const handleFnameUpdate = (fname: string) => { + fname = fname.trim(); + let isValidFname: boolean = nameRegex.test(fname); + setForm({ + ...form, + fname, + isValidFname, + }); + }; + + const handleLnameUpdate = (lname: string) => { + lname = lname.trim(); + let isValidLname: boolean = nameRegex.test(lname); + setForm({ + ...form, + lname, + isValidLname, + }); + }; + + const handlePhoneUpdate = (phone: string) => { + phone = phone.trim(); + let isValidPhone: boolean = phoneRegex.test(phone); + setForm({ + ...form, + phone, + isValidPhone, + }); + }; + + const goNext = async () => { + if (!form.attemptedSubmit) { + setForm({ + ...form, + attemptedSubmit: true, + }); + } + try { + const {isValidFname} = form; + // const {isValidFname, isValidLname, isValidPhone} = form; + if (isValidFname) { + const {fname} = form; + navigation.navigate('OnboardingStepTwo', { + firstName: fname, + }); + // const {phone} = form; + // const code = await sendOtpStatusCode(phone); + // if (code) { + // switch (code) { + // case 200: + // const {fname, lname} = form; + + // break; + // case 409: + // Alert.alert(ERROR_PHONE_IN_USE); + // break; + // default: + // Alert.alert(ERROR_TWILIO_SERVER_ERROR); + } else { + setForm({...form, attemptedSubmit: false}); + setTimeout(() => setForm({...form, attemptedSubmit: true})); + } + } catch (error) { + Alert.alert(ERROR_NEXT_PAGE); + return { + name: 'Navigation error', + description: error, + }; + } + }; + + const footer = useMemo( + () => ( + + // // + // {/* navigation.navigate('Login')} + // /> */} + // {/* */} + // {/* */} + // {/* */} + // // + ), + [ + form.fname, + // form.lname, + // form.phone, + form.isValidFname, + // form.isValidLname, + // form.isValidPhone, + ], + ); + + return ( + + + {/* getting rid of registration progress in onboarding*/} + {/* */} + + {/* */} + SIGN UP + {/* */} + handleFocusChange('lname')} + blurOnSubmit={false} + valid={form.isValidFname} + invalidWarning="Please enter a valid first name." + attemptedSubmit={form.attemptedSubmit} + width={280} + /> + {/* */} + {/* */} + {footer} + + + ); +}; + +const styles = StyleSheet.create({ + container: { + height: SCREEN_HEIGHT, + width: SCREEN_WIDTH, + alignItems: 'center', + justifyContent: 'center', + }, + button: { + width: 40, + aspectRatio: 10, + }, + formHeader: { + color: '#fff', + fontSize: 30, + fontWeight: '600', + marginBottom: '16%', + position: 'absolute', + top: 0, + marginTop: '45%', + }, + load: { + top: '5%', + }, + footer: { + width: '100%', + flexDirection: 'row', + justifyContent: 'space-around', + ...Platform.select({ + ios: { + bottom: '20%', + }, + android: { + bottom: '10%', + }, + }), + }, +}); + +export default OnboardingStepOne; diff --git a/src/screens/onboarding/legacy/OnboardingStepTwo.tsx b/src/screens/onboarding/legacy/OnboardingStepTwo.tsx new file mode 100644 index 00000000..65f953ea --- /dev/null +++ b/src/screens/onboarding/legacy/OnboardingStepTwo.tsx @@ -0,0 +1,368 @@ +import AsyncStorage from '@react-native-community/async-storage'; +import {RouteProp} from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; +import React, {useMemo, useRef, useState} from 'react'; +import { + Alert, + KeyboardAvoidingView, + Platform, + StatusBar, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import { + Background, + LoadingIndicator, + TaggInput, + TaggSquareButton, + TermsConditions, +} from '../../../components'; +import {emailRegex, passwordRegex, usernameRegex} from '../../../constants'; +import { + ERROR_DOUBLE_CHECK_CONNECTION, + ERROR_REGISTRATION, + ERROR_SOMETHING_WENT_WRONG_REFRESH, +} from '../../../constants/strings'; +import {OnboardingStackParams} from '../../../routes'; +import {sendRegister} from '../../../services'; +import {BackgroundGradientType} from '../../../types'; + +type OnboardingStepTwoRouteProp = RouteProp< + OnboardingStackParams, + 'OnboardingStepTwo' +>; +type OnboardingStepTwoNavigationProp = StackNavigationProp< + OnboardingStackParams, + 'OnboardingStepTwo' +>; +interface OnboardingStepTwoProps { + route: OnboardingStepTwoRouteProp; + navigation: OnboardingStepTwoNavigationProp; +} + +const OnboardingStepTwo: React.FC = ({ + route, + navigation, +}) => { + const emailRef = useRef(); + const usernameRef = useRef(); + const passwordRef = useRef(); + const confirmRef = useRef(); + + const handleFocusChange = (field: string): void => { + switch (field) { + case 'email': + const emailField: any = emailRef.current; + emailField.focus(); + break; + case 'username': + const usernameField: any = usernameRef.current; + usernameField.focus(); + break; + case 'password': + const passwordField: any = passwordRef.current; + passwordField.focus(); + break; + case 'confirm': + const confirmField: any = confirmRef.current; + confirmField.focus(); + break; + default: + return; + } + }; + + // registration form state + const [form, setForm] = useState({ + email: '', + username: '', + password: '', + confirm: '', + isValidEmail: false, + isValidUsername: false, + isValidPassword: false, + passwordsMatch: false, + tcAccepted: false, + attemptedSubmit: false, + }); + + const handleEmailUpdate = (email: string) => { + email = email.trim(); + let isValidEmail: boolean = emailRegex.test(email); + setForm({ + ...form, + email, + isValidEmail, + }); + }; + + const handleUsernameUpdate = (username: string) => { + let isValidUsername: boolean = usernameRegex.test(username); + setForm({ + ...form, + username, + isValidUsername, + }); + }; + + const handlePasswordUpdate = (password: string) => { + let isValidPassword: boolean = passwordRegex.test(password); + let passwordsMatch: boolean = form.password === form.confirm; + setForm({ + ...form, + password, + isValidPassword, + passwordsMatch, + }); + }; + + const handleConfirmUpdate = (confirm: string) => { + let passwordsMatch: boolean = form.password === confirm; + setForm({ + ...form, + confirm, + passwordsMatch, + }); + }; + + const handleTcUpdate = (tcAccepted: boolean) => { + setForm({ + ...form, + tcAccepted, + }); + }; + + const handleRegister = async () => { + if (!form.attemptedSubmit) { + setForm({ + ...form, + attemptedSubmit: true, + }); + } + const { + isValidEmail, + isValidUsername, + isValidPassword, + passwordsMatch, + tcAccepted, + } = form; + try { + if ( + isValidEmail && + isValidUsername && + isValidPassword && + passwordsMatch + ) { + if (tcAccepted) { + const {email, username, password} = form; + const {firstName, lastName, phone} = route.params; + const response = await sendRegister( + firstName, + lastName, + phone, + email, + username, + password, + ); + if (response) { + const data = await response.json(); + const {token, UserID} = data; + switch (response.status) { + case 201: + await AsyncStorage.setItem('token', token); + navigation.navigate('ProfileInfoOnboarding', { + userId: UserID, + username: username, + }); + break; + case 400: + Alert.alert(ERROR_REGISTRATION(data.toLowerCase())); + break; + default: + Alert.alert(ERROR_SOMETHING_WENT_WRONG_REFRESH); + break; + } + } else { + Alert.alert(ERROR_SOMETHING_WENT_WRONG_REFRESH); + } + } else { + Alert.alert( + 'Terms and conditions', + 'You must first agree to the terms and conditions.', + ); + } + } else { + setForm({...form, attemptedSubmit: false}); + setTimeout(() => setForm({...form, attemptedSubmit: true})); + } + } catch (error) { + Alert.alert(ERROR_REGISTRATION(ERROR_DOUBLE_CHECK_CONNECTION)); + return { + name: 'Registration error', + description: error, + }; + } + }; + + const footer = useMemo( + () => ( + + + + + + ), + [ + form.email, + form.username, + form.password, + form.confirm, + form.isValidEmail, + form.isValidUsername, + form.isValidPassword, + form.passwordsMatch, + form.tcAccepted, + ], + ); + + return ( + + + {/* */} + + + SIGN UP + + handleFocusChange('password')} + blurOnSubmit={false} + ref={usernameRef} + valid={form.isValidUsername} + invalidWarning={ + 'Username must beĀ at least 6 characters and contain only alphanumerics.' + } + attemptedSubmit={form.attemptedSubmit} + width={280} + /> + + handleFocusChange('confirm')} + blurOnSubmit={false} + secureTextEntry + ref={passwordRef} + valid={form.isValidPassword} + invalidWarning={ + 'Password must be at least 8 characters & contain at least one of a-z, A-Z, 0-9, and a special character.' + } + attemptedSubmit={form.attemptedSubmit} + width={280} + /> + + + + + {footer} + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + // wizard: { + // position: 'absolute', + // top: SCREEN_HEIGHT * 0.1, + // }, + formHeader: { + color: '#fff', + fontSize: 30, + fontWeight: '600', + marginBottom: '16%', + }, + tc: { + marginVertical: '5%', + }, + load: { + top: '5%', + }, + footer: { + width: '100%', + flexDirection: 'row', + justifyContent: 'space-around', + ...Platform.select({ + ios: { + bottom: '20%', + }, + android: { + bottom: '10%', + }, + }), + }, +}); + +export default OnboardingStepTwo; -- cgit v1.2.3-70-g09d2 From 81469b7527553622fd66cf12cc194616c55253c1 Mon Sep 17 00:00:00 2001 From: George Rusu Date: Fri, 7 May 2021 11:28:49 -0700 Subject: updated cursor color in TaggInput --- src/screens/onboarding/BasicInfoOnboarding.tsx | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/screens/onboarding/BasicInfoOnboarding.tsx b/src/screens/onboarding/BasicInfoOnboarding.tsx index 25a5a6f8..103abc5c 100644 --- a/src/screens/onboarding/BasicInfoOnboarding.tsx +++ b/src/screens/onboarding/BasicInfoOnboarding.tsx @@ -318,6 +318,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { accessibilityLabel="Phone number input field." placeholder="Phone Number" autoCompleteType="tel" + selectionColor='white' textContentType="telephoneNumber" autoCapitalize="none" keyboardType="number-pad" @@ -353,6 +354,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { autoCapitalize="none" textContentType="name" returnKeyType="done" + selectionColor='white' onChangeText={step.onChangeText} onSubmitEditing={advance} autoFocus={true} @@ -379,6 +381,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { autoCompleteType="password" textContentType="oneTimeCode" returnKeyType="next" + selectionColor='white' onChangeText={handlePasswordUpdate} onSubmitEditing={advanceRegistration} blurOnSubmit={false} -- cgit v1.2.3-70-g09d2 From 1b385f90bc767429971d1748299f33ac542fe429 Mon Sep 17 00:00:00 2001 From: George Rusu Date: Fri, 7 May 2021 13:11:21 -0700 Subject: updated TaggInput to take externalStyles and updated error message color in onboarding --- src/components/onboarding/TaggInput.tsx | 5 +- src/screens/onboarding/BasicInfoOnboarding.tsx | 124 ++++++++++++++++--------- 2 files changed, 83 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/components/onboarding/TaggInput.tsx b/src/components/onboarding/TaggInput.tsx index 405564ab..657ce822 100644 --- a/src/components/onboarding/TaggInput.tsx +++ b/src/components/onboarding/TaggInput.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {View, TextInput, StyleSheet, TextInputProps} from 'react-native'; +import {View, TextInput, StyleSheet, TextInputProps, ViewStyle, StyleProp} from 'react-native'; import * as Animatable from 'react-native-animatable'; import {TAGG_LIGHT_PURPLE} from '../../constants'; @@ -7,6 +7,7 @@ interface TaggInputProps extends TextInputProps { valid?: boolean; invalidWarning?: string; attemptedSubmit?: boolean; + externalStyles?: Record> } /** * An input component that receives all props a normal TextInput component does. TaggInput components grow to 60% of their parent's width by default, but this can be set using the `width` prop. @@ -25,7 +26,7 @@ const TaggInput = React.forwardRef((props: TaggInputProps, ref: any) => { + style={[styles.warning, props.externalStyles?.warning]}> {props.invalidWarning} )} diff --git a/src/screens/onboarding/BasicInfoOnboarding.tsx b/src/screens/onboarding/BasicInfoOnboarding.tsx index 103abc5c..88736340 100644 --- a/src/screens/onboarding/BasicInfoOnboarding.tsx +++ b/src/screens/onboarding/BasicInfoOnboarding.tsx @@ -1,8 +1,8 @@ import AsyncStorage from '@react-native-community/async-storage'; -import {useNavigation} from '@react-navigation/core'; -import {RouteProp} from '@react-navigation/native'; -import {StackNavigationProp} from '@react-navigation/stack'; -import React, {useEffect, useState} from 'react'; +import { useNavigation } from '@react-navigation/core'; +import { RouteProp } from '@react-navigation/native'; +import { StackNavigationProp } from '@react-navigation/stack'; +import React, { useEffect, useState } from 'react'; import { Alert, KeyboardAvoidingView, @@ -36,10 +36,10 @@ import { ERROR_TWILIO_SERVER_ERROR, ERROR_T_AND_C_NOT_ACCEPTED, } from '../../constants/strings'; -import {OnboardingStackParams} from '../../routes'; -import {sendOtpStatusCode, sendRegister} from '../../services'; -import {BackgroundGradientType} from '../../types'; -import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +import { OnboardingStackParams } from '../../routes'; +import { sendOtpStatusCode, sendRegister } from '../../services'; +import { BackgroundGradientType } from '../../types'; +import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../utils'; type BasicInfoOnboardingRouteProp = RouteProp< OnboardingStackParams, @@ -54,14 +54,15 @@ interface BasicInfoOnboardingProps { navigation: BasicInfoOnboardingNavigationProp; } -const BasicInfoOnboarding: React.FC = ({route}) => { - const {isPhoneVerified} = route.params; +const BasicInfoOnboarding: React.FC = ({ route }) => { + const { isPhoneVerified } = route.params; const navigation = useNavigation(); const [attemptedSubmit, setAttemptedSubmit] = useState(false); const [valid, setValid] = useState(false); const [currentStep, setCurrentStep] = useState(0); const [tcAccepted, setTCAccepted] = useState(false); const [passVisibility, setPassVisibility] = useState(false); + const [autoCapitalize, setAutoCap] = useState('None') const [form, setForm] = useState({ fname: '', lname: '', @@ -77,12 +78,13 @@ const BasicInfoOnboarding: React.FC = ({route}) => { } try { if (valid) { - const {phone} = form; - const code = await sendOtpStatusCode(phone); + const { phone } = form; + const code = 200; + //await sendOtpStatusCode(phone); if (code) { switch (code) { case 200: - const {fname, lname} = form; + const { fname, lname } = form; navigation.navigate('PhoneVerification', { firstName: fname, lastName: lname, @@ -120,6 +122,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { ...form, fname: name, }); + setAutoCap('words'); setValid(isValidName); break; case 1: @@ -127,6 +130,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { ...form, lname: name, }); + setAutoCap('words'); setValid(isValidName); break; case 2: @@ -135,6 +139,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { username: name, }); setValid(usernameRegex.test(name)); + setAutoCap('None'); break; } }; @@ -144,6 +149,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { ...form, phone, }); + setAutoCap('None'); setValid(phoneRegex.test(phone)); }; const handleEmailUpdate = (email: string) => { @@ -152,6 +158,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { ...form, email, }); + setAutoCap('None'); setValid(emailRegex.test(email)); }; const handlePasswordUpdate = (password: string) => { @@ -159,37 +166,38 @@ const BasicInfoOnboarding: React.FC = ({route}) => { ...form, password, }); + setAutoCap('None'); setValid(passwordRegex.test(password)); }; const formSteps: { placeholder: string; onChangeText: (text: string) => void; }[] = [ - { - placeholder: 'First Name', - onChangeText: (text) => handleNameUpdate(text, 0), - }, - { - placeholder: 'Last Name', - onChangeText: (text) => handleNameUpdate(text, 1), - }, - { - placeholder: 'Phone', - onChangeText: handlePhoneUpdate, - }, - { - placeholder: 'School Email', - onChangeText: handleEmailUpdate, - }, - { - placeholder: 'Username', - onChangeText: (text) => handleNameUpdate(text, 2), - }, - { - placeholder: 'Password', - onChangeText: handlePasswordUpdate, - }, - ]; + { + placeholder: 'First Name', + onChangeText: (text) => handleNameUpdate(text, 0), + }, + { + placeholder: 'Last Name', + onChangeText: (text) => handleNameUpdate(text, 1), + }, + { + placeholder: 'Phone', + onChangeText: handlePhoneUpdate, + }, + { + placeholder: 'School Email', + onChangeText: handleEmailUpdate, + }, + { + placeholder: 'Username', + onChangeText: (text) => handleNameUpdate(text, 2), + }, + { + placeholder: 'Password', + onChangeText: handlePasswordUpdate, + }, + ]; const resetForm = (formStep: String) => { setValid(false); switch (formStep) { @@ -249,7 +257,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { Alert.alert('Terms and conditions', ERROR_T_AND_C_NOT_ACCEPTED); return; } - const {fname, lname, phone, email, username, password} = form; + const { fname, lname, phone, email, username, password } = form; const response = await sendRegister( fname, lname, @@ -260,7 +268,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { ); if (response) { const data = await response.json(); - const {token, UserID} = data; + const { token, UserID } = data; switch (response.status) { case 201: await AsyncStorage.setItem('token', token); @@ -321,6 +329,9 @@ const BasicInfoOnboarding: React.FC = ({route}) => { selectionColor='white' textContentType="telephoneNumber" autoCapitalize="none" + externalStyles = {{ + warning: styles.passWarning + }} keyboardType="number-pad" onChangeText={handlePhoneUpdate} autoFocus={true} @@ -349,9 +360,9 @@ const BasicInfoOnboarding: React.FC = ({route}) => { style={styles.input} accessibilityHint={`Enter your ${step.placeholder.toLowerCase()}`} accessibilityLabel={`${step.placeholder} input field.`} - placeholder={currentStep + ' ' + step.placeholder} + placeholder={step.placeholder} autoCompleteType="name" - autoCapitalize="none" + autoCapitalize="words" textContentType="name" returnKeyType="done" selectionColor='white' @@ -359,6 +370,9 @@ const BasicInfoOnboarding: React.FC = ({route}) => { onSubmitEditing={advance} autoFocus={true} blurOnSubmit={false} + externalStyles = {{ + warning: styles.passWarning + }} valid={valid} invalidWarning={`Please enter a valid ${step.placeholder.toLowerCase()}`} attemptedSubmit={attemptedSubmit} @@ -394,8 +408,10 @@ const BasicInfoOnboarding: React.FC = ({route}) => { style={styles.input} /> setPassVisibility(!passVisibility)}> - Show Password + Show Password Date: Fri, 7 May 2021 13:53:58 -0700 Subject: added new Arrow, working on transition animation --- src/components/onboarding/ArrowButton.tsx | 5 ++- src/components/onboarding/TaggInput.tsx | 2 +- src/screens/onboarding/BasicInfoOnboarding.tsx | 44 ++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/components/onboarding/ArrowButton.tsx b/src/components/onboarding/ArrowButton.tsx index bf07c6ac..57ebc774 100644 --- a/src/components/onboarding/ArrowButton.tsx +++ b/src/components/onboarding/ArrowButton.tsx @@ -4,6 +4,7 @@ import {Image, TouchableOpacity, TouchableOpacityProps} from 'react-native'; interface ArrowButtonProps extends TouchableOpacityProps { direction: 'forward' | 'backward'; disabled?: boolean; + onboarding?: boolean } const ArrowButton: React.FC = (props: ArrowButtonProps) => { const arrow = @@ -11,7 +12,9 @@ const ArrowButton: React.FC = (props: ArrowButtonProps) => { ? props.disabled ? require('../../assets/images/arrow-forward-disabled.png') : require('../../assets/images/arrow-forward-enabled.png') - : require('../../assets/images/arrow-backward.png'); + : require('../../assets/images/arrow-backward.png') + ? props.onboarding + : require ('../../assets/images/onboarding-arrow.png') return ( diff --git a/src/components/onboarding/TaggInput.tsx b/src/components/onboarding/TaggInput.tsx index 657ce822..d52117b0 100644 --- a/src/components/onboarding/TaggInput.tsx +++ b/src/components/onboarding/TaggInput.tsx @@ -16,7 +16,7 @@ const TaggInput = React.forwardRef((props: TaggInputProps, ref: any) => { return ( = ({ route }) => { const [tcAccepted, setTCAccepted] = useState(false); const [passVisibility, setPassVisibility] = useState(false); const [autoCapitalize, setAutoCap] = useState('None') + const [fadeIn, setFadeIn] = useState(new Animated.Value(0)) const [form, setForm] = useState({ fname: '', lname: '', @@ -71,6 +75,15 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { email: '', password: '', }); + const fadeAnim = useRef(new Animated.Value(0)).current; + + const fadeCopmponentIn = () => { + // Will change fadeAnim value to 1 in 5 seconds + Animated.timing(fadeAnim, { + toValue: 1, + duration: 5000 + }).start(); + }; const goNext = async () => { if (!attemptedSubmit) { @@ -308,6 +321,7 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { {currentStep !== 0 && currentStep !== 3 && ( { // if I go back do I want to reset the previous form? @@ -330,7 +344,7 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { textContentType="telephoneNumber" autoCapitalize="none" externalStyles = {{ - warning: styles.passWarning + warning: styles.passWarning, }} keyboardType="number-pad" onChangeText={handlePhoneUpdate} @@ -371,7 +385,7 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { autoFocus={true} blurOnSubmit={false} externalStyles = {{ - warning: styles.passWarning + warning: styles.passWarning, }} valid={valid} invalidWarning={`Please enter a valid ${step.placeholder.toLowerCase()}`} @@ -401,6 +415,9 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { blurOnSubmit={false} secureTextEntry={!passVisibility} valid={valid} + externalStyles = {{ + warning: styles.passWarning, + }} invalidWarning={ 'Password must be at least 8 characters & contain at least one of a-z, A-Z, 0-9, and a special character.' } @@ -419,6 +436,13 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { accepted={tcAccepted} onChange={setTCAccepted} /> + )} @@ -440,18 +464,17 @@ const styles = StyleSheet.create({ }, showPass: { color: 'white', - alignSelf: 'flex-start', marginVertical: '1%', borderBottomWidth: 1, paddingBottom: '1%', - left: '3%', + left: '-18%', borderBottomColor: 'white', marginBottom: '8%', }, passWarning: { fontSize: 14, marginTop: 5, - color: 'red', + color: '#FF8989', maxWidth: 350, alignSelf: 'flex-start' }, @@ -464,6 +487,15 @@ const styles = StyleSheet.create({ borderBottomWidth: 1, borderBottomColor: '#fff', }, + errorInput: { + minWidth: '60%', + height: 40, + fontSize: 16, + fontWeight: '600', + color: '#FF8989', + borderBottomWidth: 1, + borderBottomColor: '#fff', + }, button: { alignItems: 'center', width: 40, -- cgit v1.2.3-70-g09d2 From e1328426609011d21fcba9634e536a26396d13b0 Mon Sep 17 00:00:00 2001 From: George Rusu Date: Fri, 7 May 2021 13:57:01 -0700 Subject: uploaded new arrow png --- src/assets/images/onboarding-arrow.png | Bin 0 -> 385 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/assets/images/onboarding-arrow.png (limited to 'src') diff --git a/src/assets/images/onboarding-arrow.png b/src/assets/images/onboarding-arrow.png new file mode 100644 index 00000000..8cca6502 Binary files /dev/null and b/src/assets/images/onboarding-arrow.png differ -- cgit v1.2.3-70-g09d2 From 1b015ed2a6fd4c16b2b79a771a9ee66f04a55d7a Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 7 May 2021 17:41:01 -0400 Subject: fixed animation --- src/screens/onboarding/BasicInfoOnboarding.tsx | 156 +++++++++++++------------ 1 file changed, 81 insertions(+), 75 deletions(-) (limited to 'src') diff --git a/src/screens/onboarding/BasicInfoOnboarding.tsx b/src/screens/onboarding/BasicInfoOnboarding.tsx index e53411ae..277331b2 100644 --- a/src/screens/onboarding/BasicInfoOnboarding.tsx +++ b/src/screens/onboarding/BasicInfoOnboarding.tsx @@ -1,9 +1,8 @@ import AsyncStorage from '@react-native-community/async-storage'; -import { useNavigation } from '@react-navigation/core'; -import { RouteProp } from '@react-navigation/native'; -import { StackNavigationProp } from '@react-navigation/stack'; -import { defineLocale } from 'moment'; -import React, { useEffect, useRef, useState } from 'react'; +import {useNavigation} from '@react-navigation/core'; +import {RouteProp} from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; +import React, {useEffect, useState} from 'react'; import { Alert, KeyboardAvoidingView, @@ -14,8 +13,8 @@ import { TouchableOpacity, View, } from 'react-native'; -import { createAnimatableComponent } from 'react-native-animatable'; -import Animated from 'react-native-reanimated'; +import {normalize} from 'react-native-elements'; +import Animated, {Easing} from 'react-native-reanimated'; import { ArrowButton, Background, @@ -39,10 +38,10 @@ import { ERROR_TWILIO_SERVER_ERROR, ERROR_T_AND_C_NOT_ACCEPTED, } from '../../constants/strings'; -import { OnboardingStackParams } from '../../routes'; -import { sendOtpStatusCode, sendRegister } from '../../services'; -import { BackgroundGradientType } from '../../types'; -import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../utils'; +import {OnboardingStackParams} from '../../routes'; +import {sendOtpStatusCode, sendRegister} from '../../services'; +import {BackgroundGradientType} from '../../types'; +import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; type BasicInfoOnboardingRouteProp = RouteProp< OnboardingStackParams, @@ -57,16 +56,18 @@ interface BasicInfoOnboardingProps { navigation: BasicInfoOnboardingNavigationProp; } -const BasicInfoOnboarding: React.FC = ({ route }) => { - const { isPhoneVerified } = route.params; +const BasicInfoOnboarding: React.FC = ({route}) => { + const {isPhoneVerified} = route.params; const navigation = useNavigation(); const [attemptedSubmit, setAttemptedSubmit] = useState(false); const [valid, setValid] = useState(false); const [currentStep, setCurrentStep] = useState(0); const [tcAccepted, setTCAccepted] = useState(false); const [passVisibility, setPassVisibility] = useState(false); - const [autoCapitalize, setAutoCap] = useState('None') - const [fadeIn, setFadeIn] = useState(new Animated.Value(0)) + const [autoCapitalize, setAutoCap] = useState('None'); + const [fadeValue, setFadeValue] = useState>( + new Animated.Value(0), + ); const [form, setForm] = useState({ fname: '', lname: '', @@ -75,15 +76,18 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { email: '', password: '', }); - const fadeAnim = useRef(new Animated.Value(0)).current; - const fadeCopmponentIn = () => { - // Will change fadeAnim value to 1 in 5 seconds - Animated.timing(fadeAnim, { - toValue: 1, - duration: 5000 - }).start(); - }; + const fadeComponentIn = async () => { + Animated.timing(fadeValue, { + toValue: 1, + duration: 1000, + easing: Easing.linear, + }).start(); + }; + + useEffect(() => { + fadeComponentIn(); + }, [fadeValue]); const goNext = async () => { if (!attemptedSubmit) { @@ -91,13 +95,12 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { } try { if (valid) { - const { phone } = form; - const code = 200; - //await sendOtpStatusCode(phone); + const {phone} = form; + const code = await sendOtpStatusCode(phone); if (code) { switch (code) { case 200: - const { fname, lname } = form; + const {fname, lname} = form; navigation.navigate('PhoneVerification', { firstName: fname, lastName: lname, @@ -186,31 +189,31 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { placeholder: string; onChangeText: (text: string) => void; }[] = [ - { - placeholder: 'First Name', - onChangeText: (text) => handleNameUpdate(text, 0), - }, - { - placeholder: 'Last Name', - onChangeText: (text) => handleNameUpdate(text, 1), - }, - { - placeholder: 'Phone', - onChangeText: handlePhoneUpdate, - }, - { - placeholder: 'School Email', - onChangeText: handleEmailUpdate, - }, - { - placeholder: 'Username', - onChangeText: (text) => handleNameUpdate(text, 2), - }, - { - placeholder: 'Password', - onChangeText: handlePasswordUpdate, - }, - ]; + { + placeholder: 'First Name', + onChangeText: (text) => handleNameUpdate(text, 0), + }, + { + placeholder: 'Last Name', + onChangeText: (text) => handleNameUpdate(text, 1), + }, + { + placeholder: 'Phone', + onChangeText: handlePhoneUpdate, + }, + { + placeholder: 'School Email', + onChangeText: handleEmailUpdate, + }, + { + placeholder: 'Username', + onChangeText: (text) => handleNameUpdate(text, 2), + }, + { + placeholder: 'Password', + onChangeText: handlePasswordUpdate, + }, + ]; const resetForm = (formStep: String) => { setValid(false); switch (formStep) { @@ -259,6 +262,7 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { setCurrentStep(currentStep + 1); setAttemptedSubmit(false); setValid(false); + setFadeValue(new Animated.Value(0)); } }; const advanceRegistration = async () => { @@ -270,7 +274,7 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { Alert.alert('Terms and conditions', ERROR_T_AND_C_NOT_ACCEPTED); return; } - const { fname, lname, phone, email, username, password } = form; + const {fname, lname, phone, email, username, password} = form; const response = await sendRegister( fname, lname, @@ -281,7 +285,7 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { ); if (response) { const data = await response.json(); - const { token, UserID } = data; + const {token, UserID} = data; switch (response.status) { case 201: await AsyncStorage.setItem('token', token); @@ -321,8 +325,9 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { {currentStep !== 0 && currentStep !== 3 && ( { // if I go back do I want to reset the previous form? setCurrentStep(currentStep - 1); @@ -333,17 +338,17 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { )} {step.placeholder === 'Phone' && !isPhoneVerified ? ( - <> + = ({ route }) => { buttonColor={'white'} labelColor={'blue'} /> - + ) : ( <> {step.placeholder !== 'Password' ? ( <> SIGN UP - + = ({ route }) => { autoCapitalize="words" textContentType="name" returnKeyType="done" - selectionColor='white' + selectionColor="white" onChangeText={step.onChangeText} onSubmitEditing={advance} autoFocus={true} blurOnSubmit={false} - externalStyles = {{ + externalStyles={{ warning: styles.passWarning, }} valid={valid} @@ -398,10 +403,10 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { buttonColor={'white'} labelColor={'blue'} /> - + ) : ( - <> + = ({ route }) => { autoCompleteType="password" textContentType="oneTimeCode" returnKeyType="next" - selectionColor='white' + selectionColor="white" onChangeText={handlePasswordUpdate} onSubmitEditing={advanceRegistration} blurOnSubmit={false} secureTextEntry={!passVisibility} valid={valid} - externalStyles = {{ + externalStyles={{ warning: styles.passWarning, }} invalidWarning={ @@ -437,13 +442,13 @@ const BasicInfoOnboarding: React.FC = ({ route }) => { onChange={setTCAccepted} /> - + onPress={advanceRegistration} + title={'Next'} + buttonStyle={'normal'} + buttonColor={'white'} + labelColor={'blue'} + /> + )} )} @@ -476,7 +481,7 @@ const styles = StyleSheet.create({ marginTop: 5, color: '#FF8989', maxWidth: 350, - alignSelf: 'flex-start' + alignSelf: 'flex-start', }, input: { minWidth: '60%', @@ -515,7 +520,8 @@ const styles = StyleSheet.create({ marginVertical: '5%', }, footer: { - width: '100%', + height: normalize(50), + width: normalize(50), flexDirection: 'row', justifyContent: 'space-around', ...Platform.select({ -- cgit v1.2.3-70-g09d2 From 8c20f573c29e9e42046aeeefbc786ca30aa74e68 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 7 May 2021 19:06:53 -0400 Subject: added arrow button asset --- src/assets/images/onboarding-arrow.png | Bin 385 -> 0 bytes src/assets/images/onboarding-backarrow.png | Bin 0 -> 2036 bytes src/components/onboarding/ArrowButton.tsx | 44 +++++++++++++++++++++-------- src/components/onboarding/TaggInput.tsx | 11 ++++++-- 4 files changed, 41 insertions(+), 14 deletions(-) delete mode 100644 src/assets/images/onboarding-arrow.png create mode 100644 src/assets/images/onboarding-backarrow.png (limited to 'src') diff --git a/src/assets/images/onboarding-arrow.png b/src/assets/images/onboarding-arrow.png deleted file mode 100644 index 8cca6502..00000000 Binary files a/src/assets/images/onboarding-arrow.png and /dev/null differ diff --git a/src/assets/images/onboarding-backarrow.png b/src/assets/images/onboarding-backarrow.png new file mode 100644 index 00000000..9aaa66cb Binary files /dev/null and b/src/assets/images/onboarding-backarrow.png differ diff --git a/src/components/onboarding/ArrowButton.tsx b/src/components/onboarding/ArrowButton.tsx index 57ebc774..78dbab32 100644 --- a/src/components/onboarding/ArrowButton.tsx +++ b/src/components/onboarding/ArrowButton.tsx @@ -1,26 +1,46 @@ import React from 'react'; -import {Image, TouchableOpacity, TouchableOpacityProps} from 'react-native'; +import { + Image, + StyleSheet, + TouchableOpacity, + TouchableOpacityProps, +} from 'react-native'; interface ArrowButtonProps extends TouchableOpacityProps { direction: 'forward' | 'backward'; disabled?: boolean; - onboarding?: boolean + onboarding?: boolean; } -const ArrowButton: React.FC = (props: ArrowButtonProps) => { - const arrow = - props.direction === 'forward' - ? props.disabled - ? require('../../assets/images/arrow-forward-disabled.png') - : require('../../assets/images/arrow-forward-enabled.png') - : require('../../assets/images/arrow-backward.png') - ? props.onboarding - : require ('../../assets/images/onboarding-arrow.png') +const ArrowButton: React.FC = (props) => { + const {direction, disabled, onboarding} = props; + let uri; + + if (direction === 'forward') { + if (disabled) { + uri = require('../../assets/images/arrow-forward-disabled.png'); + } else { + uri = require('../../assets/images/arrow-forward-enabled.png'); + } + } else { + if (onboarding) { + uri = require('../../assets/images/onboarding-backarrow.png'); + } else { + uri = require('../../assets/images/arrow-backward.png'); + } + } return ( - + ); }; +const styles = StyleSheet.create({ + image: { + // width: '100%', + // height: '100%', + }, +}); + export default ArrowButton; diff --git a/src/components/onboarding/TaggInput.tsx b/src/components/onboarding/TaggInput.tsx index d52117b0..297392e5 100644 --- a/src/components/onboarding/TaggInput.tsx +++ b/src/components/onboarding/TaggInput.tsx @@ -1,5 +1,12 @@ import React from 'react'; -import {View, TextInput, StyleSheet, TextInputProps, ViewStyle, StyleProp} from 'react-native'; +import { + View, + TextInput, + StyleSheet, + TextInputProps, + ViewStyle, + StyleProp, +} from 'react-native'; import * as Animatable from 'react-native-animatable'; import {TAGG_LIGHT_PURPLE} from '../../constants'; @@ -7,7 +14,7 @@ interface TaggInputProps extends TextInputProps { valid?: boolean; invalidWarning?: string; attemptedSubmit?: boolean; - externalStyles?: Record> + externalStyles?: Record>; } /** * An input component that receives all props a normal TextInput component does. TaggInput components grow to 60% of their parent's width by default, but this can be set using the `width` prop. -- cgit v1.2.3-70-g09d2 From 67981afac39be67de4fcae97826cee435ab1dc29 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 7 May 2021 19:17:37 -0400 Subject: updated styling --- src/components/onboarding/ArrowButton.tsx | 4 +- src/screens/onboarding/BasicInfoOnboarding.tsx | 216 ++++++++++++++----------- src/screens/onboarding/PhoneVerification.tsx | 37 ++--- 3 files changed, 143 insertions(+), 114 deletions(-) (limited to 'src') diff --git a/src/components/onboarding/ArrowButton.tsx b/src/components/onboarding/ArrowButton.tsx index 78dbab32..dcf559a8 100644 --- a/src/components/onboarding/ArrowButton.tsx +++ b/src/components/onboarding/ArrowButton.tsx @@ -38,8 +38,8 @@ const ArrowButton: React.FC = (props) => { const styles = StyleSheet.create({ image: { - // width: '100%', - // height: '100%', + width: '100%', + height: '100%', }, }); diff --git a/src/screens/onboarding/BasicInfoOnboarding.tsx b/src/screens/onboarding/BasicInfoOnboarding.tsx index 277331b2..2152ffb9 100644 --- a/src/screens/onboarding/BasicInfoOnboarding.tsx +++ b/src/screens/onboarding/BasicInfoOnboarding.tsx @@ -11,10 +11,9 @@ import { StyleSheet, Text, TouchableOpacity, - View, } from 'react-native'; import {normalize} from 'react-native-elements'; -import Animated, {Easing} from 'react-native-reanimated'; +import Animated, {Easing, useValue} from 'react-native-reanimated'; import { ArrowButton, Background, @@ -41,7 +40,7 @@ import { import {OnboardingStackParams} from '../../routes'; import {sendOtpStatusCode, sendRegister} from '../../services'; import {BackgroundGradientType} from '../../types'; -import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +import {HeaderHeight, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; type BasicInfoOnboardingRouteProp = RouteProp< OnboardingStackParams, @@ -68,6 +67,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { const [fadeValue, setFadeValue] = useState>( new Animated.Value(0), ); + const fadeButtonValue = useValue(0); const [form, setForm] = useState({ fname: '', lname: '', @@ -77,19 +77,38 @@ const BasicInfoOnboarding: React.FC = ({route}) => { password: '', }); - const fadeComponentIn = async () => { - Animated.timing(fadeValue, { - toValue: 1, - duration: 1000, + const fadeFormIn = () => { + setFadeValue(new Animated.Value(0)); + }; + + const fadeButtonTo = (target: number) => { + Animated.timing(fadeButtonValue, { + toValue: target, + duration: 100, easing: Easing.linear, }).start(); }; useEffect(() => { - fadeComponentIn(); + const fade = async () => { + Animated.timing(fadeValue, { + toValue: 1, + duration: 1000, + easing: Easing.linear, + }).start(); + }; + fade(); }, [fadeValue]); - const goNext = async () => { + useEffect(() => { + if (valid) { + fadeButtonTo(1); + } else { + fadeButtonTo(0); + } + }, [valid]); + + const goToPhoneVerification = async () => { if (!attemptedSubmit) { setAttemptedSubmit(true); } @@ -262,7 +281,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { setCurrentStep(currentStep + 1); setAttemptedSubmit(false); setValid(false); - setFadeValue(new Animated.Value(0)); + fadeFormIn(); } }; const advanceRegistration = async () => { @@ -317,63 +336,70 @@ const BasicInfoOnboarding: React.FC = ({route}) => { style={styles.container} gradientType={BackgroundGradientType.Light}> - {/* getting rid of registration progress in onboarding*/} - {/* */} - - {currentStep !== 0 && currentStep !== 3 && ( - { - // if I go back do I want to reset the previous form? - setCurrentStep(currentStep - 1); - resetForm(step.placeholder); - setAttemptedSubmit(false); - }} - /> - )} - + {currentStep !== 0 && currentStep !== 3 && ( + { + // if I go back do I want to reset the previous form? + setCurrentStep(currentStep - 1); + resetForm(step.placeholder); + setAttemptedSubmit(false); + setFadeValue(new Animated.Value(0)); + }} + /> + )} {step.placeholder === 'Phone' && !isPhoneVerified ? ( - - - - + <> + + PHONE NUMBER + + + + + + + + ) : ( <> {step.placeholder !== 'Password' ? ( <> SIGN UP - + = ({route}) => { invalidWarning={`Please enter a valid ${step.placeholder.toLowerCase()}`} attemptedSubmit={attemptedSubmit} /> - + + + ) : ( - + = ({route}) => { setPassVisibility(!passVisibility)}> Show Password @@ -441,13 +471,15 @@ const BasicInfoOnboarding: React.FC = ({route}) => { accepted={tcAccepted} onChange={setTCAccepted} /> - + + + )} @@ -464,18 +496,27 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, + formContainer: { + marginTop: '20%', + height: SCREEN_HEIGHT * 0.2, + width: '80%', + justifyContent: 'space-between', + alignItems: 'center', + }, arrow: { color: '#6EE7E7', }, - showPass: { - color: 'white', + showPassContainer: { marginVertical: '1%', borderBottomWidth: 1, paddingBottom: '1%', - left: '-18%', + alignSelf: 'flex-start', borderBottomColor: 'white', marginBottom: '8%', }, + showPass: { + color: 'white', + }, passWarning: { fontSize: 14, marginTop: 5, @@ -484,7 +525,7 @@ const styles = StyleSheet.create({ alignSelf: 'flex-start', }, input: { - minWidth: '60%', + minWidth: '100%', height: 40, fontSize: 16, fontWeight: '600', @@ -511,7 +552,7 @@ const styles = StyleSheet.create({ fontSize: 30, fontWeight: '600', position: 'absolute', - top: '20%', + top: SCREEN_HEIGHT * 0.15, }, load: { top: '5%', @@ -519,19 +560,12 @@ const styles = StyleSheet.create({ tc: { marginVertical: '5%', }, - footer: { - height: normalize(50), - width: normalize(50), - flexDirection: 'row', - justifyContent: 'space-around', - ...Platform.select({ - ios: { - bottom: '20%', - }, - android: { - bottom: '10%', - }, - }), + backArrow: { + width: normalize(29), + height: normalize(25), + position: 'absolute', + top: HeaderHeight * 1.5, + left: 20, }, }); export default BasicInfoOnboarding; diff --git a/src/screens/onboarding/PhoneVerification.tsx b/src/screens/onboarding/PhoneVerification.tsx index 93ae8fdc..7d9a04d8 100644 --- a/src/screens/onboarding/PhoneVerification.tsx +++ b/src/screens/onboarding/PhoneVerification.tsx @@ -4,7 +4,6 @@ import React, {useMemo} from 'react'; import { Alert, KeyboardAvoidingView, - Platform, StyleSheet, TouchableOpacity, View, @@ -16,6 +15,7 @@ import { useBlurOnFulfill, useClearByFocusCell, } from 'react-native-confirmation-code-field'; +import {normalize} from 'react-native-elements'; import {trackPromise} from 'react-promise-tracker'; import { ArrowButton, @@ -31,6 +31,7 @@ import { import {OnboardingStackParams} from '../../routes'; import {sendOtp, verifyOtp} from '../../services'; import {BackgroundGradientType} from '../../types'; +import {HeaderHeight} from '../../utils'; type PhoneVerificationRouteProp = RouteProp< OnboardingStackParams, @@ -75,14 +76,14 @@ const PhoneVerification: React.FC = ({ const footer = useMemo( () => ( - - - navigation.navigate('BasicInfoOnboarding', {isPhoneVerified: false}) - } - /> - + + navigation.navigate('BasicInfoOnboarding', {isPhoneVerified: false}) + } + /> ), [], ); @@ -201,18 +202,12 @@ const styles = StyleSheet.create({ loadingIndicator: { marginVertical: '5%', }, - footer: { - width: '100%', - flexDirection: 'row', - justifyContent: 'space-around', - ...Platform.select({ - ios: { - bottom: '20%', - }, - android: { - bottom: '10%', - }, - }), + backArrow: { + width: normalize(29), + height: normalize(25), + position: 'absolute', + top: HeaderHeight * 1.5, + left: 20, }, }); export default PhoneVerification; -- cgit v1.2.3-70-g09d2 From c6e1baa36d09a9a93eb67a6533fbb3b13b7350e9 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 7 May 2021 19:25:15 -0400 Subject: set auto cap --- src/screens/onboarding/BasicInfoOnboarding.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/screens/onboarding/BasicInfoOnboarding.tsx b/src/screens/onboarding/BasicInfoOnboarding.tsx index 2152ffb9..25a48985 100644 --- a/src/screens/onboarding/BasicInfoOnboarding.tsx +++ b/src/screens/onboarding/BasicInfoOnboarding.tsx @@ -63,7 +63,9 @@ const BasicInfoOnboarding: React.FC = ({route}) => { const [currentStep, setCurrentStep] = useState(0); const [tcAccepted, setTCAccepted] = useState(false); const [passVisibility, setPassVisibility] = useState(false); - const [autoCapitalize, setAutoCap] = useState('None'); + const [autoCapitalize, setAutoCap] = useState< + 'none' | 'sentences' | 'words' | 'characters' | undefined + >('none'); const [fadeValue, setFadeValue] = useState>( new Animated.Value(0), ); @@ -174,7 +176,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { username: name, }); setValid(usernameRegex.test(name)); - setAutoCap('None'); + setAutoCap('none'); break; } }; @@ -184,7 +186,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { ...form, phone, }); - setAutoCap('None'); + setAutoCap('none'); setValid(phoneRegex.test(phone)); }; const handleEmailUpdate = (email: string) => { @@ -193,7 +195,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { ...form, email, }); - setAutoCap('None'); + setAutoCap('none'); setValid(emailRegex.test(email)); }; const handlePasswordUpdate = (password: string) => { @@ -201,7 +203,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { ...form, password, }); - setAutoCap('None'); + setAutoCap('none'); setValid(passwordRegex.test(password)); }; const formSteps: { @@ -369,7 +371,6 @@ const BasicInfoOnboarding: React.FC = ({route}) => { autoCompleteType="tel" selectionColor="white" textContentType="telephoneNumber" - autoCapitalize="none" externalStyles={{ warning: styles.passWarning, }} @@ -407,7 +408,7 @@ const BasicInfoOnboarding: React.FC = ({route}) => { accessibilityLabel={`${step.placeholder} input field.`} placeholder={step.placeholder} autoCompleteType="name" - autoCapitalize="words" + autoCapitalize={autoCapitalize} textContentType="name" returnKeyType="done" selectionColor="white" -- cgit v1.2.3-70-g09d2 From a623d705daa287be8ba3efaaf3c2c803e255dede Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 7 May 2021 19:38:26 -0400 Subject: added invalid styles --- src/screens/onboarding/BasicInfoOnboarding.tsx | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/screens/onboarding/BasicInfoOnboarding.tsx b/src/screens/onboarding/BasicInfoOnboarding.tsx index 25a48985..3fa33f63 100644 --- a/src/screens/onboarding/BasicInfoOnboarding.tsx +++ b/src/screens/onboarding/BasicInfoOnboarding.tsx @@ -363,7 +363,11 @@ const BasicInfoOnboarding: React.FC = ({route}) => { = ({route}) => { style={[styles.formContainer, {opacity: fadeValue}]}> = ({route}) => { placeholder="Password" autoCompleteType="password" textContentType="oneTimeCode" - returnKeyType="next" + returnKeyType="done" selectionColor="white" onChangeText={handlePasswordUpdate} onSubmitEditing={advanceRegistration} blurOnSubmit={false} + autoFocus={true} secureTextEntry={!passVisibility} valid={valid} externalStyles={{ @@ -457,7 +466,11 @@ const BasicInfoOnboarding: React.FC = ({route}) => { 'Password must be at least 8 characters & contain at least one of a-z, A-Z, 0-9, and a special character.' } attemptedSubmit={attemptedSubmit} - style={styles.input} + style={ + attemptedSubmit && !valid + ? [styles.input, styles.invalidColor] + : styles.input + } /> Date: Fri, 7 May 2021 16:49:02 -0700 Subject: reverted legacy onboarding --- .../onboarding/legacy/OnboardingStepOne.tsx | 137 ++++++++++----------- .../onboarding/legacy/OnboardingStepTwo.tsx | 43 ++++--- 2 files changed, 90 insertions(+), 90 deletions(-) (limited to 'src') diff --git a/src/screens/onboarding/legacy/OnboardingStepOne.tsx b/src/screens/onboarding/legacy/OnboardingStepOne.tsx index 5e88d004..b25d41fd 100644 --- a/src/screens/onboarding/legacy/OnboardingStepOne.tsx +++ b/src/screens/onboarding/legacy/OnboardingStepOne.tsx @@ -13,21 +13,19 @@ import { import { ArrowButton, Background, - BasicButton, - // RegistrationWizard, + RegistrationWizard, TaggInput, - TaggSquareButton, -} from '../../components'; -import {nameRegex, phoneRegex} from '../../constants'; +} from '../../../components'; +import {nameRegex, phoneRegex} from '../../../constants'; import { ERROR_NEXT_PAGE, ERROR_PHONE_IN_USE, ERROR_TWILIO_SERVER_ERROR, -} from '../../constants/strings'; -import {OnboardingStackParams} from '../../routes'; -import {sendOtpStatusCode} from '../../services'; -import {BackgroundGradientType} from '../../types'; -import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +} from '../../../constants/strings'; +import {OnboardingStackParams} from '../../../routes'; +import {sendOtpStatusCode} from '../../../services'; +import {BackgroundGradientType} from '../../../types'; +import {SCREEN_HEIGHT} from '../../../utils'; type OnboardingStepOneNavigationProp = StackNavigationProp< OnboardingStackParams, @@ -110,26 +108,27 @@ const OnboardingStepOne: React.FC = ({navigation}) => { }); } try { - const {isValidFname} = form; - // const {isValidFname, isValidLname, isValidPhone} = form; - if (isValidFname) { - const {fname} = form; - navigation.navigate('OnboardingStepTwo', { - firstName: fname, - }); - // const {phone} = form; - // const code = await sendOtpStatusCode(phone); - // if (code) { - // switch (code) { - // case 200: - // const {fname, lname} = form; - - // break; - // case 409: - // Alert.alert(ERROR_PHONE_IN_USE); - // break; - // default: - // Alert.alert(ERROR_TWILIO_SERVER_ERROR); + const {isValidFname, isValidLname, isValidPhone} = form; + if (isValidFname && isValidLname && isValidPhone) { + const {phone} = form; + const code = await sendOtpStatusCode(phone); + if (code) { + switch (code) { + case 200: + const {fname, lname} = form; + navigation.navigate('PhoneVerification', { + firstName: fname, + lastName: lname, + phone, + }); + break; + case 409: + Alert.alert(ERROR_PHONE_IN_USE); + break; + default: + Alert.alert(ERROR_TWILIO_SERVER_ERROR); + } + } } else { setForm({...form, attemptedSubmit: false}); setTimeout(() => setForm({...form, attemptedSubmit: true})); @@ -145,36 +144,29 @@ const OnboardingStepOne: React.FC = ({navigation}) => { const footer = useMemo( () => ( - - // // - // {/* navigation.navigate('Login')} - // /> */} - // {/* */} - // {/* */} - // {/* */} - // // + + navigation.navigate('Login')} + /> + + + + ), [ form.fname, - // form.lname, - // form.phone, + form.lname, + form.phone, form.isValidFname, - // form.isValidLname, - // form.isValidPhone, + form.isValidLname, + form.isValidPhone, ], ); @@ -183,14 +175,13 @@ const OnboardingStepOne: React.FC = ({navigation}) => { style={styles.container} gradientType={BackgroundGradientType.Light}> - {/* getting rid of registration progress in onboarding*/} - {/* */} + - {/* */} - SIGN UP - {/* */} + + ENTER NAME + = ({navigation}) => { attemptedSubmit={form.attemptedSubmit} width={280} /> - {/* = ({navigation}) => { invalidWarning="Please enter a valid last name." attemptedSubmit={form.attemptedSubmit} width={280} - /> */} - {/* + = ({navigation}) => { attemptedSubmit={form.attemptedSubmit} width={280} onSubmitEditing={goNext} - /> */} - {footer} + /> + {footer} ); }; const styles = StyleSheet.create({ container: { - height: SCREEN_HEIGHT, - width: SCREEN_WIDTH, + flex: 1, alignItems: 'center', justifyContent: 'center', }, - button: { - width: 40, - aspectRatio: 10, + wizard: { + position: 'absolute', + top: SCREEN_HEIGHT * 0.1, }, formHeader: { color: '#fff', fontSize: 30, fontWeight: '600', marginBottom: '16%', - position: 'absolute', - top: 0, - marginTop: '45%', }, load: { top: '5%', @@ -283,4 +270,4 @@ const styles = StyleSheet.create({ }, }); -export default OnboardingStepOne; +export default OnboardingStepOne; \ No newline at end of file diff --git a/src/screens/onboarding/legacy/OnboardingStepTwo.tsx b/src/screens/onboarding/legacy/OnboardingStepTwo.tsx index 65f953ea..40130263 100644 --- a/src/screens/onboarding/legacy/OnboardingStepTwo.tsx +++ b/src/screens/onboarding/legacy/OnboardingStepTwo.tsx @@ -13,10 +13,11 @@ import { View, } from 'react-native'; import { + ArrowButton, Background, LoadingIndicator, + RegistrationWizard, TaggInput, - TaggSquareButton, TermsConditions, } from '../../../components'; import {emailRegex, passwordRegex, usernameRegex} from '../../../constants'; @@ -28,6 +29,7 @@ import { import {OnboardingStackParams} from '../../../routes'; import {sendRegister} from '../../../services'; import {BackgroundGradientType} from '../../../types'; +import {SCREEN_HEIGHT} from '../../../utils'; type OnboardingStepTwoRouteProp = RouteProp< OnboardingStackParams, @@ -172,7 +174,7 @@ const OnboardingStepTwo: React.FC = ({ switch (response.status) { case 201: await AsyncStorage.setItem('token', token); - navigation.navigate('ProfileInfoOnboarding', { + navigation.navigate('OnboardingStepThree', { userId: UserID, username: username, }); @@ -209,13 +211,24 @@ const OnboardingStepTwo: React.FC = ({ const footer = useMemo( () => ( + + navigation.navigate('PhoneVerification', {...route.params}) + } + /> - @@ -238,7 +251,7 @@ const OnboardingStepTwo: React.FC = ({ style={styles.container} gradientType={BackgroundGradientType.Light}> - {/* */} + @@ -259,7 +272,7 @@ const OnboardingStepTwo: React.FC = ({ ref={usernameRef} valid={form.isValidUsername} invalidWarning={ - 'Username must beĀ at least 6 characters and contain only alphanumerics.' + 'Username must be at least 6 characters and contain only alphanumerics.' } attemptedSubmit={form.attemptedSubmit} width={280} @@ -334,10 +347,10 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, - // wizard: { - // position: 'absolute', - // top: SCREEN_HEIGHT * 0.1, - // }, + wizard: { + position: 'absolute', + top: SCREEN_HEIGHT * 0.1, + }, formHeader: { color: '#fff', fontSize: 30, @@ -365,4 +378,4 @@ const styles = StyleSheet.create({ }, }); -export default OnboardingStepTwo; +export default OnboardingStepTwo; \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 525678fde244e8e9d3ea373ee8fb93c26ea92be8 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 7 May 2021 20:09:57 -0400 Subject: added default for arrowbutton --- src/components/onboarding/ArrowButton.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/components/onboarding/ArrowButton.tsx b/src/components/onboarding/ArrowButton.tsx index dcf559a8..67fd0f62 100644 --- a/src/components/onboarding/ArrowButton.tsx +++ b/src/components/onboarding/ArrowButton.tsx @@ -5,6 +5,7 @@ import { TouchableOpacity, TouchableOpacityProps, } from 'react-native'; +import {normalize} from '../../utils'; interface ArrowButtonProps extends TouchableOpacityProps { direction: 'forward' | 'backward'; @@ -30,7 +31,7 @@ const ArrowButton: React.FC = (props) => { } return ( - + ); @@ -41,6 +42,10 @@ const styles = StyleSheet.create({ width: '100%', height: '100%', }, + defautSize: { + width: normalize(29), + height: normalize(25), + }, }); export default ArrowButton; -- cgit v1.2.3-70-g09d2