diff --git a/backend/src/server.ts b/backend/src/server.ts
index 2e3bb3f..7215eff 100644
--- a/backend/src/server.ts
+++ b/backend/src/server.ts
@@ -27,22 +27,10 @@ app.get(
// Google callback route
app.get(
"/auth/google/callback",
- passport.authenticate("google", { failureRedirect: "http://localhost:8081/NotAuthorized" }),
- (req, res, next) => {
- if (!req.user) {
- console.error("User is undefined during login.");
- return res.status(401).json({ error: "User not authenticated" });
- }
- req.login(req.user, (err) => {
- if (err) {
- console.error("Error during req.login:", err);
- return next(err); // Pass the error to the error handler
- }
- console.log("User logged in:", req.user); // Debug log
- console.log("Session data:", req.session); // Debug log
- res.redirect("http://localhost:8081/"); // Redirect after successful login
- });
- },
+ passport.authenticate("google", {
+ successRedirect: "http://localhost:8081/",
+ failureRedirect: "http://localhost:8081/NotAuthorized",
+ }),
);
app.get("/auth/me", (req, res) => {
if (!req.isAuthenticated()) return res.status(401).json({ error: "Not Authenticated" });
diff --git a/frontend/app/(tabs)/_layout.tsx b/frontend/app/(tabs)/_layout.tsx
index 9040e3b..9ec1af3 100644
--- a/frontend/app/(tabs)/_layout.tsx
+++ b/frontend/app/(tabs)/_layout.tsx
@@ -1,20 +1,42 @@
-import React from "react";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { Ionicons } from "@expo/vector-icons";
import Home from ".";
import History from "./History"; // Import the History component
+import { useEffect } from "react";
// import LoginPage from "../Login";
import Account from "./Account";
import Goals from "./Goals";
import { useAuth } from "@/context/authContext";
import { Redirect } from "expo-router";
+import Checking from "@/components/Checking";
+import { BACKEND_PORT } from "@env";
const Tab = createBottomTabNavigator();
export default function TabLayout() {
- const { user } = useAuth();
+ const { user, isLoading, login } = useAuth();
+
+ useEffect(() => {
+ fetch(`http://localhost:${BACKEND_PORT}/auth/me`, {
+ credentials: "include",
+ })
+ .then((response) => {
+ if (response.ok) {
+ response.json().then(login);
+ }
+ })
+ .catch((error) => {
+ console.error("Error checking auth:", error);
+ });
+ }, []);
+
+ if (isLoading) {
+ return ;
+ }
+
if (!user) {
return ;
}
+
return (
{
- // const router = useRouter();
- const [isRedirected, setIsRedirected] = useState(false);
- const { login } = useAuth();
- const handleGoogleLogin = () => {
- // Redirect to backend Google OAuth URL
- window.location.href = `http://localhost:${BACKEND_PORT}/auth/google`;
- };
- useEffect(() => {
- const checkAuth = async () => {
- try {
- const response = await fetch(
- `http://localhost:${BACKEND_PORT}/auth/me`,
- {
- credentials: "include",
- },
- );
+ const router = useRouter();
+ const { isLoading, user } = useAuth();
- if (response.ok) {
- const userData = await response.json();
- await login(userData);
- }
- } catch (error) {
- console.error("Error checking auth:", error);
- }
- };
+ if (isLoading) {
+ return ;
+ }
+
+ if (user) {
+ return ;
+ }
- // Check if we've been redirected back from OAuth
- // You can also check for specific query parameters from your OAuth callback
- if (window.location.pathname === "/Login" && !isRedirected) {
- setIsRedirected(true);
- checkAuth();
- }
- }, [isRedirected]);
return (
Login Page
Sign in to access your account.
-
+
);
};
diff --git a/frontend/app/_layout.tsx b/frontend/app/_layout.tsx
index 49348d9..a079ccd 100644
--- a/frontend/app/_layout.tsx
+++ b/frontend/app/_layout.tsx
@@ -6,56 +6,19 @@ import {
import { useFonts } from "expo-font";
import { Stack } from "expo-router";
import * as SplashScreen from "expo-splash-screen";
-import { useEffect, useState } from "react";
+import { useEffect } from "react";
import { useColorScheme } from "@/hooks/useColorScheme";
import Header from "@/components/Header/Header";
import Toast from "react-native-toast-message";
import { AuthProvider } from "@/context/authContext";
import { useAuth } from "@/context/authContext";
-import { useRouter } from "expo-router";
-import { ActivityIndicator, View } from "react-native";
-import { BACKEND_PORT } from "@env";
SplashScreen.preventAutoHideAsync();
function AuthCheck() {
const { user } = useAuth();
- const router = useRouter();
const colorScheme = useColorScheme();
- const [checking, setChecking] = useState(true);
- useEffect(() => {
- const checkAuth = async () => {
- try {
- const response = await fetch(
- `http://localhost:${BACKEND_PORT}/auth/me`,
- {
- credentials: "include",
- },
- );
-
- if (!response.ok) {
- setChecking(false);
- return;
- }
-
- setChecking(false);
- } catch (error) {
- console.error("Auth check failed:", error);
- } finally {
- setChecking(false); // Even if failed, stop loading
- }
- };
- checkAuth();
- }, [router]);
-
- if (checking) {
- return (
-
-
-
- );
- }
return (
<>
diff --git a/frontend/components/Checking.tsx b/frontend/components/Checking.tsx
new file mode 100644
index 0000000..be3ac3d
--- /dev/null
+++ b/frontend/components/Checking.tsx
@@ -0,0 +1,9 @@
+import { ActivityIndicator, View } from "react-native";
+
+export default function Checking() {
+ return (
+
+
+
+ );
+}
diff --git a/frontend/context/authContext.tsx b/frontend/context/authContext.tsx
index 3a6a76c..a6cf683 100644
--- a/frontend/context/authContext.tsx
+++ b/frontend/context/authContext.tsx
@@ -1,17 +1,9 @@
-import {
- createContext,
- useContext,
- useState,
- useEffect,
- ReactNode,
-} from "react";
-import AsyncStorage from "@react-native-async-storage/async-storage";
+import { createContext, useContext, ReactNode } from "react";
import { useRouter } from "expo-router";
-import { BACKEND_PORT } from "@env";
+import useStorageState, { AuthUserSession } from "@/hooks/useStorageState";
-interface AuthContextType {
- user: any | null;
- userId: any | null;
+interface AuthContextType extends AuthUserSession {
+ isLoading: boolean;
login: (userData: any) => Promise;
logout: () => Promise;
}
@@ -19,6 +11,7 @@ interface AuthContextType {
const AuthContext = createContext({
user: null,
userId: null,
+ isLoading: false,
login: async () => {},
logout: async () => {},
});
@@ -28,61 +21,29 @@ interface AuthProviderProps {
}
export const AuthProvider: React.FC = ({ children }) => {
- const [user, setUser] = useState(null);
- const [userId, setUserId] = useState(null);
+ const [[isLoading, session], setSession] = useStorageState();
const router = useRouter();
- const [loading, setLoading] = useState(true);
-
- useEffect(() => {
- const loadUser = async () => {
- const storedUser = await AsyncStorage.getItem("user");
- if (storedUser) {
- try {
- const userData = JSON.parse(storedUser);
-
- // Validate session with backend
- const res = await fetch(`http://localhost:${BACKEND_PORT}/auth/me`, {
- credentials: "include",
- });
-
- if (!res.ok) throw new Error("Session invalid");
- setUser(userData);
- setUserId(userData.id);
- } catch (err) {
- await AsyncStorage.clear();
- setUser(null);
- setUserId(null);
- router.replace("/Login");
- }
- }
- setLoading(false);
- };
-
- loadUser();
- }, []);
const login = async (userData: any) => {
- await Promise.all([
- AsyncStorage.setItem("user", JSON.stringify(userData)),
- AsyncStorage.setItem("userId", userData.id),
- ]);
- setUser(userData);
- setUserId(userData.id);
+ setSession({ user: userData, userId: userData.id });
router.replace("/");
};
const logout = async () => {
- await AsyncStorage.removeItem("user");
- await AsyncStorage.removeItem("userId");
- setUser(null);
- setUserId(null);
+ setSession({ user: null, userId: null });
router.replace("/Login");
};
- if (loading) return null;
-
return (
-
+
{children}
);
diff --git a/frontend/hooks/useStorageState.ts b/frontend/hooks/useStorageState.ts
new file mode 100644
index 0000000..e8ca3d0
--- /dev/null
+++ b/frontend/hooks/useStorageState.ts
@@ -0,0 +1,90 @@
+import { createContext, useCallback, useEffect, useReducer } from "react";
+import AsyncStorage from "@react-native-async-storage/async-storage";
+import { BACKEND_PORT } from "@env";
+
+export interface AuthUserSession {
+ user: any | null;
+ userId: any | null;
+}
+
+const authUserSessionKeys = ["user", "userId"];
+
+type UseStateHook = [
+ [boolean, AuthUserSession],
+ (value: AuthUserSession | null) => void,
+];
+
+function useAsyncState(
+ initialValue: [boolean, AuthUserSession | null] = [true, null],
+): UseStateHook {
+ return useReducer(
+ (
+ state: [boolean, AuthUserSession | null],
+ action: AuthUserSession | null = null,
+ ): [boolean, AuthUserSession | null] => {
+ console.log("useStorageState.tsx: set isLoading to false");
+ console.table(action);
+ return [false, action];
+ },
+ initialValue,
+ ) as UseStateHook;
+}
+
+async function getStorageItem(): Promise {
+ let session: AuthUserSession = {
+ user: null,
+ userId: null,
+ };
+ const storedUser = await AsyncStorage.getItem("user");
+ if (storedUser) {
+ try {
+ const userData = JSON.parse(storedUser);
+
+ // Validate session with backend
+ const res = await fetch(`http://localhost:${BACKEND_PORT}/auth/me`, {
+ credentials: "include",
+ });
+
+ if (!res.ok) throw new Error("Session invalid");
+ return { user: userData, userId: userData.id };
+ } catch (err) {
+ await AsyncStorage.clear();
+ return { user: null, userId: null };
+ }
+ }
+ return session;
+}
+
+async function setStorageItem(value: AuthUserSession | null) {
+ if (value == null) {
+ await AsyncStorage.removeItem("user");
+ await AsyncStorage.removeItem("userId");
+ } else {
+ await AsyncStorage.setItem("user", JSON.stringify(value.user));
+ await AsyncStorage.setItem("userId", value.userId);
+ }
+}
+
+export default function useStorageState(): UseStateHook {
+ // Public
+ const [state, setState] = useAsyncState([true, { user: null, userId: null }]);
+
+ // Get
+ useEffect(() => {
+ console.log("userStorageState.tsx: the getter is ran");
+ getStorageItem().then((value) => {
+ console.table(value);
+ setState(value);
+ });
+ }, []);
+
+ // Set
+ const setValue = useCallback((value: AuthUserSession | null) => {
+ console.log("userStorageState.tsx: the setter is ran");
+ console.table(value);
+ setState(value);
+ setStorageItem(value);
+ }, []);
+
+ return [state, setValue];
+}