First commit of the new app
This commit is contained in:
222
assets/contexts/useAuth.tsx
Normal file
222
assets/contexts/useAuth.tsx
Normal file
@@ -0,0 +1,222 @@
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { API_CONFIG, API_ENDPOINTS, buildApiUrl } from '../config/api';
|
||||
import { AuthContextType, AuthProviderProps, LoginResponse, User, UserData } from '../types';
|
||||
// Contexto
|
||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||
|
||||
// Chaves para AsyncStorage
|
||||
const STORAGE_KEYS = {
|
||||
TOKEN: '@cruiseLovers:token',
|
||||
USER: '@cruiseLovers:user',
|
||||
} as const;
|
||||
|
||||
// Provider
|
||||
|
||||
export function AuthProvider({ children }: AuthProviderProps) {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [token, setToken] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [userData, setUserData] = useState<UserData | null>(null);
|
||||
// Carregar dados salvos ao iniciar
|
||||
useEffect(() => {
|
||||
loadStoredAuth();
|
||||
}, []);
|
||||
|
||||
const loadStoredAuth = async () => {
|
||||
try {
|
||||
const [storedToken, storedUser] = await Promise.all([
|
||||
AsyncStorage.getItem(STORAGE_KEYS.TOKEN),
|
||||
AsyncStorage.getItem(STORAGE_KEYS.USER),
|
||||
]);
|
||||
|
||||
if (storedToken && storedUser) {
|
||||
setToken(storedToken);
|
||||
setUser(JSON.parse(storedUser));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erro ao carregar autenticação:', err);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const login = async (email: string, password: string) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
const url = buildApiUrl(API_ENDPOINTS.USER_LOGIN);
|
||||
|
||||
// Encriptar password com MD5
|
||||
const encryptedPassword = CryptoJS.MD5(password).toString();
|
||||
|
||||
// Criar FormData com os dados de login
|
||||
const formData = new FormData();
|
||||
formData.append('email', email);
|
||||
formData.append('password', encryptedPassword);
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
// Não definir Content-Type - o React Native define automaticamente para multipart/form-data
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const data: LoginResponse = await response.json();
|
||||
|
||||
if (!response.ok || data.status !== 200) {
|
||||
// A API retorna status 401 com mensagem quando não existe utilizador
|
||||
throw new Error(data.message || 'Erro ao fazer login');
|
||||
}
|
||||
|
||||
// Se a resposta for bem-sucedida, Guardar token e user
|
||||
if (data.token) {
|
||||
setToken(data.token);
|
||||
await AsyncStorage.setItem(STORAGE_KEYS.TOKEN, data.token);
|
||||
}
|
||||
|
||||
if (data.user) {
|
||||
setUser(data.user);
|
||||
await AsyncStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(data.user));
|
||||
} else {
|
||||
// Se não vier user na resposta, criar um objeto básico com email
|
||||
setUser(userData);
|
||||
await AsyncStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(userData));
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Erro desconhecido ao fazer login';
|
||||
setError(errorMessage);
|
||||
throw err;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const updateProfile = async (nome?: string, apelido?: string, oldPassword?: string, newPassword?: string) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
if (!token) {
|
||||
throw new Error('Não autenticado');
|
||||
}
|
||||
|
||||
const url = buildApiUrl(API_ENDPOINTS.UPDATE_PROFILE);
|
||||
// Criar FormData com os dados de atualização
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('token', token);
|
||||
|
||||
if (nome) {
|
||||
formData.append('nome', nome);
|
||||
}
|
||||
if (apelido) {
|
||||
formData.append('apelido', apelido);
|
||||
}
|
||||
if (newPassword) {
|
||||
const encryptedNewPassword = CryptoJS.MD5(newPassword).toString();
|
||||
formData.append('password', encryptedNewPassword);
|
||||
if (oldPassword) {
|
||||
const encryptedOldPassword = CryptoJS.MD5(oldPassword).toString();
|
||||
formData.append('old_password', encryptedOldPassword);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const data: LoginResponse = await response.json();
|
||||
|
||||
if (!response.ok || data.status !== 200) {
|
||||
throw new Error(data.message || 'Erro ao atualizar perfil');
|
||||
}
|
||||
|
||||
// Atualizar dados do utilizador se vierem na resposta
|
||||
if (data.user) {
|
||||
setUser(data.user);
|
||||
await AsyncStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(data.user));
|
||||
} else if (nome || apelido) {
|
||||
const updatedUser = { ...user, nome: nome ?? user?.nome, apelido: apelido ?? user?.apelido };
|
||||
setUser(updatedUser as User);
|
||||
await AsyncStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(updatedUser));
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Erro desconhecido ao atualizar perfil';
|
||||
setError(errorMessage);
|
||||
throw err;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
// Opcional: chamar endpoint de logout na API
|
||||
if (token) {
|
||||
try {
|
||||
const url = buildApiUrl(API_ENDPOINTS.USER_LOGOUT);
|
||||
await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...API_CONFIG.DEFAULT_HEADERS,
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Erro ao fazer logout na API:', err);
|
||||
// Continuar mesmo se falhar
|
||||
}
|
||||
}
|
||||
|
||||
// Limpar estado local
|
||||
setUser(null);
|
||||
setToken(null);
|
||||
setError(null);
|
||||
|
||||
// Limpar AsyncStorage
|
||||
await Promise.all([
|
||||
AsyncStorage.removeItem(STORAGE_KEYS.TOKEN),
|
||||
AsyncStorage.removeItem(STORAGE_KEYS.USER),
|
||||
]);
|
||||
} catch (err) {
|
||||
console.error('Erro ao fazer logout:', err);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const value: AuthContextType = {
|
||||
user,
|
||||
token,
|
||||
isAuthenticated: !!user && !!token,
|
||||
isLoading,
|
||||
login,
|
||||
logout,
|
||||
updateProfile,
|
||||
error,
|
||||
};
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
}
|
||||
|
||||
// Hook para usar o contexto
|
||||
export function useAuth() {
|
||||
const context = useContext(AuthContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useAuth deve ser usado dentro de um AuthProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
Reference in New Issue
Block a user