Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,18 @@ This command will move the starter code to the **app-example** directory and cre

## Start Contributing

Please checkout the Contrbiuting Section for more information
Please checkout the Contrbiuting Section for more information

## Changes Made to base structure

I started by running reset-project to clean up the repo, then I did an install.
There were a few vulnerabilities, 13 vulnerabilities (5 low, 3 moderate, 5 high),
so I ran a audit fix next. After running with npx expo start, I noticed we're using an older version of expo, fine for now. Also installed a recommended Expo Go version for expo 53, bit insane that 55 is already in beta, looks like making widgets is going to become a lot easier.

## Alternative approaches I would have liked to take, given more time.

1. Make a dev build to branch off expo go for making future Expo modules.
2. I would have made an axios interceptor for authorization for all subsequent API requests, creating a Http client inside of the utils subfolder, within api. Using it to attach an Authorization header on each future request and checking its date inside the interceptor, I would then define it inside the root, the \_layout.tsx file in this case.
3. Incorporate testing, unit testing for the token storage, making sure to test the persistence and expiration. I would also test the isAuthenticated state, mocking a login.
4. I would also do some integration testing with the React Native Testing Library, to make sure error messages are field specific.
5. End to End testing as well as a storyBook Implementation.
9 changes: 9 additions & 0 deletions app/(app)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Stack } from "expo-router";

export default function AppLayout() {
return (
<Stack>
<Stack.Screen name="home" />
</Stack>
);
}
29 changes: 29 additions & 0 deletions app/(app)/home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { StyleSheet, Text, View } from "react-native";
import AuthButton from "../components/AuthButton";
import { useAuth } from "../context/AuthContext";
const HomeScreen: React.FC = () => {
const { user, signOut } = useAuth();
// console.log(user?.displayName);
return (
<View style={{ ...styles.container, alignItems: "center" }}>
<Text style={styles.title}>Home Screen</Text>

{user && <Text>User: {user?.displayName}</Text>}
<AuthButton
title="Logout"
onPress={signOut}
textStyle={{ color: "blue" }}
/>
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
},
title: { textAlign: "center", fontSize: 30, marginBottom: "10%" },
});

export default HomeScreen;
9 changes: 9 additions & 0 deletions app/(auth)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Stack } from "expo-router";

export default function AuthLayout() {
return (
<Stack>
<Stack.Screen name="login" />
</Stack>
);
}
150 changes: 150 additions & 0 deletions app/(auth)/login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { useState } from "react";
import {
KeyboardAvoidingView,
Platform,
StyleSheet,
Text,
TextInput,
} from "react-native";

import AuthButton from "../components/AuthButton";
import { useAuth } from "../context/AuthContext";
import simulateLogin from "../utils/apis/simulateAuthApi";
import errorMessageUtil from "../utils/errors/errorMessageUtil";

type ErrorMessages = {
username?: string;
password?: string;
};

const LoginScreen: React.FC = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [errors, setErrors] = useState<ErrorMessages>({});

const { signIn } = useAuth();

//for populating error fields
const validateOnSubmit = (): ErrorMessages => {
const error: ErrorMessages = {};

if (!username.trim()) {
error.username = "Username cannot be empty.";
}
if (!password.trim()) {
error.password = "Password cannot be empty.";
}

return error;
};

const handleLoginButton = async () => {
if (isLoading) {
return;
}

const currentErrors = validateOnSubmit();
setErrors(currentErrors);

if (currentErrors.username || currentErrors.password) {
return;
}

setIsLoading(true);

try {
const response = await simulateLogin({ username, password });
await signIn(response.user, response.accessToken, response.refreshToken);
} catch (e: unknown) {
console.log("Login Error:", errorMessageUtil(e));
} finally {
setIsLoading(false);
}
};

return (
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
style={styles.container}
>
<Text style={styles.title}>Login Screen</Text>
<>
<Text style={styles.inputLabel}>Username</Text>
<TextInput
style={{
...styles.input,
borderColor: errors.username ? "red" : "gray",
}}
placeholder="testemail@email.com"
autoCapitalize="none"
autoCorrect={false}
value={username}
onChangeText={(text) => {
setUsername(text);
setErrors((prev) => ({
...prev,
username: undefined,
}));
}}
/>
{errors.username && (
<Text style={styles.errorText}>{errors.username}</Text>
)}

<Text style={styles.inputLabel}>Password</Text>
<TextInput
style={[
styles.input,
errors.password ? styles.inputErrorBorder : null,
]}
placeholder="Password"
secureTextEntry
value={password}
onChangeText={(text) => {
setPassword(text);
setErrors((prev) => ({
...prev,
password: undefined,
}));
}}
/>
{errors.password && (
<Text style={styles.errorText}>{errors.password}</Text>
)}

<AuthButton
title="Login"
onPress={handleLoginButton}
isLoading={isLoading}
/>
</>
</KeyboardAvoidingView>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
padding: 10,
},
title: { textAlign: "center", fontSize: 30, marginBottom: "10%" },
inputLabel: { fontSize: 16, marginTop: "5%", marginBottom: "1%" },
input: {
borderWidth: 1,
borderRadius: 5,
height: 50,
paddingHorizontal: 10,
},
inputErrorBorder: {
borderColor: "red",
},
errorText: {
marginTop: "1%",
color: "red",
fontSize: 16,
},
});

export default LoginScreen;
45 changes: 0 additions & 45 deletions app/(tabs)/_layout.tsx

This file was deleted.

39 changes: 0 additions & 39 deletions app/(tabs)/explore.tsx

This file was deleted.

44 changes: 0 additions & 44 deletions app/(tabs)/index.tsx

This file was deleted.

32 changes: 0 additions & 32 deletions app/+not-found.tsx

This file was deleted.

Loading