Skip to content

Commit

Permalink
Merge pull request #51 from andreasalstrup/basicLogin
Browse files Browse the repository at this point in the history
Basic login
  • Loading branch information
andreasalstrup authored Nov 29, 2023
2 parents e04a7c3 + 0575e1f commit 9244d78
Show file tree
Hide file tree
Showing 18 changed files with 2,569 additions and 762 deletions.
160 changes: 160 additions & 0 deletions app/(login)/CreateAccountScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import styles from './styles'
import { Text, View, } from '../../components/Themed';
import { TextInput } from 'react-native-gesture-handler';
import { ImageBackground, Pressable } from 'react-native';
import { router } from 'expo-router';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { LogoAndName } from '../../components/LogoAndName';
import { useState } from 'react';

export default function CreateAccountScreen () {
const [phoneNumber, setPhoneNumber] = useState('')
const [password, setPassword] = useState('')
const [repeatPassword, setRepeatPassword] = useState('')
const [fullName, setFullName] = useState('')
const [email, setEmail] = useState('')
const [error, setError] = useState('')
const [showWrongPasswords, setShowWrongPasswords] = useState(false)
const [hidePassword, setHidePassword] = useState(true)
const [submitActive, setSubmitActive] = useState(false)
const [creatingUser, setCreatingUser] = useState(false)

const toggleHidePassword = () => setHidePassword(!hidePassword)

const validatePhone = (phone : string) => phone.length == 8

const validatePass = (pass: string) => pass.length > 7

function passwordsMatch (pass : string, repPass : string) {
let equal : boolean = pass == repPass
let value : boolean = (pass != '' && repPass != '') && (!equal)
setShowWrongPasswords(value)
return equal
}

function toggleSubmitButton (phone : string, pass : string, repPass : string) { //We need params to mitigate the async updating
setSubmitActive(passwordsMatch(pass, repPass) && validatePhone(phone) && validatePass(pass))
}

function createAccount(ack : any) {
if (ack?.err){
setCreatingUser(false);
console.log("Some error on user creation")
if (ack.err == "User already created!"){
setError("User already exists")
}
else{//The other possible error is: "User is already being created or authenticated!" which probably means the user has clicked create user multiple times
return
}
}
else{
let newUser = gun.user(ack.pub)
if (fullName != ''){
newUser.get('fullName').put(fullName)
}
if (email != '') {
newUser.get('email').put(email)
}
setShowWrongPasswords(false)
user.auth(phoneNumber,password, login)
}
}

function login (ack : any) {
if (!ack.err){
if (user.is){
console.log("Redirect to GroupScreen")
router.replace('/GroupScreen')
}
else{
console.log("User somehow doesn't exist");
}
}
else{
console.log("Newly created user cannot be authenticated, something went wrong with the database");
// Not sure how this should be handled
}
}

return (
<>
<View style={styles.container}>

<ImageBackground source={require('../../assets/images/accountScreensImage.png')} style={styles.backgroundImage}>
<LogoAndName/>
{error != "" && <Text style={styles.error}> {error} </Text>}
<Text style={styles.descriptiveText}>Phone Number*</Text>
<View style={styles.inputBox}>
<TextInput maxLength={8} inputMode='tel' autoComplete={'tel'} style={styles.inputField}
value={phoneNumber}
onChangeText={
(newPhoneNumber) =>{
setPhoneNumber(newPhoneNumber)
toggleSubmitButton(newPhoneNumber, password, repeatPassword)
}
}/>
</View>
<Text style={styles.descriptiveText}>Password*</Text>
<View style={styles.inputBox}>
<TextInput secureTextEntry={hidePassword}
style={styles.inputField}
value={password}
autoCapitalize='none'
onChangeText={
(newPassword) =>{
setPassword(newPassword)
toggleSubmitButton(phoneNumber, newPassword, repeatPassword)
}
}
/>
<MaterialCommunityIcons
name={hidePassword ? 'eye' : 'eye-off'}
style={styles.eye}
size={24}
onPress={toggleHidePassword}
/>
</View>
{!validatePass(password) && <Text style={styles.descriptiveText}>Password needs at least 7 characters</Text>}
<Text style={styles.descriptiveText}>Repeat Password*</Text>
<View style={styles.inputBox}>
<TextInput autoCapitalize='none'
secureTextEntry={hidePassword}
style={styles.inputField}
value={repeatPassword}
onChangeText={(newRepeatPassword) => {
setRepeatPassword(newRepeatPassword)
toggleSubmitButton(phoneNumber, password, newRepeatPassword)
}
}
/>
</View>
{showWrongPasswords && <Text style={styles.error}> Passwords do not match</Text>}
<Text style={styles.descriptiveText}>Full Name</Text>
<View style={styles.inputBox}>
<TextInput autoComplete={'name'} style={styles.inputField}
value={fullName}
onChangeText={(fullName) => setFullName(fullName)}
/>
</View>
<Text style={styles.descriptiveText}>E-mail</Text>

<View style={styles.inputBox}>
<TextInput inputMode='email' autoComplete={'email'} style={styles.inputField}
value={email}
onChangeText={(email) => setEmail(email)}
/>
</View>
<View style={styles.separator}/>
<Pressable style={submitActive ? styles.button : styles.disabledButton }
onPress={(submitActive && !creatingUser) ? () =>{
setCreatingUser(true);
user.create(phoneNumber, password, createAccount)
} : () => {}}>
<Text style={styles.buttonText}> Create account </Text>
</Pressable>
</ImageBackground>
</View>
</>
)

}
5 changes: 5 additions & 0 deletions app/(login)/GroupScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Redirect } from 'expo-router';

export default function GroupScreen() {
return <Redirect href='../(tabs)/shoppingList'/>
}
89 changes: 89 additions & 0 deletions app/(login)/LoginScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import styles from './styles'
import { Text, View, } from '../../components/Themed';
import { TextInput } from 'react-native-gesture-handler';
import { ImageBackground, Pressable } from 'react-native';
import { useState } from 'react';
import { Link, router } from 'expo-router';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { LogoAndName } from '../../components/LogoAndName';

export default function loginScreen () {
const [phoneNumber, setPhoneNumber] = useState('12345678')
const [password, setPassword] = useState('12345678')
const [wrongCredentials, setWrongCredentials] = useState(false)
const [hidePassword, setHidePassword] = useState(true)
const [authing, setAuthing] = useState(false)

const toggleHidePassword = () => setHidePassword(!hidePassword)

function checkSuccesfulLogin (ack: any) {
if (!ack.err){
if (user.is){
let newUser = gun.user(ack.pub)
let inGroup = true
newUser.get("group").once((ack) =>{
if(ack == undefined){
console.log("Group not found")
router.replace('/GroupScreen')
} else {
console.log("Group found")
router.replace('../(tabs)/ShoppingList/index')
}
})
}else{
console.log("User somehow doesn't exist");
}
}else{
setWrongCredentials(true)
setAuthing(false)
}
}
return (
<View style={styles.container}>
<ImageBackground source={require('../../assets/images/accountScreensImage.png')} style={styles.backgroundImage}>
<LogoAndName/>
<Text style={styles.descriptiveText}>Phone Number</Text>
<View style={styles.inputBox}>
<TextInput maxLength={8} inputMode='tel' autoComplete={'tel'} style={styles.inputField}
value={phoneNumber}
onChangeText={(phoneNumber) =>{
setPhoneNumber(phoneNumber)
}
}
/>
</View>
<Text style={styles.descriptiveText}>Password</Text>
<View style={styles.inputBox}>
<TextInput secureTextEntry={hidePassword} style={styles.inputField}
autoCapitalize='none'
value={password}
onChangeText={(password) => setPassword(password)}
/>

<MaterialCommunityIcons
name={hidePassword ? 'eye' : 'eye-off'}
style={styles.eye}
size={24}
onPress={toggleHidePassword}
/>
</View>
{wrongCredentials && <Text style={styles.error}> Wrong credentials</Text>}
<View style={styles.separator}/>

<Pressable
style={styles.button}
onPress={()=>{
if (!authing){
setAuthing(true)
user.auth(phoneNumber,password, checkSuccesfulLogin)
}
}}>
<Text style={styles.buttonText}> Sign in</Text>
</Pressable>
<Text style={styles.descriptiveText}><Link href={"/CreateAccountScreen"}> Create new account</Link></Text>
</ImageBackground>

</View>
);
}

80 changes: 80 additions & 0 deletions app/(login)/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { StyleSheet } from 'react-native';
import {Dimensions} from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
title: {
fontSize: 20,
fontWeight: 'bold',
},
error: {
fontSize: 14,
fontWeight: 'bold',
color: "red",
},
descriptiveText: {
fontSize: 13,
color: "white",
},
separator: {
marginVertical: 30,
height: 1,
width: '80%',
},
inputField: {
height: 40,
width: 200,

justifyContent: 'center',
color: "white",
},
eye: {
marginLeft: -24,
color:"#aaa",
},
inputBox: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'black',
opacity: 0.7,
borderRadius: 4,
color: "white",
},
backgroundImage: {
justifyContent: 'center',
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
alignItems: 'center',
resizeMode: 'cover',
},
button: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 12,
paddingHorizontal: 32,
borderRadius: 4,
elevation: 3,
width: 0.6 * Dimensions.get('window').width,
backgroundColor: '#5CBCA9',
},
disabledButton: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 12,
paddingHorizontal: 32,
borderRadius: 4,
elevation: 3,
width: 0.6 * Dimensions.get('window').width,
backgroundColor: 'grey',
},
buttonText: {
color: "white",
fontSize: 15,
fontWeight: "bold",
},
});

export default styles
18 changes: 7 additions & 11 deletions app/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,45 +20,41 @@ export default function TabLayout() {
return (
<Tabs
screenOptions={{
headerShown: false,
tabBarActiveTintColor: Colors[colorScheme ].tabIconSelected,
tabBarActiveBackgroundColor: Colors[colorScheme].tabIconSelectedBackground,
}}>
<Tabs.Screen
name="(index)"
options={{
headerShown: false,
name="shoppingList"
options={{
title: 'List',
tabBarIcon: ({ color }) => <TabBarIcon name="list-ul" color={color} />,
}}
/>
<Tabs.Screen
name="expenses"
options={{
headerShown: false,
options={{
title: 'Expenses',
tabBarIcon: ({ color }) => <TabBarIcon name="dollar-sign" color={color} />,
}}
/>
<Tabs.Screen
name="meal"
options={{
headerShown: false,
options={{
title: 'Meal Plan',
tabBarIcon: ({ color }) => <TabBarIcon name="hamburger" color={color} />,
}}
/>
<Tabs.Screen
name="notice"
options={{
headerShown: false,
options={{
title: 'Notice Board',
tabBarIcon: ({ color }) => <TabBarIcon name="info-circle" color={color} />,
}}
/>
<Tabs.Screen
name="settings"
options={{
headerShown: false,
options={{
title: 'Settings',
tabBarIcon: ({ color }) => <TabBarIcon name="cog" color={color} />,
}}
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 9244d78

Please sign in to comment.