From db575615046544e83759a3615f37540305aa9742 Mon Sep 17 00:00:00 2001 From: Ashm Walia <40498934+ashmgarv@users.noreply.github.com> Date: Tue, 8 Dec 2020 20:19:32 -0800 Subject: [TMA-308] Forgot password logic [Frontend] (#131) * Done with changes * Submit on enter * Fixed StrongPassword issue * Clean and modular Verification.tsx * small fix --- src/constants/api.ts | 1 + src/constants/constants.ts | 2 + src/constants/regex.ts | 6 + src/routes/onboarding/Onboarding.tsx | 16 ++ src/routes/onboarding/OnboardingStack.tsx | 7 +- src/screens/onboarding/Login.tsx | 13 +- src/screens/onboarding/PasswordReset.tsx | 246 ++++++++++++++++++++++++ src/screens/onboarding/PasswordResetRequest.tsx | 198 +++++++++++++++++++ src/screens/onboarding/RegistrationOne.tsx | 4 +- src/screens/onboarding/RegistrationThree.tsx | 4 +- src/screens/onboarding/Verification.tsx | 133 ++++++++----- src/screens/onboarding/index.ts | 2 + src/services/UserProfileService.ts | 191 ++++++++++++++++++ src/types/types.ts | 9 +- 14 files changed, 767 insertions(+), 65 deletions(-) create mode 100644 src/screens/onboarding/PasswordReset.tsx create mode 100644 src/screens/onboarding/PasswordResetRequest.tsx (limited to 'src') diff --git a/src/constants/api.ts b/src/constants/api.ts index d3047e55..5a752e7b 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -24,6 +24,7 @@ export const FOLLOWING_ENDPOINT: string = API_URL + 'following/'; export const ALL_USERS_ENDPOINT: string = API_URL + 'users/'; export const REPORT_ISSUE_ENDPOINT: string = API_URL + 'report/'; export const BLOCK_USER_ENDPOINT: string = API_URL + 'block/'; +export const PASSWORD_RESET_ENDPOINT: string = API_URL + 'password-reset/'; // Register Social Link (Non-integrated) export const LINK_SNAPCHAT_ENDPOINT: string = API_URL + 'link-sc/'; diff --git a/src/constants/constants.ts b/src/constants/constants.ts index c14068c1..8832cec3 100644 --- a/src/constants/constants.ts +++ b/src/constants/constants.ts @@ -97,7 +97,9 @@ export const defaultMoments: Array = [ 'Activity', ]; +export const TAGG_CUSTOMER_SUPPORT: string = 'support@tagg.id'; export const BROWSABLE_SOCIAL_URLS: Record = { Instagram: 'https://instagram.com/', Twitter: 'https://twitter.com/', }; + diff --git a/src/constants/regex.ts b/src/constants/regex.ts index 01fd9a2d..6ab045bd 100644 --- a/src/constants/regex.ts +++ b/src/constants/regex.ts @@ -56,3 +56,9 @@ export const genderRegex: RegExp = /^$|^[A-Za-z\- ]{2,20}$/; * */ export const phoneRegex: RegExp = /([0-9]{10})/; + +/** + * The code regex has the following constraints + * - must be 6 digits + */ +export const codeRegex: RegExp = /([0-9]{6})/; diff --git a/src/routes/onboarding/Onboarding.tsx b/src/routes/onboarding/Onboarding.tsx index 4ebc281c..63a75934 100644 --- a/src/routes/onboarding/Onboarding.tsx +++ b/src/routes/onboarding/Onboarding.tsx @@ -10,6 +10,8 @@ import { ProfileOnboarding, Checkpoint, SocialMedia, + PasswordResetRequest, + PasswordReset, } from '../../screens'; import {StackCardInterpolationProps} from '@react-navigation/stack'; @@ -32,6 +34,20 @@ const Onboarding: React.FC = () => { cardStyleInterpolator: forFade, }} /> + + = ({navigation}: LoginProps) => { }; /** - * Handler for the Let's Start button or the Go button on the keyboard. - Makes a POST request to the Django login API and presents Alerts based on the status codes that the backend returns. - * Stores token received in the response, into client's AsynStorage - */ + * Handler for the Let's Start button or the Go button on the keyboard. + Makes a POST request to the Django login API and presents Alerts based on the status codes that the backend returns. + * Stores token received in the response, into client's AsynStorage + */ const handleLogin = async () => { if (!form.attemptedSubmit) { setForm({ @@ -207,7 +207,7 @@ const Login: React.FC = ({navigation}: LoginProps) => { accessibilityLabel="Forgot password button" accessibilityHint="Select this if you forgot your tagg password" style={styles.forgotPassword} - onPress={() => Alert.alert("tagg! You're it!")}> + onPress={() => navigation.navigate('PasswordResetRequest')}> Forgot password ); @@ -293,8 +293,7 @@ const Login: React.FC = ({navigation}: LoginProps) => { attemptedSubmit={form.attemptedSubmit} ref={inputRef} /> - {/* Commenting this line because the Forgot Password has not been implemented for Alpha */} - {/* */} + diff --git a/src/screens/onboarding/PasswordReset.tsx b/src/screens/onboarding/PasswordReset.tsx new file mode 100644 index 00000000..25991d6e --- /dev/null +++ b/src/screens/onboarding/PasswordReset.tsx @@ -0,0 +1,246 @@ +import React, {useState, useRef} from 'react'; +import {RouteProp} from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; +import { + View, + Text, + StyleSheet, + StatusBar, + Alert, + Platform, + KeyboardAvoidingView, +} from 'react-native'; + +import {OnboardingStackParams} from '../../routes'; + +import { + ArrowButton, + TaggInput, + Background, + LoadingIndicator, + SubmitButton, +} from '../../components'; + +import {trackPromise} from 'react-promise-tracker'; +import {passwordRegex} from '../../constants'; +import {handlePasswordReset} from '../../services'; + +type PasswordResetRequestRouteProp = RouteProp< + OnboardingStackParams, + 'PasswordReset' +>; +type PasswordResetRequestNavigationProp = StackNavigationProp< + OnboardingStackParams, + 'PasswordReset' +>; +interface PasswordResetRequestProps { + route: PasswordResetRequestRouteProp; + navigation: PasswordResetRequestNavigationProp; +} +/** + * Password reset page + * @param navigation react-navigation navigation object + */ +const PasswordResetRequest: React.FC = ({ + route, + navigation, +}) => { + const passwordRef = useRef(); + const confirmRef = useRef(); + const {value} = route.params; + + /** + * Handles focus change to the next input field. + * @param field key for field to move focus onto + */ + const handleFocusChange = (): void => { + const confirmField: any = confirmRef.current; + confirmField.focus(); + }; + + const [form, setForm] = useState({ + password: '', + confirm: '', + isValidPassword: false, + passwordsMatch: false, + attemptedSubmit: false, + }); + + 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 handleSubmit = async () => { + if (!form.attemptedSubmit) { + setForm({ + ...form, + attemptedSubmit: true, + }); + } + try { + if (form.isValidPassword && form.passwordsMatch) { + const success = await trackPromise( + handlePasswordReset(value, form.password), + ); + if (success) { + navigation.navigate('Login'); + } + } else { + setForm({...form, attemptedSubmit: false}); + setTimeout(() => setForm({...form, attemptedSubmit: true})); + } + } catch (error) { + Alert.alert( + 'Looks like our servers are down. 😓', + "Try again in a couple minutes. We're sorry for the inconvenience.", + ); + return { + name: 'Verify Password error', + description: error, + }; + } + }; + + /** + * Reset Password Button. + */ + const ResetPasswordButton = () => ( + + ); + + const Footer = () => ( + + navigation.navigate('Login')} + /> + + ); + + return ( + + + + + Enter your new password + + handleFocusChange} + 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} + /> + + + + +