aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAshm Walia <40498934+ashmgarv@users.noreply.github.com>2020-12-08 20:19:32 -0800
committerGitHub <noreply@github.com>2020-12-08 23:19:32 -0500
commitdb575615046544e83759a3615f37540305aa9742 (patch)
treef30a29f47420990872c9baede4978582cea0b607 /src
parent0cb19c5b173d4cf6ba67378cbffd61abac7f18c3 (diff)
[TMA-308] Forgot password logic [Frontend] (#131)
* Done with changes * Submit on enter * Fixed StrongPassword issue * Clean and modular Verification.tsx * small fix
Diffstat (limited to 'src')
-rw-r--r--src/constants/api.ts1
-rw-r--r--src/constants/constants.ts2
-rw-r--r--src/constants/regex.ts6
-rw-r--r--src/routes/onboarding/Onboarding.tsx16
-rw-r--r--src/routes/onboarding/OnboardingStack.tsx7
-rw-r--r--src/screens/onboarding/Login.tsx13
-rw-r--r--src/screens/onboarding/PasswordReset.tsx246
-rw-r--r--src/screens/onboarding/PasswordResetRequest.tsx198
-rw-r--r--src/screens/onboarding/RegistrationOne.tsx4
-rw-r--r--src/screens/onboarding/RegistrationThree.tsx4
-rw-r--r--src/screens/onboarding/Verification.tsx133
-rw-r--r--src/screens/onboarding/index.ts2
-rw-r--r--src/services/UserProfileService.ts191
-rw-r--r--src/types/types.ts9
14 files changed, 767 insertions, 65 deletions
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<string> = [
'Activity',
];
+export const TAGG_CUSTOMER_SUPPORT: string = 'support@tagg.id';
export const BROWSABLE_SOCIAL_URLS: Record<string, string> = {
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';
@@ -33,6 +35,20 @@ const Onboarding: React.FC = () => {
}}
/>
<OnboardingStack.Screen
+ name="PasswordResetRequest"
+ component={PasswordResetRequest}
+ options={{
+ gestureEnabled: false,
+ }}
+ />
+ <OnboardingStack.Screen
+ name="PasswordReset"
+ component={PasswordReset}
+ options={{
+ gestureEnabled: false,
+ }}
+ />
+ <OnboardingStack.Screen
name="InvitationCodeVerification"
component={InvitationCodeVerification}
/>
diff --git a/src/routes/onboarding/OnboardingStack.tsx b/src/routes/onboarding/OnboardingStack.tsx
index ccf9be47..33ff51ea 100644
--- a/src/routes/onboarding/OnboardingStack.tsx
+++ b/src/routes/onboarding/OnboardingStack.tsx
@@ -1,7 +1,12 @@
import {createStackNavigator} from '@react-navigation/stack';
+import {VerificationScreenType} from '../../types';
export type OnboardingStackParams = {
Login: undefined;
+ PasswordResetRequest: undefined;
+ PasswordReset: {
+ value: string;
+ };
InvitationCodeVerification: undefined;
RegistrationOne: undefined;
RegistrationTwo: {phone: string};
@@ -12,7 +17,7 @@ export type OnboardingStackParams = {
email: string;
};
Checkpoint: {username: string; userId: string};
- Verification: {phone: string};
+ Verification: {id: string; screenType: VerificationScreenType};
ProfileOnboarding: {username: string; userId: string};
SocialMedia: {username: string; userId: string};
};
diff --git a/src/screens/onboarding/Login.tsx b/src/screens/onboarding/Login.tsx
index 2738d6ca..cb550ef8 100644
--- a/src/screens/onboarding/Login.tsx
+++ b/src/screens/onboarding/Login.tsx
@@ -127,10 +127,10 @@ const Login: React.FC<LoginProps> = ({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<LoginProps> = ({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')}>
<Text style={styles.forgotPasswordText}>Forgot password</Text>
</TouchableOpacity>
);
@@ -293,8 +293,7 @@ const Login: React.FC<LoginProps> = ({navigation}: LoginProps) => {
attemptedSubmit={form.attemptedSubmit}
ref={inputRef}
/>
- {/* Commenting this line because the Forgot Password has not been implemented for Alpha */}
- {/* <ForgotPassword /> */}
+ <ForgotPassword />
<LoginButton />
</KeyboardAvoidingView>
<RegistrationPrompt />
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<PasswordResetRequestProps> = ({
+ 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 = () => (
+ <SubmitButton
+ text="Reset Password"
+ color="#fff"
+ style={styles.button}
+ accessibilityLabel="Reset Password"
+ accessibilityHint="Select this after entering teh code and new password"
+ onPress={handleSubmit}
+ />
+ );
+
+ const Footer = () => (
+ <View style={styles.footer}>
+ <ArrowButton
+ direction="backward"
+ onPress={() => navigation.navigate('Login')}
+ />
+ </View>
+ );
+
+ return (
+ <Background style={styles.container}>
+ <StatusBar barStyle="light-content" />
+ <KeyboardAvoidingView
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
+ style={styles.container}>
+ <View>
+ <Text style={styles.description}>Enter your new password</Text>
+ </View>
+ <TaggInput
+ accessibilityHint="Enter a password."
+ accessibilityLabel="Password input field."
+ placeholder="Password"
+ autoCompleteType="password"
+ textContentType="oneTimeCode"
+ returnKeyType="next"
+ onChangeText={handlePasswordUpdate}
+ onSubmitEditing={() => 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}
+ />
+ <TaggInput
+ accessibilityHint={'Re-enter your password.'}
+ accessibilityLabel={'Password confirmation input field.'}
+ placeholder={'Confirm Password'}
+ autoCompleteType="password"
+ textContentType="oneTimeCode"
+ returnKeyType={'go'}
+ onChangeText={handleConfirmUpdate}
+ onSubmitEditing={handleSubmit}
+ secureTextEntry
+ ref={confirmRef}
+ valid={form.passwordsMatch}
+ invalidWarning={'Passwords must match.'}
+ attemptedSubmit={form.attemptedSubmit}
+ width={280}
+ />
+ <ResetPasswordButton />
+ <LoadingIndicator />
+ </KeyboardAvoidingView>
+ <Footer />
+ </Background>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ header: {
+ ...Platform.select({
+ ios: {
+ top: 50,
+ },
+ android: {
+ bottom: 40,
+ },
+ }),
+ },
+ formHeader: {
+ color: '#fff',
+ fontSize: 20,
+ fontWeight: 'bold',
+ alignSelf: 'flex-start',
+ marginBottom: '6%',
+ marginHorizontal: '10%',
+ },
+ load: {
+ top: '5%',
+ },
+ description: {
+ color: '#fff',
+ fontWeight: '600',
+ fontSize: 17,
+ marginHorizontal: '10%',
+ },
+ footer: {
+ width: '100%',
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ ...Platform.select({
+ ios: {
+ bottom: '20%',
+ },
+ android: {
+ bottom: '10%',
+ },
+ }),
+ },
+ button: {
+ marginVertical: '10%',
+ },
+});
+
+export default PasswordResetRequest;
diff --git a/src/screens/onboarding/PasswordResetRequest.tsx b/src/screens/onboarding/PasswordResetRequest.tsx
new file mode 100644
index 00000000..5f67eb19
--- /dev/null
+++ b/src/screens/onboarding/PasswordResetRequest.tsx
@@ -0,0 +1,198 @@
+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,
+ TouchableOpacity,
+ KeyboardAvoidingView,
+} from 'react-native';
+
+import {OnboardingStackParams} from '../../routes';
+
+import {
+ ArrowButton,
+ TaggInput,
+ Background,
+ LoadingIndicator,
+} from '../../components';
+
+import {trackPromise} from 'react-promise-tracker';
+import {emailRegex, usernameRegex} from '../../constants';
+import {handlePasswordResetRequest} from '../../services';
+import {VerificationScreenType} from '../../types';
+
+type PasswordResetRequestRouteProp = RouteProp<
+ OnboardingStackParams,
+ 'PasswordResetRequest'
+>;
+type PasswordResetRequestNavigationProp = StackNavigationProp<
+ OnboardingStackParams,
+ 'PasswordResetRequest'
+>;
+interface PasswordResetRequestProps {
+ route: PasswordResetRequestRouteProp;
+ navigation: PasswordResetRequestNavigationProp;
+}
+/**
+ * Password reset request page for getting username / email
+ * @param navigation react-navigation navigation object
+ */
+const PasswordResetRequest: React.FC<PasswordResetRequestProps> = ({
+ navigation,
+}) => {
+ const [form, setForm] = useState({
+ value: '',
+ isValid: false,
+ attemptedSubmit: false,
+ });
+
+ const handleValueUpdate = (value: string) => {
+ value = value.trim();
+
+ //Entered field should either be a valid username or a valid email
+ let isValid: boolean = emailRegex.test(value) || usernameRegex.test(value);
+
+ setForm({
+ ...form,
+ value,
+ isValid,
+ });
+ };
+
+ const goToPasswordCodeVerification = async () => {
+ if (!form.attemptedSubmit) {
+ setForm({
+ ...form,
+ attemptedSubmit: true,
+ });
+ }
+ try {
+ if (form.isValid) {
+ const success = await trackPromise(
+ handlePasswordResetRequest(form.value),
+ );
+ if (success) {
+ navigation.navigate('Verification', {
+ id: form.value,
+ screenType: VerificationScreenType.Password,
+ });
+ }
+ } 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: 'Send OTP error',
+ description: error,
+ };
+ }
+ };
+
+ const Footer = () => (
+ <View style={styles.footer}>
+ <ArrowButton
+ direction="backward"
+ onPress={() => navigation.navigate('Login')}
+ />
+ <TouchableOpacity onPress={goToPasswordCodeVerification}>
+ <ArrowButton
+ direction="forward"
+ disabled={!form.isValid}
+ onPress={goToPasswordCodeVerification}
+ />
+ </TouchableOpacity>
+ </View>
+ );
+
+ return (
+ <Background style={styles.container}>
+ <StatusBar barStyle="light-content" />
+ <KeyboardAvoidingView
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
+ style={styles.container}>
+ <View>
+ <Text style={styles.description}>
+ Enter your registered username / email
+ </Text>
+ </View>
+ <TaggInput
+ accessibilityHint="Enter a username / email"
+ accessibilityLabel="Input field."
+ placeholder="Username / Email"
+ autoCompleteType="username"
+ textContentType="username"
+ autoCapitalize="none"
+ returnKeyType="go"
+ onSubmitEditing={goToPasswordCodeVerification}
+ onChangeText={handleValueUpdate}
+ valid={form.isValid}
+ invalidWarning={'You must enter a valid username / email'}
+ attemptedSubmit={form.attemptedSubmit}
+ width={280}
+ />
+ <LoadingIndicator />
+ </KeyboardAvoidingView>
+ <Footer />
+ </Background>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ header: {
+ ...Platform.select({
+ ios: {
+ top: 50,
+ },
+ android: {
+ bottom: 40,
+ },
+ }),
+ },
+ formHeader: {
+ color: '#fff',
+ fontSize: 20,
+ fontWeight: 'bold',
+ alignSelf: 'flex-start',
+ marginBottom: '6%',
+ marginHorizontal: '10%',
+ },
+ load: {
+ top: '5%',
+ },
+ description: {
+ color: '#fff',
+ fontWeight: '600',
+ fontSize: 17,
+ marginHorizontal: '10%',
+ },
+ footer: {
+ width: '100%',
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ ...Platform.select({
+ ios: {
+ bottom: '20%',
+ },
+ android: {
+ bottom: '10%',
+ },
+ }),
+ },
+});
+
+export default PasswordResetRequest;
diff --git a/src/screens/onboarding/RegistrationOne.tsx b/src/screens/onboarding/RegistrationOne.tsx
index 277b3510..3373b903 100644
--- a/src/screens/onboarding/RegistrationOne.tsx
+++ b/src/screens/onboarding/RegistrationOne.tsx
@@ -27,6 +27,7 @@ import {trackPromise} from 'react-promise-tracker';
import {SEND_OTP_ENDPOINT} from '../../constants';
import {phoneRegex} from '../../constants';
+import {VerificationScreenType} from '../../types';
type RegistrationScreenOneRouteProp = RouteProp<
OnboardingStackParams,
@@ -91,7 +92,8 @@ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => {
let otpStatusCode = sendOtpResponse.status;
if (otpStatusCode === 200) {
navigation.navigate('Verification', {
- phone: form.phone_number,
+ id: form.phone_number,
+ screenType: VerificationScreenType.Phone,
});
} else if (otpStatusCode === 409) {
Alert.alert(
diff --git a/src/screens/onboarding/RegistrationThree.tsx b/src/screens/onboarding/RegistrationThree.tsx
index 1e1b48d4..614795ca 100644
--- a/src/screens/onboarding/RegistrationThree.tsx
+++ b/src/screens/onboarding/RegistrationThree.tsx
@@ -274,7 +274,7 @@ const RegistrationThree: React.FC<RegistrationThreeProps> = ({
accessibilityLabel="Password input field."
placeholder="Password"
autoCompleteType="password"
- textContentType="newPassword"
+ textContentType="oneTimeCode"
returnKeyType="next"
onChangeText={handlePasswordUpdate}
onSubmitEditing={() => handleFocusChange('confirm')}
@@ -293,7 +293,7 @@ const RegistrationThree: React.FC<RegistrationThreeProps> = ({
accessibilityLabel={'Password confirmation input field.'}
placeholder={'Confirm Password'}
autoCompleteType="password"
- textContentType="password"
+ textContentType="oneTimeCode"
returnKeyType={form.tcAccepted ? 'go' : 'default'}
onChangeText={handleConfirmUpdate}
onSubmitEditing={handleRegister}
diff --git a/src/screens/onboarding/Verification.tsx b/src/screens/onboarding/Verification.tsx
index ad012c92..9fa1c12f 100644
--- a/src/screens/onboarding/Verification.tsx
+++ b/src/screens/onboarding/Verification.tsx
@@ -10,7 +10,7 @@ import {
ArrowButton,
LoadingIndicator,
} from '../../components';
-import {VERIFY_OTP_ENDPOINT, SEND_OTP_ENDPOINT} from '../../constants';
+
import {Text} from 'react-native-animatable';
import {
CodeField,
@@ -27,6 +27,13 @@ import {
Platform,
} from 'react-native';
import {trackPromise} from 'react-promise-tracker';
+import {VerificationScreenType} from '../../types';
+import {
+ handlePasswordCodeVerification,
+ sendOtp,
+ verifyOtp,
+ handlePasswordResetRequest,
+} from '../../services';
type VerificationScreenRouteProp = RouteProp<
OnboardingStackParams,
@@ -41,6 +48,8 @@ interface VerificationProps {
navigation: VerificationScreenNavigationProp;
}
+import {codeRegex} from '../../constants';
+
const Verification: React.FC<VerificationProps> = ({route, navigation}) => {
const [value, setValue] = React.useState('');
const ref = useBlurOnFulfill({value, cellCount: 6});
@@ -48,41 +57,46 @@ const Verification: React.FC<VerificationProps> = ({route, navigation}) => {
value,
setValue,
});
- const {phone} = route.params;
+ const {id, screenType} = route.params;
+ const isPhoneVerification = screenType === VerificationScreenType.Phone;
+
+ const handlePhoneVerification = async () => {
+ const success = await trackPromise(verifyOtp(id, value));
+ if (success) {
+ navigation.navigate('RegistrationTwo', {phone: id});
+ }
+ };
+
+ const handlePasswordVerification = async () => {
+ const success = await trackPromise(
+ handlePasswordCodeVerification(id, value),
+ );
+ if (success) {
+ navigation.navigate('PasswordReset', {value: id});
+ }
+ };
/**
* Sends the verify_otp request upon tapping the Verify button.
- * If successful, it navigates to the Profile page.
+ * If successful, it navigates to the respected page.
*/
const handleVerification = async () => {
- try {
- let verifyOtpResponse = await fetch(VERIFY_OTP_ENDPOINT, {
- method: 'POST',
- body: JSON.stringify({
- phone_number: '+1' + phone,
- otp: value,
- }),
- });
- let statusCode = verifyOtpResponse.status;
- if (statusCode === 200) {
- navigation.navigate('RegistrationTwo', {
- phone: phone,
- });
- } else {
- Alert.alert(
- 'Invalid verification code 🤔',
- 'Try again. Tap the resend code button if you need a new code.',
- );
+ if (codeRegex.test(value)) {
+ try {
+ switch (screenType) {
+ case VerificationScreenType.Phone:
+ handlePhoneVerification();
+ break;
+ case VerificationScreenType.Password:
+ handlePasswordVerification();
+ break;
+ }
+ } catch (error) {
+ console.log(error);
+ Alert.alert('Something went wrong');
}
- } catch (error) {
- Alert.alert(
- 'Verifiation failed 😓',
- 'Please double-check your network connection and retry.',
- );
- return {
- name: 'Verification error',
- description: error,
- };
+ } else {
+ Alert.alert('Please enter a valid 6 digit code');
}
};
@@ -91,42 +105,49 @@ const Verification: React.FC<VerificationProps> = ({route, navigation}) => {
*/
const handleResend = async () => {
try {
- let sendOtpResponse = await trackPromise(
- fetch(SEND_OTP_ENDPOINT, {
- method: 'POST',
- body: JSON.stringify({
- phone: phone,
- }),
- }),
- );
- let sendOtpStatusCode = sendOtpResponse.status;
- if (sendOtpStatusCode === 200) {
- setValue('');
- Alert.alert(
- 'New verification code sent!',
- 'Check your phone messages for your code.',
- );
- } else {
- Alert.alert('Something went wrong!');
+ switch (screenType) {
+ case VerificationScreenType.Phone:
+ trackPromise(sendOtp(id));
+ break;
+ case VerificationScreenType.Password:
+ trackPromise(handlePasswordResetRequest(id));
+ break;
}
} catch (error) {
console.log(error);
+ Alert.alert('Something went wrong');
+ }
+ };
+
+ const handleGoBack = () => {
+ switch (screenType) {
+ case VerificationScreenType.Phone:
+ navigation.navigate('RegistrationOne');
+ break;
+ case VerificationScreenType.Password:
+ navigation.navigate('PasswordResetRequest');
+ break;
}
};
const Footer = () => (
<View style={styles.footer}>
- <ArrowButton
- direction="backward"
- onPress={() => navigation.navigate('RegistrationOne')}
- />
+ <ArrowButton direction="backward" onPress={() => handleGoBack()} />
</View>
);
return (
<Background centered style={styles.container}>
- <RegistrationWizard style={styles.wizard} step="three" />
- <KeyboardAvoidingView behavior="padding" style={styles.form}>
+ {isPhoneVerification ? (
+ <RegistrationWizard style={styles.wizard} step="three" />
+ ) : (
+ <React.Fragment />
+ )}
+ <KeyboardAvoidingView
+ behavior="padding"
+ style={
+ isPhoneVerification ? styles.form : styles.formPasswordVerification
+ }>
<Text style={styles.formHeader}>Enter 6 digit code</Text>
<Text style={styles.description}>
We sent a 6 digit verification code to the phone number you provided.
@@ -185,6 +206,12 @@ const styles = StyleSheet.create({
justifyContent: 'flex-start',
flex: 3,
},
+ formPasswordVerification: {
+ alignItems: 'center',
+ justifyContent: 'flex-start',
+ flex: 3,
+ top: '35%',
+ },
formHeader: {
color: '#fff',
fontSize: 20,
diff --git a/src/screens/onboarding/index.ts b/src/screens/onboarding/index.ts
index e116bf2c..2411a7e7 100644
--- a/src/screens/onboarding/index.ts
+++ b/src/screens/onboarding/index.ts
@@ -7,3 +7,5 @@ export {default as Checkpoint} from './Checkpoint';
export {default as ProfileOnboarding} from './ProfileOnboarding';
export {default as InvitationCodeVerification} from './InvitationCodeVerification';
export {default as SocialMedia} from './SocialMedia';
+export {default as PasswordResetRequest} from './PasswordResetRequest';
+export {default as PasswordReset} from './PasswordReset';
diff --git a/src/services/UserProfileService.ts b/src/services/UserProfileService.ts
index c8dbcdd1..18bcfb5a 100644
--- a/src/services/UserProfileService.ts
+++ b/src/services/UserProfileService.ts
@@ -10,6 +10,10 @@ import {
GET_IG_POSTS_ENDPOINT,
GET_TWITTER_POSTS_ENDPOINT,
PROFILE_INFO_ENDPOINT,
+ PASSWORD_RESET_ENDPOINT,
+ TAGG_CUSTOMER_SUPPORT,
+ VERIFY_OTP_ENDPOINT,
+ SEND_OTP_ENDPOINT,
} from '../constants';
export const loadProfileInfo = async (token: string, userId: string) => {
@@ -116,3 +120,190 @@ export const loadRecentlySearchedUsers = async () => {
console.log(e);
}
};
+
+export const handlePasswordResetRequest = async (value: string) => {
+ try {
+ const token = await AsyncStorage.getItem('token');
+ const response = await fetch(PASSWORD_RESET_ENDPOINT + 'request/', {
+ method: 'POST',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
+ body: JSON.stringify({
+ value,
+ }),
+ });
+ const status = response.status;
+ if (status === 200) {
+ Alert.alert(
+ 'A code was sent to your registered phone number, please use the same to reset your password',
+ );
+ return true;
+ } else {
+ if (status == 404) {
+ Alert.alert(
+ `Please make sure that the email / username entered is registered with us. You may contact our customer support at ${TAGG_CUSTOMER_SUPPORT}`,
+ );
+ } else {
+ Alert.alert(
+ 'Something went wrong! 😭',
+ "Would you believe me if I told you that I don't know what happened?",
+ );
+ }
+
+ console.log(response);
+ return false;
+ }
+ } catch (error) {
+ console.log(error);
+ Alert.alert('Something went wrong! 😭', 'Looks like our servers are down');
+ return false;
+ }
+};
+
+export const handlePasswordCodeVerification = async (
+ value: string,
+ otp: string,
+) => {
+ try {
+ const token = await AsyncStorage.getItem('token');
+ const response = await fetch(PASSWORD_RESET_ENDPOINT + 'verify/', {
+ method: 'POST',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
+ body: JSON.stringify({
+ value,
+ otp,
+ }),
+ });
+ const status = response.status;
+ if (status === 200) {
+ return true;
+ } else {
+ if (status == 404) {
+ Alert.alert(
+ `Please make sure that the email / username entered is registered with us. You may contact our customer support at ${TAGG_CUSTOMER_SUPPORT}`,
+ );
+ } else if (status === 401) {
+ Alert.alert('Looks like you have entered the wrong code');
+ } else {
+ Alert.alert(
+ 'Something went wrong! 😭',
+ "Would you believe me if I told you that I don't know what happened?",
+ );
+ }
+
+ console.log(response);
+ return false;
+ }
+ } catch (error) {
+ console.log(error);
+ Alert.alert('Something went wrong! 😭', 'Looks like our servers are down');
+ return false;
+ }
+};
+
+export const handlePasswordReset = async (value: string, password: string) => {
+ try {
+ const token = await AsyncStorage.getItem('token');
+ const response = await fetch(PASSWORD_RESET_ENDPOINT + `reset/`, {
+ method: 'POST',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
+ body: JSON.stringify({
+ value,
+ password,
+ }),
+ });
+ const status = response.status;
+ if (status === 200) {
+ Alert.alert('Your password was reset successfully');
+ return true;
+ } else {
+ if (status == 404) {
+ Alert.alert(
+ `Please make sure that the email / username entered is registered with us. You may contact our customer support at ${TAGG_CUSTOMER_SUPPORT}`,
+ );
+ } else if (status == 406) {
+ Alert.alert('You may not use an already used password');
+ } else {
+ Alert.alert(
+ 'Something went wrong! 😭',
+ "Would you believe me if I told you that I don't know what happened?",
+ );
+ }
+ console.log(response);
+ return false;
+ }
+ } catch (error) {
+ console.log(error);
+ Alert.alert('Something went wrong! 😭', 'Looks like our servers are down');
+ return false;
+ }
+};
+
+export const verifyOtp = async (phone: string, otp: string) => {
+ try {
+ let response = await fetch(VERIFY_OTP_ENDPOINT, {
+ method: 'POST',
+ body: JSON.stringify({
+ phone_number: '+1' + phone,
+ otp,
+ }),
+ });
+ let statusCode = response.status;
+ if (statusCode === 200) {
+ return true;
+ } else {
+ if (statusCode === 401) {
+ Alert.alert(
+ 'Invalid verification code 🤔',
+ 'Try again. Tap the resend code button if you need a new code.',
+ );
+ } else {
+ Alert.alert(
+ 'Something went wrong! 😭',
+ "Would you believe me if I told you that I don't know what happened?",
+ );
+ }
+ }
+ } catch (error) {
+ Alert.alert(
+ 'Verifiation failed 😓',
+ 'Please double-check your network connection and retry.',
+ );
+ return {
+ name: 'Verification error',
+ description: error,
+ };
+ }
+};
+
+export const sendOtp = async (phone: string) => {
+ try {
+ console.log(phone);
+ let response = await fetch(SEND_OTP_ENDPOINT, {
+ method: 'POST',
+ body: JSON.stringify({
+ phone_number: '+1' + phone,
+ }),
+ });
+
+ let status = response.status;
+ if (status === 200) {
+ Alert.alert(
+ 'New verification code sent!',
+ 'Check your phone messages for your code.',
+ );
+ return true;
+ } else {
+ Alert.alert('Something went wrong!');
+ return false;
+ }
+ } catch (error) {
+ console.log(error);
+ return false;
+ }
+};
diff --git a/src/types/types.ts b/src/types/types.ts
index 79f15ae9..bfd0cd93 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -95,7 +95,6 @@ export type PreviewType =
| 'Discover Users'
| 'Follow';
-
export enum ScreenType {
Profile,
Search,
@@ -118,3 +117,11 @@ export interface UserXType {
avatar: string;
cover: string;
}
+
+/**
+ * We have two verification screen types, this enum is used to display proper content based on the type
+ */
+export enum VerificationScreenType {
+ Phone,
+ Password,
+}