Signup Screen One

This commit is contained in:
Omar 2018-12-27 20:18:25 -08:00
parent ef9210fab0
commit b55af52e94
9 changed files with 288 additions and 51 deletions

View File

@ -7,6 +7,10 @@ const endpoints = {
passwordReset: {
method: 'POST',
url: `${baseurl}/auth/passwordReset`
},
emailExists: {
method: 'GET',
url: `${baseurl}/auth/email/:email/exists`
}
};

View File

@ -1,12 +1,14 @@
import { createStackNavigator, createAppContainer } from 'react-navigation';
import WelcomeScreen from '../screens/onboaring/WelcomeScreen';
import LoginScreen from '../screens/auth/LoginScreen';
import SignupScreenOne from '../screens/auth/SignScreenOne';
import ForgotPasswordScreen from '../screens/auth/ForgotPasswordScreen';
const StackNavigator = createStackNavigator({
welcome: WelcomeScreen,
login: LoginScreen,
forgotPassword: ForgotPasswordScreen
forgotPassword: ForgotPasswordScreen,
signupOne: SignupScreenOne
});
const AppNavigator = createAppContainer(StackNavigator);

View File

@ -25,7 +25,8 @@
"share": "Share Now",
"skip": "Skip",
"uploadImage": "Upload Image",
"takePhoto": "Take Photo"
"takePhoto": "Take Photo",
"saveAndContinue": "Save and Continue"
},
"contactFields": {
"about_labelText": "About",
@ -559,7 +560,7 @@
"title": "Verify SMS Code"
},
"signUp": {
"minPassword": "Must be at least 8 characters.",
"minPassword": "6+ characters",
"password": "Password",
"password2": "Confirm Password",
"title": "Sign Up",

View File

@ -6,7 +6,8 @@ import { showMessage } from 'react-native-flash-message';
import { FormLabel, Spacer } from '../../components/common';
import { fetchAuth } from '../../utils/fetch';
import { strings } from '../../locales/i18n';
import { getErrorDescription } from '../../utils/errorHandler';
import { showAuthErrorMessage } from '../../utils/errorHandler';
import { isEmail } from '../../utils/aux';
import endpoints from '../../config/apiEndpoints';
import colors from '../../config/colors.json';
@ -32,41 +33,17 @@ export default class ForgotPasswordScreen extends React.Component {
Keyboard.dismiss();
this.setState({ loading: true });
const body = { email: this.state.email };
console.log(body);
fetchAuth(endpoints.passwordReset, body).then(res => {
this.setState({ loading: false });
console.log(res);
if (res.error) {
this.showErrorMessage(res.error);
showAuthErrorMessage(res.error);
} else {
this.showSuccessMessage();
}
}).catch(error => {
this.setState({ loading: false });
this.showErrorMessage(error);
});
}
/**
* Simple regex to check if the input is an email.
*/
isNotEmail() {
const regex = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/;
return !regex.test(this.state.email);
}
/**
* Display a flash message at the top of the screen.
* @param {String} error one of the error codes in {@see config.error.js}
*/
showErrorMessage(error) {
showMessage({
message: 'Authentication Error',
description: getErrorDescription(error),
type: 'danger',
icon: 'auto',
duration: 4000
showAuthErrorMessage(error);
});
}
@ -127,7 +104,7 @@ export default class ForgotPasswordScreen extends React.Component {
onPress={this.onRequestResetPress.bind(this)}
title={strings('settings.forgotPasswordScreen.submitButton')}
buttonStyle={styles.buttonStyle}
disabled={this.isNotEmail()}
disabled={!isEmail(this.state.email)}
loading={this.state.loading}
fontSize={20}
/>

View File

@ -1,12 +1,11 @@
import React from 'react';
import { Image, KeyboardAvoidingView, Platform, Dimensions, View, Text, Keyboard, TouchableWithoutFeedback } from 'react-native';
import { Button, FormInput } from 'react-native-elements';
import { showMessage } from 'react-native-flash-message';
import { FormLabel, Spacer, TextButton } from '../../components/common';
import { strings } from '../../locales/i18n';
import { fetchAuth } from '../../utils/fetch';
import { getErrorDescription } from '../../utils/errorHandler';
import { showAuthErrorMessage } from '../../utils/errorHandler';
import endpoints from '../../config/apiEndpoints';
import colors from '../../config/colors.json';
@ -38,14 +37,14 @@ export default class LoginScreen extends React.Component {
fetchAuth(endpoints.login, body).then(res => {
if (res.error) {
this.setState({ loading: false, password: '' });
this.showErrorMessage(res.error);
showAuthErrorMessage(res.error);
} else {
this.setState({ loading: false });
console.log(res.sessionId);
}
}).catch(error => {
this.setState({ loading: false, password: '' });
this.showErrorMessage(error);
showAuthErrorMessage(error);
});
}
@ -69,20 +68,6 @@ export default class LoginScreen extends React.Component {
return this.state.id.length < 1 || this.state.password.length < 6;
}
/**
* Display a flash message at the top of the screen.
* @param {String} error one of the error codes in {@see config.error.js}
*/
showErrorMessage(error) {
showMessage({
message: 'Authentication Error',
description: getErrorDescription(error),
type: 'danger',
icon: 'auto',
duration: 3000
});
}
renderContent() {
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
@ -140,7 +125,7 @@ export default class LoginScreen extends React.Component {
<TextButton
leftText={strings('userLoginScreen.userCreateText')}
rightText={strings('userLoginScreen.userCreateButton')}
onPress={() => this.props.navigation.navigate('signup1')}
onPress={() => this.props.navigation.navigate('signupOne')}
disabled={this.state.loading}
/>
</View>

View File

@ -0,0 +1,245 @@
import React, { Component } from 'react';
import { Dimensions, Image, KeyboardAvoidingView, Platform, Text, View, ScrollView, Keyboard } from 'react-native';
import { Button, FormInput } from 'react-native-elements';
import { FormLabel, Spacer } from '../../components/common';
import { strings } from '../../locales/i18n';
import { fetchAuth } from '../../utils/fetch';
import { showAuthErrorMessage } from '../../utils/errorHandler';
import { isEmail, isEmpty } from '../../utils/aux';
import logo from '../../../assets/logo-round.png';
import colors from '../../config/colors.json';
import endpoints from '../../config/apiEndpoints';
import errors from '../../config/errors';
export default class SignupScreenOne extends Component {
static navigationOptions = {
header: null
}
constructor(props) {
super(props);
this.state = {
firstName: '',
lastName: '',
email: '',
password: '',
loading: false
};
}
/**
* Handles the button press by sending the request, based on the response received either navigates the user
* to SignupScreenTwo or displays an error message.
*/
onSaveButtonPress() {
Keyboard.dismiss();
this.setState({ loading: true });
const endpoint = this.addParamsToEndpoint(endpoints.emailExists, this.state.email);
fetchAuth(endpoint).then(res => {
this.toggleLoadingSpinner();
if (res.error) {
showAuthErrorMessage(res.error);
} else if (res.result === true) {
this.setState({ email: '' });
showAuthErrorMessage(errors.AUTH_EMAIL_ALREADY_EXISTS);
} else {
console.log(res.result);
}
}).catch(error => {
this.toggleLoadingSpinner();
showAuthErrorMessage(error);
});
}
/**
* Replaces the default express `:email` parameter with the actual value.
* @param {Object} endpoint takes an endpoint object @see config/endpoints
* @param {String} email
*/
addParamsToEndpoint(endpoint, email) {
const endpointWithParams = { ...endpoint };
endpointWithParams.url = endpoint.url.replace(':email', email);
return endpointWithParams;
}
/**
* Boolen function that says if the button is disabled based on if the fields are empty.
*/
isButtonDisabled() {
const { firstName, lastName, email, password } = this.state;
return isEmpty(firstName) || isEmpty(lastName) || !isEmail(email) || password.length < 6;
}
/**
* Utility function that toggles the state of the loading variable, true to false and false to true.
*/
toggleLoadingSpinner() {
this.setState({ loading: !this.state.loading });
}
renderContent() {
return (
<ScrollView contentContainerStyle={{ flexGrow: 1, backgroundColor: colors.white }} keyboardShouldPersistTaps="handled" >
<View style={{ flex: 1 }}>
{/* LOGO */}
<View style={styles.logoContainerStyle}>
<Image source={logo} />
</View>
{/* HEADER */}
<View>
<Text style={styles.headerStyle} >Signup</Text>
</View>
{/* TEXT INPUTS */}
<View style={{ flex: 3 }}>
<Spacer />
<FormLabel
required
labelText={strings('common.contactFields.firstName_labelText').toUpperCase()}
textStyle={styles.labelStyle}
/>
<FormInput
value={this.state.firstName}
onChangeText={firstName => this.setState({ firstName })}
placeholder={strings('common.contactFields.firstName_placeholder')}
autoCorrect={false}
autoCapitalize='words'
inputStyle={styles.inputTextStyle}
/>
<Spacer />
<FormLabel
required
labelText={strings('common.contactFields.lastName_labelText').toUpperCase()}
textStyle={styles.labelStyle}
/>
<FormInput
value={this.state.lastName}
onChangeText={lastName => this.setState({ lastName })}
placeholder={strings('common.contactFields.lastName_placeholder')}
autoCorrect={false}
autoCapitalize='words'
inputStyle={styles.inputTextStyle}
/>
<Spacer />
<FormLabel
required
labelText={strings('common.contactFields.email_labelText').toUpperCase()}
textStyle={styles.labelStyle}
/>
<FormInput
value={this.state.email}
onChangeText={email => this.setState({ email })}
placeholder={strings('common.contactFields.email_or_tagferid_placeholder')}
autoCorrect={false}
autoCapitalize='none'
inputStyle={styles.inputTextStyle}
/>
<Spacer />
<FormLabel
required
labelText={strings('userCreateFlow.signUp.password').toUpperCase()}
textStyle={styles.labelStyle}
/>
<FormInput
secureTextEntry
value={this.state.password}
onChangeText={password => this.setState({ password })}
placeholder={strings('userCreateFlow.signUp.minPassword')}
autoCorrect={false}
autoCapitalize='none'
inputStyle={styles.inputTextStyle}
/>
<Spacer />
<View style={{ marginTop: 20 }}>
<Text style={styles.freeTextStyle}>
{strings('userCreateFlow.termsLeft')}
</Text>
<Text style={styles.termsButtonStyle} onPress={() => this.props.navigation.navigate('termsAndConditions')}>
{strings('userCreateFlow.termsRight')}
</Text>
</View>
</View>
<View>
<Button
onPress={this.onSaveButtonPress.bind(this)}
title={strings('common.buttons.saveAndContinue')}
buttonStyle={styles.buttonStyle}
disabled={this.isButtonDisabled()}
loading={this.state.loading}
fontSize={20}
iconRight={{ name: 'chevron-right', color: colors.white, size: 30, style: { marginLeft: 0 } }}
/>
</View>
</View>
</ScrollView>
);
}
render() {
if (Platform.OS === 'android') {
return this.renderContent();
}
return (
<KeyboardAvoidingView style={{ flex: 1 }} behavior="padding" >
{this.renderContent()}
</KeyboardAvoidingView>
);
}
}
const SCREEN_WIDTH = Dimensions.get('window').width;
const styles = {
headerStyle: {
color: colors.headerBlue,
fontWeight: 'bold',
marginLeft: 20,
fontSize: 24
},
logoContainerStyle: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
marginTop: 50
},
logoStyle: {
width: '100%',
height: '100%'
},
buttonStyle: {
width: SCREEN_WIDTH * 0.9,
marginTop: 15,
marginBottom: 30,
borderRadius: 5,
backgroundColor: colors.buttonBlue
},
inputTextStyle: {
marginLeft: 15,
color: colors.black
},
freeTextStyle: {
marginLeft: 20,
fontSize: 18,
color: '#111111'
},
termsButtonStyle: {
fontSize: 18,
color: colors.grey,
marginLeft: 20,
fontWeight: 'bold'
},
labelStyle: {
color: colors.labelBlue
}
};

8
src/utils/aux.js Normal file
View File

@ -0,0 +1,8 @@
export function isEmail(email) {
const regex = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/;
return regex.test(email);
}
export function isEmpty(value) {
return !value;
}

View File

@ -1,3 +1,5 @@
import { showMessage } from 'react-native-flash-message';
import errors from '../config/errors';
export function getErrorDescription(error) {
@ -18,3 +20,16 @@ export function getErrorDescription(error) {
default: return 'Oops, an error occured! Please try again.';
}
}
/**
* Display a flash message at the top of the screen.
* @param {String} error one of the error codes in @see config/error.js
*/
export function showAuthErrorMessage(error) {
showMessage({
message: 'Authentication Error',
description: getErrorDescription(error),
type: 'danger',
icon: 'auto',
duration: 3000
});
}

View File

@ -11,4 +11,4 @@ export function fetchAuth(endpoint, body) {
};
return fetch(endpoint.url, request).then(res => res.json());
}
}