Skip to content

Instantly share code, notes, and snippets.

@jrdn91
Created February 11, 2019 20:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jrdn91/f6656f7eb90d161b39f9fe7701d22dd1 to your computer and use it in GitHub Desktop.
Save jrdn91/f6656f7eb90d161b39f9fe7701d22dd1 to your computer and use it in GitHub Desktop.
Login Page
import React from 'react';
import { connect } from 'react-redux';
import { Container, Button, Form, Item, Input, Label } from 'native-base';
import {
NativeModules,
LayoutAnimation,
Animated,
View,
Keyboard,
ScrollView,
TouchableWithoutFeedback,
Image,
ImageBackground,
Text,
Dimensions,
Easing,
StatusBar,
Platform,
AsyncStorage
} from 'react-native';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'
import { withTheme } from 'react-native-material-ui';
import styles from './styles';
import { login, register, sendPin, socialLogin } from '../../actions/authentication';
import { openSnackBar } from '../../actions/snack-bar';
import { formatNumber } from 'libphonenumber-js'
import Dialog from '../../components/Dialog';
import PinInput from '../../components/PinInput';
import PhoneInput from '../../components/PhoneInput';
import { getGroups } from '../../actions/groups';
import theme from '../../native-base-theme/variables/material';
import LoadingButton from 'react-native-material-loading-button';
import { LoginButton, LoginManager, AccessToken, GraphRequest, GraphRequestManager } from 'react-native-fbsdk';
import { GoogleSignin, statusCodes } from 'react-native-google-signin';
import packageJson from '../../package.json'
import { updateUser } from '../../actions/users';
import { pubnub } from '../../components/App';
const { version } = packageJson;
const { UIManager } = NativeModules;
const graphRequestManager = new GraphRequestManager();
GoogleSignin.configure();
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true);
export class Login extends React.Component {
initialHeight = Dimensions.get('window').height;
initialWidth = Dimensions.get('window').width;
state = {
phone: '',
password: '',
email: '',
logoWrapperHeight: '100%',
inputViewTop: this.initialHeight,
slideInAnim: new Animated.Value(this.initialHeight),
slideRegisterAnim: new Animated.Value(this.initialWidth),
trackWidth: 0,
isRegister: false,
inputs: {},
forgotLoginDialogOpen: false,
pinSent: false,
forgotLoginDialogErrorMessage: '',
forgotLoginDialogErrorOpen: false,
forgotLoginModalVisible: false,
snackBarMessage: "",
snackBarOpen: false,
enterPhoneModalVisible: false
};
componentDidMount = () => {
console.log('mounted')
setTimeout(() => {
Animated.timing(
this.state.slideInAnim,
{
toValue: 0,
duration: 650,
}
).start();
// LayoutAnimation.configureNext({
// duration: 650,
// create: {
// type: LayoutAnimation.Types.easeOut,
// property: LayoutAnimation.Properties.height
// },
// update: {
// type: LayoutAnimation.Types.easeOut
// },
// });
LayoutAnimation.easeInEaseOut();
this.setState({ logoWrapperHeight: '35%' })
}, 500);
}
_focusNextField(id) {
this.state.inputs[id]._root.focus();
}
_handleLogin = () => {
Keyboard.dismiss()
setTimeout(() => {
this.setState({
loggingIn: true
})
const { phone, password } = this.state
this.props.dispatch(login({ phone: `+1${phone.replace(/[()\s-]/g, '')}`, password })).then((res) => {
this.setState({
loggingIn: false
})
AsyncStorage.getItem('onBoarded').then((res)=> {
if (res === 'true') {
this.props.navigation.navigate("Map")
} else {
this.props.navigation.navigate("OnBoarding")
}
})
this.props.dispatch(getGroups()).then(res => {
}).catch(err => console.log(err.response.body))
}).catch((err) => {
this.setState({
loggingIn: false
})
this.props.dispatch(openSnackBar({
message: 'There was an error logging in'
}))
console.log(err);
})
}, 40);
}
_handleRegister = () => {
Keyboard.dismiss()
this.setState({
registering: true
})
const { phone, email, firstName, lastName, password } = this.state
this.props.dispatch(register({ phone: `+1${phone.replace(/[()\s-]/g, '')}`, email, first_name: firstName, last_name: lastName, password, login_type: 'password' })).then((res) => {
this.setState({
registering: false
})
this.props.dispatch(getGroups()).then(res => {
AsyncStorage.getItem('onBoarded').then((res)=> {
if (res === 'true') {
this.props.navigation.navigate("Map")
} else {
this.props.navigation.navigate("OnBoarding")
}
})
}).catch(err => console.log(err.response.body))
}).catch((err) => {
this.setState({
registering: false
})
console.log(err);
let message = "There was an error with your registration";
if (err.message === "users create error") {
if (err.error.code === "ER_DUP_ENTRY") {
if (err.error.sqlMessage.indexOf("for key 'phone'") > -1) {
message = "That phone number is already in our system";
}
}
}
this.props.dispatch(
openSnackBar({
message
})
);
})
}
_handleSwitch = () => {
if (this.state.isRegister) {
// is register, switch to login
Animated.timing(
this.state.slideRegisterAnim,
{
toValue: this.initialWidth,
duration: 250,
easing: Easing.easeInOut
}
).start();
setTimeout(() => {
this.setState({ isRegister: false });
}, 250);
} else {
// is not login, switch to register
Animated.timing(
this.state.slideRegisterAnim,
{
toValue: 0,
duration: 250,
easing: Easing.easeInOut
}
).start();
setTimeout(() => {
this.setState({ isRegister: true });
}, 250);
}
}
_sendPin = () => {
if (!this.state.phone) {
this.setState({
snackBarMessage: "Please enter your mobile number",
snackBarOpen: true
});
return;
}
this.props.dispatch(sendPin(`+1${this.state.phone.replace(/[()\s-]/g, '')}`)).then((res) => {
this.setState({ pinSent: true });
}).catch((err) => {
console.log(err);
this.setState({
snackBarMessage: "There was an error texting pin number",
snackBarOpen: true
});
})
}
_checkPin = () => {
const { phone, auth_code } = this.state
this.props.dispatch(login({ phone: `+1${phone.replace(/[()\s-]/g, '')}`, auth_code })).then((res) => {
this.props.dispatch(getGroups()).then(res => {
AsyncStorage.getItem('onBoarded').then((res)=> {
if (res === 'true') {
this.props.navigation.navigate("Map")
} else {
this.props.navigation.navigate("OnBoarding")
}
})
}).catch(err => console.log(err.response.body))
}).catch((err) => {
console.log(err);
})
}
_handleOk = () => {
if (this.state.pinSent) {
this._checkPin();
} else {
this._sendPin();
}
}
_handleSendPin = () => {
this.setState({
forgotLoginModalVisible: true
})
}
_handlePhoneSubmit = () => {
if (this.state.pinSent) {
const { phone, auth_code } = this.state
this.setState({
checkingPin: true
})
this.props.dispatch(login({ phone: `+1${phone.replace(/[()\s-]/g, '')}`, auth_code })).then((res) => {
const { email, first_name, last_name, ...rest } = this.state.socialData
this.props.dispatch(updateUser(res.id, rest)).then(res => {
this.props.dispatch(getGroups()).then(res => {
this.setState({
checkingPin: false
})
AsyncStorage.getItem('onBoarded').then((res)=> {
if (res === 'true') {
this.props.navigation.navigate("Map")
} else {
this.props.navigation.navigate("OnBoarding")
}
})
}).catch(err => console.log(err.response.body))
})
}).catch((err) => {
console.log(err)
if (err.type === 'not found') {
const { phone } = this.state
this.props.dispatch(register({ phone: `+1${phone.replace(/[()\s-]/g, '')}`, ...this.state.socialData })).then((res) => {
this.props.dispatch(getGroups()).then(res => {
AsyncStorage.getItem('onBoarded').then((res)=> {
if (res === 'true') {
this.props.navigation.navigate("Map")
} else {
this.props.navigation.navigate("OnBoarding")
}
})
}).catch(err => {
console.log(err)
this.props.dispatch(
openSnackBar({
message: "There was an error registering this number"
})
);
this.setState({
loggingInWithFacebook: false,
loggingInWithGoogle: false
})
})
}).catch((err) => {
console.log(err);
this.props.dispatch(
openSnackBar({
message: "There was an error with your registration"
})
);
this.setState({
loggingInWithFacebook: false,
loggingInWithGoogle: false
})
})
} else {
this.setState({
checkingPin: false
})
this.props.dispatch(
openSnackBar({
message: "There was an error with this registration"
})
);
}
})
} else {
this._sendPin();
}
}
_handleSocialLogin = (data) => {
this.props.dispatch(socialLogin(data)).then(res => {
this.props.dispatch(getGroups()).then(res => {
this.setState({
loggingInWithFacebook: false,
loggingInWithGoogle: false
})
AsyncStorage.getItem('onBoarded').then((res)=> {
if (res === 'true') {
this.props.navigation.navigate("Map")
} else {
this.props.navigation.navigate("OnBoarding")
}
})
}).catch(err => console.log(err.response.body))
}).catch(err => {
console.log(err)
pubnub.publish({
message: {
method: '_handleSocialLogin',
file: 'components/app/index.js',
varName: 'social login .catch',
err
},
channel: `debug_channel`,
sendByPost: false, // true to send via post
storeInHistory: false, //override default storage options
});
if (err.err === 'not found') {
console.log('is not found')
this.setState({
enterPhoneModalVisible: true,
socialData: data
})
} else {
this.props.dispatch(
openSnackBar({
message: "There was an error logging in, please try again"
})
);
this.setState({
loggingInWithFacebook: false,
loggingInWithGoogle: false
})
}
})
}
_facebookGraphCallback = (err, data) => {
if (err) {
pubnub.publish({
message: {
method: '_facebookGraphCallback',
file: 'components/app/index.js',
varName: 'graph err',
err
},
channel: `debug_channel`,
sendByPost: false, // true to send via post
storeInHistory: false, //override default storage options
});
this.setState({
loggingInWithFacebook: false
})
console.log('graph error',err)
} else {
const {id, ...rest} = data
this._handleSocialLogin({...rest, facebook: id})
}
}
facebookLogin = async () => {
// native_only config will fail in the case that the user has
// not installed in his device the Facebook app. In this case we
// need to go for webview.
let result;
try {
this.setState({
loggingInWithFacebook: true
})
console.log(LoginManager)
LoginManager.logOut();
LoginManager.setLoginBehavior('native');
result = await LoginManager.logInWithReadPermissions(['public_profile', 'email']);
pubnub.publish({
message: {
method: 'facebookLogin',
file: 'components/app/index.js',
varName: 'try LoginManager.logInWithReadPermissions native result',
result: result
},
channel: `debug_channel`,
sendByPost: false, // true to send via post
storeInHistory: false, //override default storage options
});
} catch (nativeError) {
console.log(nativeError)
pubnub.publish({
message: {
method: 'facebookLogin',
file: 'components/app/index.js',
varName: 'try LoginManager.logInWithReadPermissions native catch',
err: nativeError
},
channel: `debug_channel`,
sendByPost: false, // true to send via post
storeInHistory: false, //override default storage options
});
this.setState({
loggingInWithFacebook: false
})
this.props.dispatch(openSnackBar({
message: "There was an error logging in, please try again"
}))
}
// handle the case that users clicks cancel button in Login view
console.log(result)
if (result.isCancelled) {
this.setState({
loggingInWithFacebook: false
})
} else {
// Create a graph request asking for user information
this.FBGraphRequest('first_name, last_name, id, email', this.FBLoginCallback);
}
}
FBGraphRequest = async (fields, callback) => {
const accessData = await AccessToken.getCurrentAccessToken();
// Create a graph request asking for user information
const infoRequest = new GraphRequest('/me', {
accessToken: accessData.accessToken,
parameters: {
fields: {
string: fields
}
}
}, callback);
// Execute the graph request created above
new GraphRequestManager().addRequest(infoRequest).start();
}
FBLoginCallback = async (error, result) => {
if (error) {
pubnub.publish({
message: {
method: 'FBLoginCallback',
file: 'components/app/index.js',
varName: 'callback error',
err: error
},
channel: `debug_channel`,
sendByPost: false, // true to send via post
storeInHistory: false, //override default storage options
});
this.setState({
loggingInWithFacebook: false
});
} else {
// Retrieve and save user details in state. In our case with
// Redux and custom action saveUser
const {id, picture, ...rest} = result
this._handleSocialLogin({...rest, facebook: id})
}
}
// _loginWithFacebook = () => {
// // LoginManager.logOut();
// try {
// this.setState({
// loggingInWithFacebook: true
// })
// AccessToken.getCurrentAccessToken().then(
// (data) => {
// if (data) {
// try {
// if (graphRequestManager.requestBatch.length === 0) {
// let infoRequest = new GraphRequest('/me', {parameters: {fields: {string: 'first_name,last_name,email'}}}, this._facebookGraphCallback);
// graphRequestManager.addRequest(infoRequest)
// }
// graphRequestManager.start()
// } catch (e) {
// pubnub.publish({
// message: {
// method: '_loginWithFacebook',
// file: 'components/app/index.js',
// varName: 'try infoRequest catch - had no data',
// err: e
// },
// channel: `debug_channel`,
// sendByPost: false, // true to send via post
// storeInHistory: false, //override default storage options
// });
// }
// } else {
// LoginManager.logOut()
// LoginManager.logInWithReadPermissions(['public_profile','email']).then((result) => {
// if (!result.isCancelled) {
// try {
// let infoRequest = new GraphRequest('/me', {parameters: {fields: {string: 'first_name,last_name,email'}}}, this._facebookGraphCallback);
// graphRequestManager.addRequest(infoRequest).start()
// } catch (e) {
// pubnub.publish({
// message: {
// method: '_loginWithFacebook',
// file: 'components/app/index.js',
// varName: 'try infoRequest catch - had data',
// err: e
// },
// channel: `debug_channel`,
// sendByPost: false, // true to send via post
// storeInHistory: false, //override default storage options
// });
// console.log(e)
// }
// }
// }, (error) => {
// pubnub.publish({
// message: {
// method: '_loginWithFacebook',
// file: 'components/app/index.js',
// varName: 'LoginManager.logInWithReadPermissions error',
// err: error
// },
// channel: `debug_channel`,
// sendByPost: false, // true to send via post
// storeInHistory: false, //override default storage options
// });
// this.setState({
// loggingInWithFacebook: false
// })
// });
// }
// }
// ).catch(err => {
// this.setState({
// loggingInWithFacebook: false
// })
// pubnub.publish({
// message: {
// method: '_loginWithFacebook',
// file: 'components/app/index.js',
// varName: 'AccessToken.getCurrentAccessToken.catch',
// err
// },
// channel: `debug_channel`,
// sendByPost: false, // true to send via post
// storeInHistory: false, //override default storage options
// });
// console.log(err)
// })
// } catch(e) {
// pubnub.publish({
// message: {
// method: '_loginWithFacebook',
// file: 'components/app/index.js',
// varName: 'catch',
// err: e
// },
// channel: `debug_channel`,
// sendByPost: false, // true to send via post
// storeInHistory: false, //override default storage options
// });
// console.log(e)
// }
// }
_loginWithGoogle = async () => {
try {
this.setState({
loggingInWithGoogle: true
})
await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
this._handleSocialLogin({google: userInfo.user.id, email: userInfo.user.email, first_name: userInfo.user.givenName, last_name: userInfo.user.familyName})
} catch (error) {
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
// user cancelled the login flow
} else if (error.code === statusCodes.IN_PROGRESS) {
// operation (f.e. sign in) is in progress already
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
// play services not available or outdated
} else {
// some other error happened
}
}
}
render() {
let { firstName, lastName, phone, password, email, trackWidth, slideInAnim, slideRegisterAnim, logoWrapperHeight } = this.state;
const backgroundImage = Platform.OS === 'ios' ? 'background_image' : 'asset:/background_image.png';
const logoImage = Platform.OS === 'ios' ? 'Logo' : 'asset:/Logo.png';
console.log(this.state.enterPhoneModalVisible)
return (
<KeyboardAwareScrollView>
<StatusBar
backgroundColor="transparent"
barStyle="light-content"
/>
<View style={styles.container}>
<Dialog
colorAccent={theme.brandPrimary}
visible={this.state.enterPhoneModalVisible}
onOk={this._handlePhoneSubmit}
onCancel={() => this.setState({ enterPhoneModalVisible: false })}
snackBarMessage={this.state.snackBarMessage}
snackBarOpen={this.state.snackBarOpen}
onSnackBarClose={() => this.setState({ snackBarOpen: false })}
isLoading={this.state.checkingPin}>
<View>
{this.state.pinSent &&
<View>
<Text style={styles.dialogText}>
Enter the pin number we just texted to complete registration
</Text>
<Text style={styles.phoneNumberText}>
{formatNumber(`+1${this.state.phone}`, 'National')}
</Text>
<PinInput onPinComplete={auth_code => this.setState({ auth_code })} />
</View>
}
{!this.state.pinSent &&
<View>
<Text style={styles.dialogText}>
Enter your mobile number to complete registration
</Text>
<Form>
<PhoneInput
value={phone}
onChangeText={(phone) => this.setState({ phone })} />
</Form>
</View>
}
</View>
</Dialog>
<Dialog
colorAccent={theme.brandPrimary}
visible={this.state.forgotLoginModalVisible}
onOk={this._handleOk}
onCancel={() => this.setState({ forgotLoginModalVisible: false, pinSent: false })}
snackBarMessage={this.state.snackBarMessage}
snackBarOpen={this.state.snackBarOpen}
onSnackBarClose={() => this.setState({ snackBarOpen: false })}>
<View>
{this.state.pinSent &&
<View>
<Text style={styles.dialogText}>
We just texted a login pin to
</Text>
<Text style={styles.phoneNumberText}>
{formatNumber(`+1${this.state.phone}`, 'National')}
</Text>
<PinInput onPinComplete={auth_code => this.setState({ auth_code })} />
</View>
}
{!this.state.pinSent &&
<View>
<Text style={styles.dialogText}>
Enter your mobile number and we will text you a pin number
</Text>
<Form>
<PhoneInput
value={phone}
onChangeText={(phone) => this.setState({ phone })} />
</Form>
</View>
}
</View>
</Dialog>
<ImageBackground style={styles.backgroundImage} imageStyle={styles.backgroundImageStyle} source={{uri: backgroundImage}}>
<View style={{flex: 1}}>
<View style={[styles.logoView, {
height: logoWrapperHeight
}]}>
<Image style={styles.logoImage} source={{ uri: logoImage }} />
</View>
<Animated.View style={[styles.inputView, {transform: [{
translateY: slideInAnim
}]}
]}>
<View style={styles.switchView}>
<View style={styles.switchInner} onLayout={(event) => {
this.setState({ trackWidth: event.nativeEvent.layout.width })
}}>
<Animated.View style={{
width: '50%',
position: 'absolute',
zIndex: 1300,
transform: [{
translateX: slideRegisterAnim.interpolate({
inputRange: [0, this.initialWidth],
outputRange: [trackWidth / 2, 0]
})
}]
}}>
<Button rounded block onPress={this._handleSwitch} style={styles.switchButton}><Text style={styles.switchButtonText}>{this.state.isRegister ? "REGISTER" : "LOGIN"}</Text></Button>
</Animated.View>
<TouchableWithoutFeedback onPress={this._handleSwitch}>
<View style={styles.switchSlideTextView}>
<Text style={styles.switchSlideText}>LOGIN</Text>
</View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={this._handleSwitch}>
<View style={styles.switchSlideTextView}>
<Text style={styles.switchSlideText}>REGISTER</Text>
</View>
</TouchableWithoutFeedback>
</View>
</View>
<View style={styles.inputWrapper}>
<Animated.ScrollView style={[styles.formView, styles.loginForm, {
transform: [{
translateX: slideRegisterAnim.interpolate({
inputRange: [0, this.initialWidth],
outputRange: [-this.initialWidth, 0]
})
}]
}]} keyboardShouldPersistTaps='handled'>
<Form>
<PhoneInput
value={phone}
onChangeText={(phone) => this.setState({ phone })} />
<Item style={{ marginLeft: 0 }} floatingLabel>
<Label>Password</Label>
<Input
secureTextEntry
value={password}
onChangeText={ (password) => this.setState({ password }) }
/>
</Item>
</Form>
<View style={styles.loginButton}>
<LoadingButton
text={'LOGIN'}
disabled={!phone || !password}
isLoading={this.state.loggingIn}
onPress={this._handleLogin}
color={theme.brandPrimary}
/>
<Button transparent block onPress={this._handleSendPin}><Text>FORGOT LOGIN?</Text></Button>
</View>
<View style={styles.loginWithView}>
<View style={styles.loginWithBar}><View style={styles.bar}/></View>
<View style={styles.loginWithTextView}>
<Text style={styles.loginWithText}>log in with</Text>
</View>
<View style={styles.loginWithBar}><View style={styles.bar}/></View>
</View>
<View style={styles.socialButtons}>
<View style={styles.buttonWrapper}>
<LoadingButton
text={'FACEBOOK'}
isLoading={this.state.loggingInWithFacebook}
onPress={this.facebookLogin}
color={'#4367b3'}
/>
</View>
<View style={styles.buttonWrapper}>
<LoadingButton
text={'GOOGLE'}
isLoading={this.state.loggingInWithGoogle}
onPress={this._loginWithGoogle}
color={'#db4437'}
/>
</View>
</View>
</Animated.ScrollView>
<Animated.ScrollView style={[styles.formView, styles.registerForm, {
transform: [{
translateX: slideRegisterAnim
}]
}]} keyboardShouldPersistTaps='handled'>
<Form>
<Item style={{ marginLeft: 0 }} floatingLabel>
<Label>First Name</Label>
<Input
value={firstName}
blurOnSubmit={ false }
onSubmitEditing={() => {
this._focusNextField('lastName');
}}
returnKeyType={ "next" }
onChangeText={ (firstName) => this.setState({ firstName }) }
getRef={ input => {
this.state.inputs['firstName'] = input;
}}
/>
</Item>
<Item style={{ marginLeft: 0 }} floatingLabel>
<Label>Last Name</Label>
<Input
value={lastName}
blurOnSubmit={ false }
onSubmitEditing={() => {
this._focusNextField('registerPhone');
}}
returnKeyType={ "next" }
onChangeText={ (lastName) => this.setState({ lastName }) }
getRef={ input => {
this.state.inputs['lastName'] = input;
}}
/>
</Item>
<PhoneInput
value={phone}
onChangeText={(phone) => this.setState({ phone })}
getRef={ input => {
this.state.inputs['registerPhone'] = input;
}}
/>
<Item style={{ marginLeft: 0 }} floatingLabel>
<Label>Email</Label>
<Input
keyboardType="email-address"
autoCapitalize="none"
value={email}
blurOnSubmit={ false }
onSubmitEditing={() => {
this._focusNextField('registerPassword');
}}
returnKeyType={ "next" }
onChangeText={ (email) => this.setState({ email }) }
getRef={ input => {
this.state.inputs['email'] = input;
}}
/>
</Item>
<Item style={{ marginLeft: 0 }} floatingLabel>
<Label>Password</Label>
<Input
secureTextEntry
value={password}
blurOnSubmit={ true }
returnKeyType={ "done" }
onChangeText={ (password) => this.setState({ password }) }
getRef={ input => {
this.state.inputs['registerPassword'] = input;
}}
/>
</Item>
</Form>
<View style={styles.loginButton}>
<LoadingButton
text={'REGISTER'}
isLoading={this.state.registering}
onPress={this._handleRegister}
color={theme.brandPrimary}
/>
</View>
</Animated.ScrollView>
</View>
<View style={styles.copyRightWrapper}>
<Text style={styles.copyRightText}>copyright &copy; {new Date().getFullYear()} MobileKeep | Version {version} - ({process.env.NODE_ENV})</Text>
</View>
</Animated.View>
</View>
</ImageBackground>
</View>
</KeyboardAwareScrollView>
);
}
}
const mapStateToProps = state => ({
authentication: state.authentication
});
export default connect(mapStateToProps)(withTheme(Login));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment