Add Keyboard fixes & Realtime Database
This commit is contained in:
parent
1368369495
commit
0a857f86a7
4 changed files with 219 additions and 164 deletions
132
app/login.jsx
132
app/login.jsx
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { View, Text } from 'react-native';
|
import { View, Text, ScrollView, KeyboardAvoidingView, Platform } from 'react-native';
|
||||||
import { StatusBar } from 'expo-status-bar';
|
import { StatusBar } from 'expo-status-bar';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
|
@ -65,68 +65,78 @@ export default function Login() {
|
||||||
}, [formValues]);
|
}, [formValues]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<View style={{ flex: 1 }}>
|
||||||
|
<StatusBar style="dark" />
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<View style={{ flexDirection: 'row', height: 80, alignItems: 'center' }} />
|
<View style={{ flexDirection: 'row', height: 80, alignItems: 'center' }} />
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<StyledContainer>
|
<KeyboardAvoidingView
|
||||||
<StatusBar style="dark" />
|
style={{ flex: 1 }}
|
||||||
<SafeAreaView />
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||||
<InnerContainer>
|
keyboardVerticalOffset={0}
|
||||||
<PageImage resizeMode="cover" source={require('./../assets/images/tones-logo.png')} />
|
>
|
||||||
<Title>Tones</Title>
|
<ScrollView keyboardShouldPersistTaps="handled" contentContainerStyle={{ flexGrow: 1 }}>
|
||||||
<SubTitle>Account Login</SubTitle>
|
<StyledContainer>
|
||||||
<StyledFormArea>
|
<SafeAreaView />
|
||||||
{error ? <MessageBox style={{ color: 'red' }}>{error}</MessageBox> : null}
|
<InnerContainer>
|
||||||
<LoginTextInput
|
<PageImage resizeMode="cover" source={require('./../assets/images/tones-logo.png')} />
|
||||||
label="Email Address"
|
<Title>Tones</Title>
|
||||||
icon="mail-outline"
|
<SubTitle>Account Login</SubTitle>
|
||||||
placeholder="test@organization.com"
|
<StyledFormArea>
|
||||||
placeholderTextColor="gray"
|
{error ? <MessageBox style={{ color: 'red' }}>{error}</MessageBox> : null}
|
||||||
onChangeText={formik.handleChange('email')}
|
<LoginTextInput
|
||||||
onBlur={formik.handleBlur('email')}
|
label="Email Address"
|
||||||
value={formik.values.email}
|
icon="mail-outline"
|
||||||
keyboardType="email-address"
|
placeholder="test@organization.com"
|
||||||
/>
|
placeholderTextColor="gray"
|
||||||
<LoginTextInput
|
onChangeText={formik.handleChange('email')}
|
||||||
label="Password"
|
onBlur={formik.handleBlur('email')}
|
||||||
icon="lock-closed-outline"
|
value={formik.values.email}
|
||||||
placeholder="* * * * * *"
|
keyboardType="email-address"
|
||||||
placeholderTextColor="gray"
|
autoComplete='email'
|
||||||
onChangeText={formik.handleChange('password')}
|
autoCapitalize='none'
|
||||||
onBlur={formik.handleBlur('password')}
|
/>
|
||||||
value={formik.values.password}
|
<LoginTextInput
|
||||||
secureTextEntry={hidePassword}
|
label="Password"
|
||||||
isPassword
|
icon="lock-closed-outline"
|
||||||
hidePassword={hidePassword}
|
placeholder="* * * * * *"
|
||||||
setHidePassword={setHidePassword}
|
placeholderTextColor="gray"
|
||||||
/>
|
onChangeText={formik.handleChange('password')}
|
||||||
<MessageBox>...</MessageBox>
|
onBlur={formik.handleBlur('password')}
|
||||||
<StyledButton onPress={formik.handleSubmit} disabled={loginButtonDisabled} style={loginButtonDisabled ? {backgroundColor: 'grey'} : {}}>
|
value={formik.values.password}
|
||||||
<ButtonText>Login</ButtonText>
|
secureTextEntry={hidePassword}
|
||||||
</StyledButton>
|
isPassword
|
||||||
|
hidePassword={hidePassword}
|
||||||
|
setHidePassword={setHidePassword}
|
||||||
|
/>
|
||||||
|
<MessageBox>...</MessageBox>
|
||||||
|
<StyledButton onPress={formik.handleSubmit} disabled={loginButtonDisabled} style={loginButtonDisabled ? { backgroundColor: 'grey' } : {}}>
|
||||||
|
<ButtonText>Login</ButtonText>
|
||||||
|
</StyledButton>
|
||||||
|
<Line />
|
||||||
|
<ExtraView>
|
||||||
|
<ExtraText>Don't have an account already? </ExtraText>
|
||||||
|
<Link href='./register'>
|
||||||
|
<TextLinkContent>Register</TextLinkContent>
|
||||||
|
</Link>
|
||||||
|
</ExtraView>
|
||||||
|
</StyledFormArea>
|
||||||
<Line />
|
<Line />
|
||||||
<ExtraView>
|
<Text>Temporary Area:</Text>
|
||||||
<ExtraText>Don't have an account already? </ExtraText>
|
<View>
|
||||||
<Link href='./register'>
|
<Link href='./incidents'>
|
||||||
<TextLinkContent>Register</TextLinkContent>
|
<TextLinkContent>Incidents</TextLinkContent>
|
||||||
</Link>
|
</Link>
|
||||||
</ExtraView>
|
<Link href='./landing'>
|
||||||
</StyledFormArea>
|
<TextLinkContent>Landing</TextLinkContent>
|
||||||
<Line />
|
</Link>
|
||||||
<Text>Temporary Area:</Text>
|
</View>
|
||||||
<View>
|
<Text>View Token: {expoPushToken}</Text>
|
||||||
<Link href='./incidents'>
|
<Line />
|
||||||
<TextLinkContent>Incidents</TextLinkContent>
|
</InnerContainer>
|
||||||
</Link>
|
</StyledContainer>
|
||||||
<Link href='./landing'>
|
</ScrollView>
|
||||||
<TextLinkContent>Landing</TextLinkContent>
|
</KeyboardAvoidingView>
|
||||||
</Link>
|
</View>
|
||||||
</View>
|
);
|
||||||
<Text>View Token: {expoPushToken}</Text>
|
}
|
||||||
<Line />
|
|
||||||
</InnerContainer>
|
|
||||||
</StyledContainer>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
224
app/register.jsx
224
app/register.jsx
|
|
@ -1,9 +1,10 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { router } from 'expo-router';
|
import { router } from 'expo-router';
|
||||||
import { createUserWithEmailAndPassword, updateProfile } from 'firebase/auth';
|
import { createUserWithEmailAndPassword, updateProfile } from 'firebase/auth';
|
||||||
|
import { getDatabase, ref, set } from 'firebase/database';
|
||||||
import { auth } from '@/contexts/firebase';
|
import { auth } from '@/contexts/firebase';
|
||||||
import { useAuth } from '@/contexts/AuthContext';
|
import { useAuth } from '@/contexts/AuthContext';
|
||||||
import { View, ScrollView, Text, TouchableOpacity } from 'react-native';
|
import { View, ScrollView, Text, TouchableOpacity, KeyboardAvoidingView, Platform } from 'react-native';
|
||||||
import { StatusBar } from 'expo-status-bar';
|
import { StatusBar } from 'expo-status-bar';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
|
@ -22,7 +23,8 @@ import {
|
||||||
MessageBox,
|
MessageBox,
|
||||||
LoginTextInput,
|
LoginTextInput,
|
||||||
RegisterDropdownInput,
|
RegisterDropdownInput,
|
||||||
} from '../components/generalHelpers.jsx';
|
formatPhoneNumber
|
||||||
|
} from '@/components/generalHelpers.jsx';
|
||||||
|
|
||||||
export default function Register() {
|
export default function Register() {
|
||||||
const [providerDropdownOpen, setProviderDropdownOpen] = useState(false);
|
const [providerDropdownOpen, setProviderDropdownOpen] = useState(false);
|
||||||
|
|
@ -50,6 +52,16 @@ export default function Register() {
|
||||||
await updateProfile(userCredential.user, {
|
await updateProfile(userCredential.user, {
|
||||||
displayName: `${values.firstName} ${values.lastName}`
|
displayName: `${values.firstName} ${values.lastName}`
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const db = getDatabase();
|
||||||
|
await set(ref(db, 'users/' + userCredential.user.uid), {
|
||||||
|
firstName: values.firstName,
|
||||||
|
lastName: values.lastName,
|
||||||
|
number: values.number,
|
||||||
|
provider: values.provider,
|
||||||
|
email: values.email,
|
||||||
|
uid: userCredential.user.uid
|
||||||
|
});
|
||||||
router.replace('./incidents');
|
router.replace('./incidents');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err.message);
|
setError(err.message);
|
||||||
|
|
@ -63,21 +75,20 @@ export default function Register() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (formValues) {
|
if (formValues) {
|
||||||
if (
|
const regex = /^[^@]+@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]+$/;
|
||||||
formValues.email &&
|
if ((formValues.number.length === 14 || (formValues.number.length === 10 && !formValues.number.includes('('))) && regex.test(formValues.email) && formValues.firstName && formValues.lastName) {
|
||||||
formValues.firstName &&
|
if (formValues.password.length !== 0 && (formValues.password === formValues.passwordConfirmation)) {
|
||||||
formValues.lastName &&
|
setRegisterButtonDisabled(false);
|
||||||
formValues.password.length !== 0 &&
|
} else {
|
||||||
formValues.password === formValues.passwordConfirmation
|
setRegisterButtonDisabled(true);
|
||||||
) {
|
}
|
||||||
setRegisterButtonDisabled(false);
|
|
||||||
} else {
|
} else {
|
||||||
setRegisterButtonDisabled(true);
|
setRegisterButtonDisabled(true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setRegisterButtonDisabled(true);
|
setRegisterButtonDisabled(true);
|
||||||
}
|
}
|
||||||
}, [formValues]);
|
}, [formValues])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user) {
|
if (user) {
|
||||||
|
|
@ -86,7 +97,8 @@ export default function Register() {
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View style={{ flex: 1 }}>
|
||||||
|
<StatusBar style="dark" />
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<View style={{ flexDirection: 'row', height: 80, alignItems: 'flex-end' }}>
|
<View style={{ flexDirection: 'row', height: 80, alignItems: 'flex-end' }}>
|
||||||
<TouchableOpacity onPress={router.back} style={{ flexDirection: 'row', alignItems: 'center', paddingBottom: 5 }}>
|
<TouchableOpacity onPress={router.back} style={{ flexDirection: 'row', alignItems: 'center', paddingBottom: 5 }}>
|
||||||
|
|
@ -95,97 +107,105 @@ export default function Register() {
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<ScrollView>
|
<KeyboardAvoidingView
|
||||||
<StyledContainer>
|
style={{ flex: 1 }}
|
||||||
<StatusBar style="dark" />
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||||
|
keyboardVerticalOffset={0}
|
||||||
|
>
|
||||||
|
<ScrollView keyboardShouldPersistTaps="handled" contentContainerStyle={{ flexGrow: 1 }}>
|
||||||
<SafeAreaView />
|
<SafeAreaView />
|
||||||
<InnerContainer>
|
<StyledContainer>
|
||||||
<PageImage resizeMode="cover" source={require('./../assets/images/tones-logo.png')} />
|
<InnerContainer>
|
||||||
{error ? <MessageBox style={{ color: 'red' }}>{error}</MessageBox> : null}
|
<PageImage resizeMode="cover" source={require('./../assets/images/tones-logo.png')} />
|
||||||
<Title>Tones</Title>
|
{error ? <MessageBox style={{ color: 'red' }}>{error}</MessageBox> : null}
|
||||||
<SubTitle>Account Register</SubTitle>
|
<Title>Tones</Title>
|
||||||
<StyledFormArea>
|
<SubTitle>Account Register</SubTitle>
|
||||||
<LoginTextInput
|
<StyledFormArea>
|
||||||
label="First Name"
|
<LoginTextInput
|
||||||
icon="person-outline"
|
label="First Name"
|
||||||
placeholder="Bud"
|
icon="person-outline"
|
||||||
placeholderTextColor="gray"
|
placeholder="Bud"
|
||||||
onChangeText={formik.handleChange('firstName')}
|
placeholderTextColor="gray"
|
||||||
onBlur={formik.handleBlur('firstName')}
|
onChangeText={formik.handleChange('firstName')}
|
||||||
value={formik.values.firstName}
|
onBlur={formik.handleBlur('firstName')}
|
||||||
/>
|
value={formik.values.firstName}
|
||||||
<LoginTextInput
|
/>
|
||||||
label="Last Name"
|
<LoginTextInput
|
||||||
icon="people-outline"
|
label="Last Name"
|
||||||
placeholder="Doble"
|
icon="people-outline"
|
||||||
placeholderTextColor="gray"
|
placeholder="Doble"
|
||||||
onChangeText={formik.handleChange('lastName')}
|
placeholderTextColor="gray"
|
||||||
onBlur={formik.handleBlur('lastName')}
|
onChangeText={formik.handleChange('lastName')}
|
||||||
value={formik.values.lastName}
|
onBlur={formik.handleBlur('lastName')}
|
||||||
/>
|
value={formik.values.lastName}
|
||||||
<LoginTextInput
|
/>
|
||||||
label="Phone Number"
|
<LoginTextInput
|
||||||
icon="call-outline"
|
label="Phone Number"
|
||||||
placeholder="123-456-7890"
|
icon="call-outline"
|
||||||
placeholderTextColor="gray"
|
placeholder="123-456-7890"
|
||||||
onChangeText={formik.handleChange('number')}
|
placeholderTextColor="gray"
|
||||||
onBlur={formik.handleBlur('number')}
|
onChangeText={formik.handleChange('number')}
|
||||||
value={formik.values.number.replace(/^(\d{3})(\d{3})(\d+)$/, "($1) $2-$3")}
|
onBlur={formik.handleBlur('number')}
|
||||||
keyboardType="number-pad"
|
value={formatPhoneNumber(formik.values.number)}
|
||||||
maxLength={14}
|
autoComplete='tel'
|
||||||
/>
|
keyboardType='phone-pad'
|
||||||
<RegisterDropdownInput
|
maxLength={14}
|
||||||
label="Cellular Provider"
|
/>
|
||||||
isOpen={providerDropdownOpen}
|
<RegisterDropdownInput
|
||||||
setOpen={setProviderDropdownOpen}
|
label="Cellular Provider"
|
||||||
selectedValue={formik.values.provider}
|
isOpen={providerDropdownOpen}
|
||||||
onValueChange={formik.handleChange('provider')}
|
setOpen={setProviderDropdownOpen}
|
||||||
menu={providerMenu}
|
selectedValue={formik.values.provider}
|
||||||
/>
|
onValueChange={formik.handleChange('provider')}
|
||||||
<LoginTextInput
|
menu={providerMenu}
|
||||||
label="Email Address"
|
/>
|
||||||
icon="mail-outline"
|
<LoginTextInput
|
||||||
placeholder="test@organization.com"
|
label="Email Address"
|
||||||
placeholderTextColor="gray"
|
icon="mail-outline"
|
||||||
onChangeText={formik.handleChange('email')}
|
placeholder="test@organization.com"
|
||||||
onBlur={formik.handleBlur('email')}
|
placeholderTextColor="gray"
|
||||||
value={formik.values.email}
|
onChangeText={formik.handleChange('email')}
|
||||||
keyboardType="email-address"
|
onBlur={formik.handleBlur('email')}
|
||||||
/>
|
value={formik.values.email}
|
||||||
<LoginTextInput
|
keyboardType="email-address"
|
||||||
label="Password"
|
autoComplete='email'
|
||||||
icon="lock-closed-outline"
|
autoCapitalize='none'
|
||||||
placeholder="* * * * * *"
|
/>
|
||||||
placeholderTextColor="gray"
|
<LoginTextInput
|
||||||
onChangeText={formik.handleChange('password')}
|
label="Password"
|
||||||
onBlur={formik.handleBlur('password')}
|
icon="lock-closed-outline"
|
||||||
value={formik.values.password}
|
placeholder="* * * * * *"
|
||||||
secureTextEntry={hidePassword}
|
placeholderTextColor="gray"
|
||||||
isPassword
|
onChangeText={formik.handleChange('password')}
|
||||||
hidePassword={hidePassword}
|
onBlur={formik.handleBlur('password')}
|
||||||
setHidePassword={setHidePassword}
|
value={formik.values.password}
|
||||||
/>
|
secureTextEntry={hidePassword}
|
||||||
<LoginTextInput
|
isPassword
|
||||||
label="Confirm Password"
|
hidePassword={hidePassword}
|
||||||
icon="shield-checkmark-outline"
|
setHidePassword={setHidePassword}
|
||||||
placeholder="* * * * * *"
|
/>
|
||||||
placeholderTextColor="gray"
|
<LoginTextInput
|
||||||
onChangeText={formik.handleChange('passwordConfirmation')}
|
label="Confirm Password"
|
||||||
onBlur={formik.handleBlur('passwordConfirmation')}
|
icon="shield-checkmark-outline"
|
||||||
value={formik.values.passwordConfirmation}
|
placeholder="* * * * * *"
|
||||||
secureTextEntry={hidePassword}
|
placeholderTextColor="gray"
|
||||||
isPassword
|
onChangeText={formik.handleChange('passwordConfirmation')}
|
||||||
hidePassword={hidePassword}
|
onBlur={formik.handleBlur('passwordConfirmation')}
|
||||||
setHidePassword={setHidePassword}
|
value={formik.values.passwordConfirmation}
|
||||||
/>
|
secureTextEntry={hidePassword}
|
||||||
<MessageBox>...</MessageBox>
|
isPassword
|
||||||
<StyledButton onPress={formik.handleSubmit} style={registerButtonDisabled ? {backgroundColor: 'grey'} : {}}>
|
hidePassword={hidePassword}
|
||||||
<ButtonText>Register</ButtonText>
|
setHidePassword={setHidePassword}
|
||||||
</StyledButton>
|
/>
|
||||||
</StyledFormArea>
|
<MessageBox>...</MessageBox>
|
||||||
</InnerContainer>
|
<StyledButton onPress={formik.handleSubmit} style={registerButtonDisabled ? { backgroundColor: 'grey' } : {}}>
|
||||||
</StyledContainer>
|
<ButtonText>Register</ButtonText>
|
||||||
</ScrollView>
|
</StyledButton>
|
||||||
|
</StyledFormArea>
|
||||||
|
</InnerContainer>
|
||||||
|
</StyledContainer>
|
||||||
|
</ScrollView>
|
||||||
|
</KeyboardAvoidingView>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -336,4 +336,28 @@ export const RegisterDropdownInput = ({
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const formatPhoneNumber = (e) => {
|
||||||
|
let formattedNumber;
|
||||||
|
const length = e?.length;
|
||||||
|
|
||||||
|
const regex = () => e.replace(/[^0-9\.]+/g, "");
|
||||||
|
const areaCode = () => `(${regex().slice(0, 3)})`;
|
||||||
|
const firstSix = () => `${areaCode()} ${regex().slice(3, 6)}`;
|
||||||
|
const trailer = (start) => `${regex().slice(start, regex().length)}`;
|
||||||
|
|
||||||
|
if (length <= 3) {
|
||||||
|
formattedNumber = regex();
|
||||||
|
} else if (length === 4) {
|
||||||
|
formattedNumber = `${areaCode()} ${trailer(3)}`;
|
||||||
|
} else if (length === 5) {
|
||||||
|
formattedNumber = `${areaCode().replace(")", "")}`;
|
||||||
|
} else if (length >= 5 && length <= 9) {
|
||||||
|
formattedNumber = `${areaCode()} ${trailer(3)}`;
|
||||||
|
} else if (length >= 10) {
|
||||||
|
formattedNumber = `${firstSix()}-${trailer(6)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedNumber;
|
||||||
|
};
|
||||||
|
|
@ -5,6 +5,7 @@ import ReactNativeAsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
const firebaseConfig = {
|
const firebaseConfig = {
|
||||||
apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY,
|
apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY,
|
||||||
authDomain: process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN,
|
authDomain: process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN,
|
||||||
|
databaseURL: process.env.EXPO_PUBLIC_FIREBASE_DATABASE_URL,
|
||||||
projectId: process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID,
|
projectId: process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID,
|
||||||
storageBucket: process.env.EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET,
|
storageBucket: process.env.EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET,
|
||||||
messagingSenderId: process.env.EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
|
messagingSenderId: process.env.EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue