Created
April 14, 2023 14:05
-
-
Save bosz/ce0b95e91ce8f591ebb35058e0b2c5a1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {useState, useEffect} from 'react'; | |
import { | |
View, | |
Alert, | |
ScrollView, | |
TextInput, | |
TouchableOpacity, | |
ActivityIndicator, | |
} from 'react-native'; | |
import auth from '@react-native-firebase/auth'; | |
import {MyPage, Text, Navbar, SelectInput} from '../../components'; | |
import Toast from 'react-native-toast-message'; | |
import {COUNTRY_PHONE_EXTENSION_CODES} from './../../res/data'; | |
import styles from './PhoneAuth.style'; | |
import theme from '../../style/theme'; | |
import {API_URL} from '@env'; | |
import {generateValidationErrorString, STORAGE} from '../../utility'; | |
import {useDispatch, useSelector} from 'react-redux'; | |
import {onSignupSuccesfull, setAuthModalStatus} from '../../redux/profileSlice'; | |
const Input = props => { | |
return ( | |
<TextInput | |
{...props} | |
placeholder={props.placeholder ?? 'Insert text here'} | |
style={[styles.textInput, props.style]} | |
onChangeText={props.onChangeText} | |
keyboardType={props.keyboardType} | |
secureTextEntry={props.secureTextEntry} | |
autoCapitalize={props.autoCapitalize ?? 'sentences'} | |
defaultValue={props.value} | |
placeholderTextColor="#35465d44" | |
/> | |
); | |
}; | |
const PhoneAuth = props => { | |
const dispatch = useDispatch(); | |
const [name, setName] = useState(''); | |
const [firebaseUser, setFirebaseUser] = useState(''); | |
const [email, setEmail] = useState(''); | |
const [phoneNumber, setPhoneNumber] = useState(''); | |
const [invalidCodeError, setInvalidCodeError] = useState(''); | |
const [fullPhoneNumber, setFullPhoneNumber] = useState(''); | |
const [countryCode, setCountryCode] = useState('+'); | |
const [password, setPassword] = useState(''); | |
const [formError, setFormError] = useState(''); | |
const [passwordConfirmation, setPasswordConfirmation] = useState(''); | |
const [step, setStep] = useState(1); | |
const [loading, setLoading] = useState(false); | |
const [confirm, setConfirm] = useState(null); | |
const [code, setCode] = useState(''); | |
const device = useSelector(state => state.profile.device) | |
useEffect(() => { | |
const subscriber = auth().onAuthStateChanged(onAuthStateChanged); | |
return subscriber; // unsubscribe on unmount | |
}, []); | |
useEffect(() => { | |
if (!countryCode.length) { | |
setCountryCode('+'); | |
} else { | |
setFullPhoneNumber(countryCode + phoneNumber); | |
} | |
}, [countryCode, phoneNumber]); | |
useEffect(() => { | |
if (code.length == 6) { | |
confirmCode(); | |
} | |
}, [code]); | |
useEffect(() => { | |
if (firebaseUser && confirm) { | |
attemptLogin(); | |
} | |
}, [firebaseUser]); | |
// Handle user state changes | |
function onAuthStateChanged(user) { | |
setFirebaseUser(user); | |
} | |
// STEP 1: Handle the button press | |
async function signInWithPhoneNumber() { | |
const isValidCode = COUNTRY_PHONE_EXTENSION_CODES.some( | |
standardCountryCode => standardCountryCode.dial_code == countryCode, | |
); | |
if (!isValidCode) { | |
return Alert.alert( | |
'Invalid country code', | |
'Input a valid country phone code to proceed', | |
); | |
} | |
if (!phoneNumber.length) { | |
return Alert.alert( | |
'Invalid phone number', | |
'Input a valid phone number to proceed', | |
); | |
} | |
setLoading(true); | |
const confirmation = await auth().signInWithPhoneNumber(fullPhoneNumber); | |
setConfirm(confirmation); | |
setStep(2); | |
setLoading(false); | |
} | |
// STEP 2: Confirm code | |
async function confirmCode() { | |
setLoading(true); | |
try { | |
await confirm.confirm(code); | |
attemptLogin(); | |
} catch (error) { | |
setInvalidCodeError('The code you entered is invalid. Try again'); | |
} | |
setLoading(false); | |
} | |
// STEP 2.5 - Attempt to login with the phone number and uid (provider_id) | |
const attemptLogin = () => { | |
setLoading(true); | |
let url = `${API_URL}/auth/phone/login`; | |
let formData = new FormData(); | |
formData.append('phone_number', fullPhoneNumber); | |
formData.append('provider_id', firebaseUser.uid); | |
device?.userId && formBody.append('device_id', device.userId); | |
fetch(url, {method: 'POST', body: formData}) | |
.then(response => { | |
const statusCode = response.status; | |
const responseJson = response.json(); | |
return Promise.all([statusCode, responseJson]); | |
}) | |
.then(res => { | |
const statusCode = res[0]; | |
const responseJson = res[1]; | |
if (statusCode == 200) { | |
// Success - Account found | |
dispatch(setAuthModalStatus(false)); | |
dispatch(onSignupSuccesfull(responseJson)); | |
STORAGE.saveObject('PROFILE', responseJson); | |
Toast.show({ | |
type: 'success', | |
text1: 'Login successful', | |
text2: `Welcome ${responseJson.user.name} back to nuova. Enjoy the launch 🚀🚀🚀 !!!`, | |
}); | |
} else if (statusCode == 404) { | |
// Account not found | |
setStep(3); | |
} else if (statusCode == 422) { | |
Alert.alert('Invalid params', 'Some fields are required'); | |
} else { | |
Alert.alert('Error', 'Something unexepcted happened'); | |
} | |
}) | |
.catch(err => Alert.alert('Server error', err.message)) | |
.finally(fin => setLoading(false)); | |
}; | |
const completeRegistration = () => { | |
setLoading(true); | |
// const {device} = this.props; | |
// VALIDATION | |
let validationErrors = []; | |
if (name.length == 0) { | |
validationErrors.push({name: 'Name not provided'}); | |
} | |
if (email.length > 0) { | |
// if (!_validateEmail(email)) { | |
// validationErrors.push({email: 'Invalid email'}); | |
// } | |
} | |
if (password.length < 6) { | |
validationErrors.push({ | |
password: 'Password must be more than 6 characters', | |
}); | |
} | |
if (password != passwordConfirmation) { | |
validationErrors.push({ | |
password_confirmation: 'Password donnot match', | |
}); | |
} | |
if (validationErrors.length) { | |
let errorString = generateValidationErrorString(validationErrors); | |
Alert.alert('Error', errorString); | |
setLoading(false); | |
return false; | |
} | |
let url = `${API_URL}/auth/phone/complete-registration`; | |
let formData = new FormData(); | |
formData.append('phone_number', fullPhoneNumber); | |
formData.append('code', code); | |
formData.append('name', name); | |
formData.append('email', email); | |
formData.append('password', password); | |
formData.append('password_confirmation', passwordConfirmation); | |
formData.append('provider_id', firebaseUser.uid); | |
device?.userId && formBody.append('device_id', device.userId); | |
fetch(url, {method: 'POST', body: formData}) | |
.then(response => { | |
const statusCode = response.status; | |
const responseJson = response.json(); | |
return Promise.all([statusCode, responseJson]); | |
}) | |
.then(res => { | |
const statusCode = res[0]; | |
const responseJson = res[1]; | |
if (statusCode == 200) { | |
dispatch(setAuthModalStatus(false)); | |
dispatch(onSignupSuccesfull(responseJson)); | |
STORAGE.saveObject('PROFILE', responseJson); | |
Toast.show({ | |
type: 'success', | |
text1: 'Login successful', | |
text2: `Welcome ${responseJson.user.name} back to nuova. Enjoy the launch 🚀🚀🚀 !!!`, | |
}); | |
} else if (statusCode == 422) { | |
return Alert.alert('Invalid fields', responseJson.message); | |
} else if (statusCode == 400) { | |
Alert.alert('Error', responseJson.message); | |
} else { | |
Alert.alert('Error', 'Something unexpected happened'); | |
} | |
}) | |
.catch(err => { | |
Alert.alert('Server Error', err.message); | |
}) | |
.finally(fin => setLoading(false)); | |
}; | |
return ( | |
<MyPage | |
navBar={ | |
<Navbar | |
onBackPressed={() => props.onBackPressed()} | |
title={'Phone number sign in'} | |
{...props} | |
/> | |
} | |
contentContainerStyle={{paddingTop: 0, paddingHorizontal: 0}}> | |
<ScrollView | |
keyboardShouldPersistTaps="handled" | |
contentContainerStyle={styles.body}> | |
<View style={styles.bodyContent}> | |
{/*STEP 1: Get phoneNumber and send to backend for OTP code*/} | |
{step == 1 && ( | |
<View> | |
<View> | |
<Text style={styles.headerstyle}> | |
What is your phone number | |
</Text> | |
</View> | |
<View style={{flexDirection: 'row'}}> | |
<Input | |
placeholder={'Code'} | |
icon="deskphone" | |
value={countryCode} | |
keyboardType="decimal-pad" | |
maxLength={4} | |
onChangeText={c => setCountryCode(c)} | |
style={styles.codeInput} | |
/> | |
<Input | |
placeholder={'What is your phone number'} | |
icon="deskphone" | |
value={phoneNumber} | |
keyboardType="decimal-pad" | |
onChangeText={phoneNumber => setPhoneNumber(phoneNumber)} | |
style={styles.codePhoneInput} | |
/> | |
</View> | |
<TouchableOpacity | |
disabled={loading} | |
onPress={signInWithPhoneNumber} | |
style={styles.buttonstyle}> | |
{loading ? ( | |
<ActivityIndicator size={23} color={theme.PRIMARY_COLOR} /> | |
) : ( | |
<Text style={styles.buttontextstyle}> | |
Request confirmation code | |
</Text> | |
)} | |
</TouchableOpacity> | |
</View> | |
)} | |
{/*STEP 2: Listen to the otp code */} | |
{step == 2 && ( | |
<View> | |
<View> | |
<Text style={styles.subText}> | |
You should receive an sms with a confirmation code shortly. | |
</Text> | |
</View> | |
<View> | |
<Text style={styles.headerstyle}>Input OTP code</Text> | |
</View> | |
{invalidCodeError ? ( | |
<Text style={styles.invalidCodeError}>{invalidCodeError}</Text> | |
) : null} | |
<Input | |
placeholder={'Input OTP Code'} | |
icon="deskphone" | |
value={code} | |
keyboardType="numeric" | |
maxLength={6} | |
onChangeText={code => setCode(code)} | |
/> | |
<TouchableOpacity | |
onPress={confirmCode} | |
style={styles.buttonstyle}> | |
{loading ? ( | |
<ActivityIndicator size={23} color={theme.PRIMARY_COLOR} /> | |
) : ( | |
<Text style={styles.buttontextstyle}> | |
{'Verify OTP code'} | |
</Text> | |
)} | |
</TouchableOpacity> | |
</View> | |
)} | |
{/*STEP 3: Receive other user information*/} | |
{step == 3 && ( | |
<View> | |
<View> | |
<Text style={styles.subText}>Lets know you</Text> | |
</View> | |
<View> | |
<Text style={styles.headerstyle}>Personal information</Text> | |
</View> | |
<Input | |
placeholder={'Your name'} | |
icon="account-outline" | |
value={name} | |
onChangeText={name => setName(name)} | |
/> | |
<Input | |
placeholder={'Your email'} | |
icon="email-outline" | |
keyboardType="email-address" | |
autoCapitalize="none" | |
value={email} | |
onChangeText={email => setEmail(email)} | |
/> | |
<Input | |
secureTextEntry | |
placeholder={'Choose a password'} | |
icon="lock-outline" | |
value={password} | |
onChangeText={password => setPassword(password)} | |
passwordiconlocked="eye-outline" | |
passwordiconunlocked="eye-off-outline" | |
/> | |
<Input | |
secureTextEntry | |
placeholder={'Re-enter the password'} | |
icon="lock-outline" | |
value={passwordConfirmation} | |
onChangeText={pwd => setPasswordConfirmation(pwd)} | |
passwordiconlocked="eye-outline" | |
passwordiconunlocked="eye-off-outline" | |
/> | |
<TouchableOpacity | |
onPress={completeRegistration} | |
style={styles.buttonstyle}> | |
{loading ? ( | |
<ActivityIndicator size={23} color={theme.PRIMARY_COLOR} /> | |
) : ( | |
<Text style={styles.buttontextstyle}>Submit</Text> | |
)} | |
</TouchableOpacity> | |
</View> | |
)} | |
</View> | |
</ScrollView> | |
</MyPage> | |
); | |
}; | |
export default PhoneAuth; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment