From 7a95090359a1196835d5f5f65bc3f7fba47873e1 Mon Sep 17 00:00:00 2001 From: Omar Date: Tue, 1 Jan 2019 13:58:08 -0800 Subject: [PATCH] Phone Verification --- package-lock.json | 99 +++++++++++ package.json | 2 + src/config/apiEndpoints.js | 8 + src/config/router.js | 14 +- src/locales/translations/en.json | 8 +- src/screens/auth/LoginScreen.js | 2 +- .../{SignupScreenOne.js => SignupScreen1.js} | 2 +- .../{SignupScreenTwo.js => SignupScreen2.js} | 5 +- src/screens/auth/SignupScreen3a.js | 166 ++++++++++++++++++ src/screens/auth/SignupScreen3b.js | 162 +++++++++++++++++ src/utils/errorHandler.js | 2 +- 11 files changed, 456 insertions(+), 14 deletions(-) rename src/screens/auth/{SignupScreenOne.js => SignupScreen1.js} (99%) rename src/screens/auth/{SignupScreenTwo.js => SignupScreen2.js} (98%) create mode 100644 src/screens/auth/SignupScreen3a.js create mode 100644 src/screens/auth/SignupScreen3b.js diff --git a/package-lock.json b/package-lock.json index dab0434..34d3318 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4647,6 +4647,11 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "fuse.js": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-2.6.2.tgz", + "integrity": "sha1-1dmU/alvVDtaUd84tyzsnMYNneo=" + }, "gauge": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", @@ -6963,6 +6968,11 @@ "resolved": "https://registry.npmjs.org/lodash.times/-/lodash.times-4.3.2.tgz", "integrity": "sha1-Ph8lZcQxdU1Uq1fy7RdBk5KFyh0=" }, + "lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -7468,6 +7478,11 @@ } } }, + "moment": { + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.19.3.tgz", + "integrity": "sha1-vbmdJw1tf9p4zA+6zoVeJ/59pp8=" + }, "morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -7581,6 +7596,14 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "node-emoji": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.8.1.tgz", + "integrity": "sha512-+ktMAh1Jwas+TnGodfCfjUbJKoANqPaJFN0z0iqh41eqD8dvguNzcitVSBSVK1pidz0AqGbLKcoVuVLRVZ/aVg==", + "requires": { + "lodash.toarray": "^4.4.0" + } + }, "node-fetch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz", @@ -8500,6 +8523,63 @@ "yargs": "^9.0.0" } }, + "react-native-country-picker-modal": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/react-native-country-picker-modal/-/react-native-country-picker-modal-0.6.2.tgz", + "integrity": "sha1-upcRi+Q3O+DBHNUeRF5r1Eji8co=", + "requires": { + "fuse.js": "2.6.2", + "lodash": "4.12.0", + "node-emoji": "1.8.1", + "prop-types": "15.6.0", + "react-native-safe-area-view": "^0.7.0", + "world-countries": "1.8.0" + }, + "dependencies": { + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + }, + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + }, + "lodash": { + "version": "4.12.0", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-4.12.0.tgz", + "integrity": "sha1-K9bcRqBA9Z5obJcu0h2T3FkFMlg=" + }, + "prop-types": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz", + "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=", + "requires": { + "fbjs": "^0.8.16", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, + "react-native-safe-area-view": { + "version": "0.7.0", + "resolved": "http://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.7.0.tgz", + "integrity": "sha512-SjLdW/Th0WVMhyngH4O6yC21S+O4U4AAG3QxBr7fZ2ftgjXSpKbDHAhEpxBdFwei6HsnsC2h9oYMtPpaW9nfGg==", + "requires": { + "hoist-non-react-statics": "^2.3.1" + } + } + } + }, "react-native-elements": { "version": "0.19.1", "resolved": "http://registry.npmjs.org/react-native-elements/-/react-native-elements-0.19.1.tgz", @@ -8536,6 +8616,15 @@ } } }, + "react-native-masked-text": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/react-native-masked-text/-/react-native-masked-text-1.9.2.tgz", + "integrity": "sha512-+EctPpbQkXJe3ljWggkMRv4Fn+Oy7WKegFPKzK/N91qV9aZWZZwMosWDiH4u3aU+cO+GAjWAZn9ilSLGDroJrQ==", + "requires": { + "moment": "2.19.3", + "tinymask": "^1.0.2" + } + }, "react-native-safe-area-view": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.11.0.tgz", @@ -10294,6 +10383,11 @@ "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" }, + "tinymask": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinymask/-/tinymask-1.0.2.tgz", + "integrity": "sha1-3zd1qUCHsNPQVsJW6JI8AbfsCUE=" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -10774,6 +10868,11 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" }, + "world-countries": { + "version": "1.8.0", + "resolved": "http://registry.npmjs.org/world-countries/-/world-countries-1.8.0.tgz", + "integrity": "sha1-F/SOfoRwrFohNq1pON/GVvwry5U=" + }, "wrap-ansi": { "version": "2.1.0", "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", diff --git a/package.json b/package.json index fffc4fe..df55e6f 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,11 @@ "i18n-js": "^3.1.0", "react": "16.6.3", "react-native": "0.57.8", + "react-native-country-picker-modal": "^0.6.2", "react-native-elements": "^0.19.1", "react-native-flash-message": "^0.1.10", "react-native-gesture-handler": "^1.0.12", + "react-native-masked-text": "^1.9.2", "react-native-vector-icons": "^4.6.0", "react-native-webview": "^2.14.3", "react-navigation": "^3.0.9", diff --git a/src/config/apiEndpoints.js b/src/config/apiEndpoints.js index aab124d..cdcefc1 100644 --- a/src/config/apiEndpoints.js +++ b/src/config/apiEndpoints.js @@ -19,6 +19,14 @@ const endpoints = { twitterToken: { method: 'GET', url: `${baseurl}/auth/twitter/token` + }, + sendCode: { + method: 'POST', + url: `${baseurl}/auth/phone/code` + }, + verifyCode: { + method: 'POST', + url: `${baseurl}/auth/phone/verify` } }; diff --git a/src/config/router.js b/src/config/router.js index c738b2f..9eaaf5c 100644 --- a/src/config/router.js +++ b/src/config/router.js @@ -1,17 +1,21 @@ import { createStackNavigator, createAppContainer } from 'react-navigation'; import WelcomeScreen from '../screens/onboaring/WelcomeScreen'; import LoginScreen from '../screens/auth/LoginScreen'; -import SignupScreenOne from '../screens/auth/SignupScreenOne'; -import SignupScreenTwo from '../screens/auth/SignupScreenTwo'; +import SignupScreenOne from '../screens/auth/SignupScreen1'; +import SignupScreenTwo from '../screens/auth/SignupScreen2'; +import SignupScreenThreeA from '../screens/auth/SignupScreen3a'; +import SignupScreenThreeB from '../screens/auth/SignupScreen3b'; import TwitterWebView from '../screens/auth/TwitterWebView'; import ForgotPasswordScreen from '../screens/auth/ForgotPasswordScreen'; -const MainNavigator = createStackNavigator({ +const MainNavigator = createStackNavigator({ welcome: WelcomeScreen, login: LoginScreen, forgotPassword: ForgotPasswordScreen, - signupOne: SignupScreenOne, - signupTwo: SignupScreenTwo + signup1: SignupScreenOne, + signup2: SignupScreenTwo, + signup3a: SignupScreenThreeA, + signup3b: SignupScreenThreeB, }); const RootNavigator = createStackNavigator( diff --git a/src/locales/translations/en.json b/src/locales/translations/en.json index 2d0b810..6789c5b 100644 --- a/src/locales/translations/en.json +++ b/src/locales/translations/en.json @@ -553,13 +553,13 @@ }, "phoneAdd": { "countrySelect": "Click the flag to select your country", - "description": "Please confirm your country code and enter your phone number to activate your Tagfer account. We will send you an SMS message for verification", + "description": "Please confirm your country code and enter your phone number to activate your Tagfer account. We will send you an SMS message for verification.", "rateDisclaimer": "Standard messaging and data rates apply", - "title": "Add Phone Number" + "title": "Phone Number" }, "phoneVerify": { - "description": "Verify your phone and activate your Tagfer account. Enter the code that was sent to: {{phoneNumber}}.", - "title": "Verify SMS Code" + "description": "Verify your phone and activate your Tagfer account. Enter the code that was sent to: {{phoneNumber}}", + "title": "Phone Verification" }, "signUp": { "minPassword": "6+ characters", diff --git a/src/screens/auth/LoginScreen.js b/src/screens/auth/LoginScreen.js index 6699490..adb6c60 100644 --- a/src/screens/auth/LoginScreen.js +++ b/src/screens/auth/LoginScreen.js @@ -133,7 +133,7 @@ export default class LoginScreen extends React.Component { this.props.navigation.navigate('signupOne')} + onPress={() => this.props.navigation.navigate('signup1')} disabled={this.state.loading} /> diff --git a/src/screens/auth/SignupScreenOne.js b/src/screens/auth/SignupScreen1.js similarity index 99% rename from src/screens/auth/SignupScreenOne.js rename to src/screens/auth/SignupScreen1.js index 49a79ab..471d564 100644 --- a/src/screens/auth/SignupScreenOne.js +++ b/src/screens/auth/SignupScreen1.js @@ -49,7 +49,7 @@ export default class SignupScreenOne extends React.Component { const errorObject = getErrorObject(errors.AUTH_EMAIL_ALREADY_EXISTS); this.setState({ error: errorObject.desc }); } else { - this.props.navigation.navigate('signupTwo'); + this.props.navigation.navigate('signup2'); } }).catch(() => { this.setState({ loading: false }); diff --git a/src/screens/auth/SignupScreenTwo.js b/src/screens/auth/SignupScreen2.js similarity index 98% rename from src/screens/auth/SignupScreenTwo.js rename to src/screens/auth/SignupScreen2.js index 39a4b70..f5c4f92 100644 --- a/src/screens/auth/SignupScreenTwo.js +++ b/src/screens/auth/SignupScreen2.js @@ -16,7 +16,8 @@ export default class SignupScreenTwo extends React.Component { static navigationOptions = { title: strings('userCreateFlow.createTagferId.title'), headerStyle: { borderBottomWidth: 0 }, - headerTintColor: '#0D497E' + headerTintColor: '#0D497E', + headerRight: 2/6 }; constructor(props) { @@ -77,7 +78,7 @@ export default class SignupScreenTwo extends React.Component { const errorObject = getErrorObject(errors.AUTH_UID_ALREADY_EXISTS); this.setState({ error: errorObject.desc }); } else { - this.props.navigation.navigate('signupThree'); + this.props.navigation.navigate('signup3a'); } }).catch(() => { this.setState({ loading: false }); diff --git a/src/screens/auth/SignupScreen3a.js b/src/screens/auth/SignupScreen3a.js new file mode 100644 index 0000000..f1e694a --- /dev/null +++ b/src/screens/auth/SignupScreen3a.js @@ -0,0 +1,166 @@ +import React from 'react'; +import { Dimensions, KeyboardAvoidingView, Platform, ScrollView, Text, View } from 'react-native'; +import { Button } from 'react-native-elements'; +import { TextInputMask } from 'react-native-masked-text'; +import CountryPicker from 'react-native-country-picker-modal'; + +import { strings } from '../../locales/i18n'; +import { showFlashErrorMessage } from '../../utils/errorHandler'; +import { fetchAuth } from '../../utils/fetch'; + +import colors from '../../config/colors.json'; +import errors from '../../config/errors'; +import endpoints from '../../config/apiEndpoints'; + + +export default class SignupScreenThreeA extends React.Component { + static navigationOptions = { + title: strings('userCreateFlow.phoneAdd.title'), + headerStyle: { borderBottomWidth: 0 }, + headerTintColor: '#0D497E', + headerRight: 3/6 + }; + + constructor(props) { + super(props); + this.state = { + phoneNumber: '', + country: { + cca2: 'US', + callingCode: '1' + } + }; + } + + // Reformat the masked number and send the request + onSaveButtonPress() { + this.setState({ loading: true }); + const phoneNumber = `+${this.state.country.callingCode}${this.input.getRawValue()}`; + fetchAuth(endpoints.sendCode, { phoneNumber }).then(res => { + if (res.error) { + showFlashErrorMessage(res.error); + } else { + this.props.navigation.navigate('signup3b', { phoneNumber }); + } + }).catch(() => showFlashErrorMessage(errors.APP_NETWORK_ERROR)) + .finally(() => this.setState({ loading: false })); + } + + // 14 chars include mask |(999) 999-9999|, its actually 10 + isDisabled() { + return this.state.phoneNumber.length < 14; + } + + renderContent() { + return ( + + + {/* INSTRUCTIONS */} + {strings('userCreateFlow.phoneAdd.description')} + + + {/* COUNTRY FLAG */} + this.setState({ country })} + onClose={() => this.props.navigation.goBack()} + cca2={this.state.country.cca2} + translation='eng' + styles={{ itemCountryName: { borderBottomWidth: 0 } }} + + /> + {/* CALLING CODE */} + +{this.state.country.callingCode} + + {/* PHONE NUMBER */} + (this.input = ref)} + value={this.state.phoneNumber} + underlineColorAndroid={'transparent'} + autoCapitalize={'none'} + autoCorrect={false} + onChangeText={(phoneNumber) => this.setState({ phoneNumber })} + placeholder={'Phone Number'} + keyboardType={Platform.OS === 'ios' ? 'number-pad' : 'numeric'} + style={styles.textInput} + type={'cel-phone'} + options={{ withDDD: true, dddMask: '(999) 999-9999' }} + autoFocus + /> + + + + + {/* SAVE AND CONTINUE BUTTON */} +