[NOT FULLY WORKING] Added frontend react native interface.

This commit is contained in:
c-d-p
2025-04-17 17:28:19 +02:00
parent 4f3946d1c3
commit bf7eb8275c
36 changed files with 12230 additions and 1 deletions

View File

@@ -0,0 +1,210 @@
// 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
interface AuthContextData {
authToken: string | null;
isAuthenticated: boolean;
isLoading: boolean;
login: (username: string, password: string) => Promise<void>;
logout: () => Promise<void>;
}
const AuthContext = createContext<AuthContextData>({
authToken: null,
isAuthenticated: false,
isLoading: true,
login: async () => { throw new Error('AuthContext not initialized'); },
logout: async () => { throw new Error('AuthContext not initialized'); },
});
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 loadToken = useCallback(async () => {
console.log("[AuthContext] loadToken: Starting..."); // Log: Start
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
} 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'];
} finally {
console.log("[AuthContext] loadToken: Entering finally block."); // Log: Finally Start
setIsLoading(false);
console.log("[AuthContext] loadToken: setIsLoading(false) called."); // Log: Finally End
}
}, []);
useEffect(() => {
console.log("[AuthContext] useEffect: Component mounted, calling loadToken."); // Log: useEffect call
loadToken();
}, [loadToken]);
const login = useCallback(async (username: string, password: string) => {
console.log("[AuthContext] login: Function called with:", username); // Log: Function entry
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=',
{
headers: {
'accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
console.log("[AuthContext] login: apiClient.post successful, response status:", response?.status); // Log success
const { access_token } = response.data;
if (!access_token || typeof access_token !== 'string') {
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
} 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(" 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
}
}, []);
const logout = useCallback(async () => {
console.log('Logging out.');
setAuthToken(null);
delete apiClient.defaults.headers.common['Authorization'];
await deleteToken(); // Use helper
// Optional backend logout call
}, []);
const contextValue = useMemo(() => ({
authToken,
isAuthenticated: !!authToken,
isLoading,
login,
logout,
}), [authToken, isLoading, login, logout]);
return (
<AuthContext.Provider value={contextValue}>
{children}
</AuthContext.Provider>
);
};
// --- useAuth and AuthLoadingScreen remain the same ---
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
};
export const AuthLoadingScreen: React.FC = () => {
const theme = useTheme();
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: theme.colors.background }
});
return (<View style={styles.container}><ActivityIndicator size="large" color={theme.colors.primary} /></View>);
}