[V0.3] Working dashboard calendar module
This commit is contained in:
@@ -1,15 +1,11 @@
|
||||
// src/contexts/AuthContext.tsx
|
||||
import React, { createContext, useState, useEffect, useContext, useMemo, useCallback } from 'react';
|
||||
import { Platform, ActivityIndicator, View, StyleSheet } from 'react-native'; // Import Platform
|
||||
import * as SecureStore from 'expo-secure-store';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage'; // Use AsyncStorage for web localStorage
|
||||
import apiClient from '../api/client';
|
||||
import { useTheme } from 'react-native-paper';
|
||||
|
||||
const TOKEN_KEY = 'maia_access_token'; // Use the same key
|
||||
import { getAccessToken, getRefreshToken, storeTokens, clearTokens } from '../api/client';
|
||||
|
||||
interface AuthContextData {
|
||||
authToken: string | null;
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
login: (username: string, password: string) => Promise<void>;
|
||||
@@ -17,7 +13,6 @@ interface AuthContextData {
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextData>({
|
||||
authToken: null,
|
||||
isAuthenticated: false,
|
||||
isLoading: true,
|
||||
login: async () => { throw new Error('AuthContext not initialized'); },
|
||||
@@ -28,104 +23,42 @@ interface AuthProviderProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
// Helper functions for platform-specific storage
|
||||
const storeToken = async (token: string) => {
|
||||
if (Platform.OS === 'web') {
|
||||
try {
|
||||
// Use AsyncStorage for web (polyfilled to localStorage)
|
||||
await AsyncStorage.setItem(TOKEN_KEY, token);
|
||||
} catch (e) {
|
||||
console.error("Failed to save token to web storage", e);
|
||||
}
|
||||
} else {
|
||||
await SecureStore.setItemAsync(TOKEN_KEY, token);
|
||||
}
|
||||
};
|
||||
|
||||
const getToken = async (): Promise<string | null> => {
|
||||
if (Platform.OS === 'web') {
|
||||
try {
|
||||
return await AsyncStorage.getItem(TOKEN_KEY);
|
||||
} catch (e) {
|
||||
console.error("Failed to get token from web storage", e);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// SecureStore might throw if not available, handle gracefully
|
||||
try {
|
||||
return await SecureStore.getItemAsync(TOKEN_KEY);
|
||||
} catch (e) {
|
||||
console.error("Failed to get token from secure store", e);
|
||||
// If SecureStore fails on native, treat as no token found
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const deleteToken = async () => {
|
||||
if (Platform.OS === 'web') {
|
||||
try {
|
||||
await AsyncStorage.removeItem(TOKEN_KEY);
|
||||
} catch (e) {
|
||||
console.error("Failed to remove token from web storage", e);
|
||||
}
|
||||
} else {
|
||||
// Avoid potential crash if SecureStore is unavailable
|
||||
try {
|
||||
await SecureStore.deleteItemAsync(TOKEN_KEY);
|
||||
} catch (e) {
|
||||
console.error("Failed to delete token from secure store", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
||||
const [authToken, setAuthToken] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [isAuthenticatedState, setIsAuthenticatedState] = useState<boolean>(false);
|
||||
|
||||
const loadToken = useCallback(async () => {
|
||||
console.log("[AuthContext] loadToken: Starting..."); // Log: Start
|
||||
const checkAuthStatus = useCallback(async () => {
|
||||
const token = await getAccessToken();
|
||||
const hasToken = !!token;
|
||||
if (hasToken !== isAuthenticatedState) {
|
||||
setIsAuthenticatedState(hasToken);
|
||||
}
|
||||
return hasToken;
|
||||
}, [isAuthenticatedState]);
|
||||
|
||||
const loadInitialAuth = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
console.log("[AuthContext] loadToken: Calling getToken()..."); // Log: Before await
|
||||
const storedToken = await getToken(); // Use helper
|
||||
console.log("[AuthContext] loadToken: getToken() returned:", storedToken); // Log: After await
|
||||
|
||||
if (storedToken) {
|
||||
console.log('[AuthContext] loadToken: Token found. Setting state and headers.'); // Log: Token Found Path
|
||||
setAuthToken(storedToken);
|
||||
apiClient.defaults.headers.common['Authorization'] = `Bearer ${storedToken}`;
|
||||
} else {
|
||||
console.log('[AuthContext] loadToken: No token found. Clearing state and headers.'); // Log: No Token Path
|
||||
setAuthToken(null);
|
||||
delete apiClient.defaults.headers.common['Authorization'];
|
||||
}
|
||||
console.log('[AuthContext] loadToken: Try block finished successfully.'); // Log: Try Success
|
||||
console.log("[AuthContext] loadInitialAuth: Checking initial auth status");
|
||||
await checkAuthStatus();
|
||||
console.log("[AuthContext] loadInitialAuth: Initial check complete.");
|
||||
} catch (error) {
|
||||
// **Log the actual error object**
|
||||
console.error("[AuthContext] loadToken: Caught error:", error); // Log: Catch Block
|
||||
setAuthToken(null); // Ensure logged out state on error
|
||||
delete apiClient.defaults.headers.common['Authorization'];
|
||||
console.error("[AuthContext] loadInitialAuth: Error loading initial token:", error);
|
||||
await clearTokens();
|
||||
setIsAuthenticatedState(false);
|
||||
} finally {
|
||||
console.log("[AuthContext] loadToken: Entering finally block."); // Log: Finally Start
|
||||
setIsLoading(false);
|
||||
console.log("[AuthContext] loadToken: setIsLoading(false) called."); // Log: Finally End
|
||||
}
|
||||
}, []);
|
||||
}, [checkAuthStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("[AuthContext] useEffect: Component mounted, calling loadToken."); // Log: useEffect call
|
||||
loadToken();
|
||||
}, [loadToken]);
|
||||
loadInitialAuth();
|
||||
}, [loadInitialAuth]);
|
||||
|
||||
const login = useCallback(async (username: string, password: string) => {
|
||||
console.log("[AuthContext] login: Function called with:", username); // Log: Function entry
|
||||
console.log("[AuthContext] login: Function called with:", username);
|
||||
try {
|
||||
console.log("[AuthContext] login: Preparing to call apiClient.post for /auth/login");
|
||||
console.log("[AuthContext] login: Data being sent:", { username: username, password: password });
|
||||
|
||||
// const response = await apiClient.post(`/auth/login?grant_type=password&username=${username}&password=${password}`);
|
||||
const response = await apiClient.post(
|
||||
'/auth/login',
|
||||
'grant_type=password&username=' + encodeURIComponent(username) + '&password=' + encodeURIComponent(password) + '&scope=&client_id=&client_secret=',
|
||||
@@ -137,53 +70,61 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
||||
}
|
||||
);
|
||||
|
||||
console.log("[AuthContext] login: apiClient.post successful, response status:", response?.status); // Log success
|
||||
console.log("[AuthContext] login: apiClient.post successful, response status:", response?.status);
|
||||
const { access_token, refresh_token } = response.data;
|
||||
console.log("[AuthContext] login: Response data received.");
|
||||
|
||||
const { access_token } = response.data;
|
||||
|
||||
if (!access_token || typeof access_token !== 'string') {
|
||||
if (!access_token || typeof access_token !== 'string' || !refresh_token) {
|
||||
console.error("[AuthContext] login: Invalid token structure received:", response.data);
|
||||
throw new Error('Invalid token received from server.');
|
||||
}
|
||||
|
||||
console.log('[AuthContext] login: Login successful, received token.');
|
||||
setAuthToken(access_token);
|
||||
apiClient.defaults.headers.common['Authorization'] = `Bearer ${access_token}`;
|
||||
await storeToken(access_token); // Use helper
|
||||
console.log('[AuthContext] login: Login successful, storing tokens.');
|
||||
await storeTokens(access_token, refresh_token);
|
||||
setIsAuthenticatedState(true);
|
||||
|
||||
} catch (error: any) {
|
||||
// --- Log the error object *itself* ---
|
||||
console.error("[AuthContext] login: Caught Error Object:", error);
|
||||
|
||||
// --- Check if it's an Axios error with config details ---
|
||||
if (error.isAxiosError) {
|
||||
console.error("[AuthContext] login: Axios Error Details:");
|
||||
console.error(" Request Config:", error.config);
|
||||
console.error(" Response:", error.response); // This will likely still be undefined
|
||||
console.error(" Response:", error.response?.status, error.response?.data);
|
||||
console.error(" Message:", error.message);
|
||||
}
|
||||
|
||||
// Original logging (might be redundant now but keep for context)
|
||||
console.error("Login failed:", error.response?.data || error.message);
|
||||
throw error; // Re-throw
|
||||
await clearTokens();
|
||||
setIsAuthenticatedState(false);
|
||||
throw error;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const logout = useCallback(async () => {
|
||||
console.log('Logging out.');
|
||||
setAuthToken(null);
|
||||
delete apiClient.defaults.headers.common['Authorization'];
|
||||
await deleteToken(); // Use helper
|
||||
await apiClient.post("/auth/logout");
|
||||
console.log('[AuthContext] logout: Logging out.');
|
||||
const refreshToken = await getRefreshToken();
|
||||
if (!refreshToken) {
|
||||
console.warn('[AuthContext] logout: No refresh token found to send to backend.');
|
||||
}
|
||||
|
||||
try {
|
||||
if (refreshToken) {
|
||||
console.log('[AuthContext] logout: Calling backend /auth/logout');
|
||||
await apiClient.post("/auth/logout", { refresh_token: refreshToken });
|
||||
console.log('[AuthContext] logout: Backend logout call successful (or ignored error).');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('[AuthContext] logout: Error calling backend logout:', error.response?.data || error.message);
|
||||
} finally {
|
||||
await clearTokens();
|
||||
setIsAuthenticatedState(false);
|
||||
console.log('[AuthContext] logout: Local tokens cleared and state updated.');
|
||||
}
|
||||
}, []);
|
||||
|
||||
const contextValue = useMemo(() => ({
|
||||
authToken,
|
||||
isAuthenticated: !!authToken,
|
||||
isAuthenticated: isAuthenticatedState,
|
||||
isLoading,
|
||||
login,
|
||||
logout,
|
||||
}), [authToken, isLoading, login, logout]);
|
||||
}), [isAuthenticatedState, isLoading, login, logout]);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={contextValue}>
|
||||
|
||||
Reference in New Issue
Block a user