refactor: unify API response handling and improve error state management
This commit is contained in:
@@ -44,7 +44,7 @@ export default function SettingsScreen() {
|
||||
/>
|
||||
{!!authenticatedUser?.spotifyConfig && ( <ThemedButton mode={"outlined"} title={"Remove Spotify"} onPress={() => {
|
||||
const rest = new RestService(jwtToken);
|
||||
rest.updateSelfSpotifyConfig().then((result) => {
|
||||
rest.removeSpotifyConfig().then((result) => {
|
||||
console.log("Spotify Login entfernt");
|
||||
console.log(result);
|
||||
refreshUser()
|
||||
|
||||
+4
-4
@@ -55,8 +55,8 @@ export default function LoginScreen() {
|
||||
returnKeyType="next"
|
||||
value={username}
|
||||
onChangeText={setUsername}
|
||||
error={!!error && error?.id === "username"}
|
||||
errorText={error?.message}
|
||||
error={!!error && error?.toLowerCase().includes("user") }
|
||||
errorText={error!}
|
||||
autoCapitalize="none"
|
||||
/>
|
||||
|
||||
@@ -65,8 +65,8 @@ export default function LoginScreen() {
|
||||
returnKeyType="done"
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
error={!!error && error?.id === "password"}
|
||||
errorText={error?.message}
|
||||
error={!!error && error?.toLowerCase().includes("pass") }
|
||||
errorText={error!}
|
||||
autoComplete="password"
|
||||
/>
|
||||
<ThemedButton mode="outlined" onPress={onLoginPressed} title={"Login"} />
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, {useState} from "react";
|
||||
import { Paragraph} from "react-native-paper";
|
||||
import {useTheme} from "@/src/context/ThemeProvider";
|
||||
import {StyleSheet, View} from "react-native";
|
||||
import {RestService} from "@/src/services/RestService";
|
||||
import {ApiResponse, RestService} from "@/src/services/RestService";
|
||||
import {useAuth} from "@/src/context/AuthProvider";
|
||||
import CustomModal from "@/src/components/themed/CustomModal";
|
||||
import PasswordInput from "@/src/components/PasswordInput";
|
||||
@@ -14,22 +14,24 @@ export default function ChangePasswordModal() {
|
||||
const {token: jwtToken} = useAuth();
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
const [apiResponse, setApiResponse] = useState<{ success: boolean, message: string } | null>(null);
|
||||
const [apiResponse, setApiResponse] = useState<ApiResponse<{ message: string }> | null>(null);
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!password || !confirmPassword) {
|
||||
setApiResponse({success: false, message: "Bitte füllen Sie alle Felder aus!"});
|
||||
|
||||
|
||||
setApiResponse({ok: false, data: {message: "Bitte füllen Sie alle Felder aus!"}});
|
||||
return;
|
||||
}
|
||||
if (password !== confirmPassword) {
|
||||
setApiResponse({success: false, message: "Passwörter stimmen nicht überein!"});
|
||||
setApiResponse({ok: false, data: { message: "Passwörter stimmen nicht überein!"}});
|
||||
return;
|
||||
}
|
||||
new RestService(jwtToken).changeSelfPassword(password, confirmPassword).then(
|
||||
(response) => {
|
||||
console.log(response);
|
||||
setApiResponse(response.result);
|
||||
if (response.result.success) {
|
||||
setApiResponse(response);
|
||||
if (response.ok) {
|
||||
// add something here
|
||||
setPassword("");
|
||||
setConfirmPassword("");
|
||||
@@ -50,11 +52,11 @@ export default function ChangePasswordModal() {
|
||||
<CustomModal resetCallback={resetModal} buttonTitle="Passwort ändern">
|
||||
<View style={styles.modalContent}>
|
||||
<Paragraph>Passwort ändern</Paragraph>
|
||||
{apiResponse && apiResponse.message && (
|
||||
{apiResponse && apiResponse.data?.message && (
|
||||
<View
|
||||
style={[styles.apiResponseBox, {backgroundColor: apiResponse.success ? theme.colors.success : theme.colors.error}]}>
|
||||
style={[styles.apiResponseBox, {backgroundColor: apiResponse.ok ? theme.colors.success : theme.colors.error}]}>
|
||||
<Paragraph
|
||||
style={{color: apiResponse.success ? theme.colors.onSuccess : theme.colors.onError}}>{apiResponse.message}</Paragraph>
|
||||
style={{color: apiResponse.ok ? theme.colors.onSuccess : theme.colors.onError}}>{apiResponse.data.message}</Paragraph>
|
||||
</View>
|
||||
)}
|
||||
|
||||
|
||||
@@ -3,18 +3,14 @@ import {getFromStorage, JWT_TOKEN_KEY, removeFromStorage, saveInStorage} from "@
|
||||
import {RestService} from "@/src/services/RestService";
|
||||
import {User} from "@/src/model/User";
|
||||
|
||||
interface AuthError {
|
||||
message: string;
|
||||
id: "username" | "password" | "general";
|
||||
}
|
||||
|
||||
type AuthContextType = {
|
||||
isAuthenticated: boolean | null;
|
||||
token: string | null;
|
||||
login: (username: string, password: string) => Promise<void>;
|
||||
logout: () => Promise<void>;
|
||||
error: AuthError | null;
|
||||
authenticatedUser: User | null;
|
||||
error: string | null;
|
||||
loading: boolean;
|
||||
refreshUser: () => Promise<void>;
|
||||
};
|
||||
@@ -25,7 +21,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({children}
|
||||
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
|
||||
const [authenticatedUser, setAuthenticatedUser] = useState<User | null>(null);
|
||||
const [token, setToken] = useState<string | null>(null);
|
||||
const [error, setError] = useState<AuthError | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -45,19 +41,17 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({children}
|
||||
}, []);
|
||||
|
||||
const saveUser = async (token: string): Promise<User | null> => {
|
||||
const user = await new RestService(token).getSelf();
|
||||
if (!user) {
|
||||
const response = await new RestService(token).getSelf();
|
||||
if (!response.ok || !response.data) {
|
||||
// token ist ungültig
|
||||
await removeFromStorage(JWT_TOKEN_KEY);
|
||||
setToken(null);
|
||||
setIsAuthenticated(false);
|
||||
setAuthenticatedUser(null);
|
||||
setError({
|
||||
message: "Token invalid",
|
||||
id: "general",
|
||||
});
|
||||
setError("Token invalid");
|
||||
return null;
|
||||
}
|
||||
const user = response.data;
|
||||
setAuthenticatedUser(user);
|
||||
return user;
|
||||
}
|
||||
@@ -68,21 +62,19 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({children}
|
||||
return;
|
||||
}
|
||||
const response = await new RestService(null).login(username, password);
|
||||
if (!response.success) {
|
||||
console.error("Login failed:", response.message);
|
||||
setError({
|
||||
message: response.message,
|
||||
id: response.id,
|
||||
});
|
||||
if (!response.ok) {
|
||||
console.error("Login failed:", response.data.message);
|
||||
setError(response.data.message!);
|
||||
setIsAuthenticated(false);
|
||||
return;
|
||||
}
|
||||
await saveInStorage(JWT_TOKEN_KEY, response.token);
|
||||
setToken(response.token);
|
||||
const token = response.data.token!;
|
||||
await saveInStorage(JWT_TOKEN_KEY, token);
|
||||
setToken(token);
|
||||
// Fehler zurücksetzen
|
||||
setError(null);
|
||||
// User laden und ERST DANN isAuthenticated setzen
|
||||
const user = await saveUser(response.token);
|
||||
const user = await saveUser(token);
|
||||
setIsAuthenticated(!!user);
|
||||
setLoading(false);
|
||||
};
|
||||
@@ -95,12 +87,15 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({children}
|
||||
};
|
||||
|
||||
const refreshUser = async () => {
|
||||
console.log("refreshUser")
|
||||
console.log(token)
|
||||
if (!token) return;
|
||||
await saveUser(token);
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{isAuthenticated, token, login, logout, error, authenticatedUser, loading, refreshUser}}>
|
||||
<AuthContext.Provider
|
||||
value={{isAuthenticated, token, login, logout, error, authenticatedUser, loading, refreshUser}}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
|
||||
@@ -35,7 +35,12 @@ export const useSpotifyAuth = (
|
||||
if (response?.type === 'success') {
|
||||
try {
|
||||
const {code} = response.params;
|
||||
const token = (await new RestService(jwtToken).exchangeSpotifyCodeForToken(code)).token;
|
||||
const res = (await new RestService(jwtToken).exchangeSpotifyCodeForToken(code));
|
||||
if (!res.ok) {
|
||||
console.log("Fehler beim token abrufen");
|
||||
return;
|
||||
}
|
||||
const token = res.data.token;
|
||||
console.log('Token:', token);
|
||||
onAuthSuccess(token);
|
||||
} catch (error) {
|
||||
|
||||
+27
-20
@@ -4,10 +4,6 @@ import {SpotifyConfig, User} from "@/src/model/User";
|
||||
|
||||
const API_URL = process.env.EXPO_PUBLIC_API_URL;
|
||||
|
||||
interface TokenWrapper {
|
||||
token: Token
|
||||
}
|
||||
|
||||
export interface Token {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
@@ -16,6 +12,11 @@ export interface Token {
|
||||
scope: string;
|
||||
}
|
||||
|
||||
export interface ApiResponse<T> {
|
||||
ok: boolean;
|
||||
data: T;
|
||||
}
|
||||
|
||||
class RestService {
|
||||
private readonly jwtToken: string | null;
|
||||
private api: AxiosInstance;
|
||||
@@ -54,31 +55,33 @@ class RestService {
|
||||
);
|
||||
}
|
||||
|
||||
async exchangeSpotifyCodeForToken(code: string): Promise<TokenWrapper> {
|
||||
async exchangeSpotifyCodeForToken(code: string): Promise<ApiResponse<{ token: Token }>> {
|
||||
const redirectUri = makeRedirectUri({
|
||||
scheme: 'led.matrix',
|
||||
path: 'callback',
|
||||
});
|
||||
return this.request<TokenWrapper>(
|
||||
'GET',
|
||||
`/spotify/token/generate/code/${code}/redirect-uri/${encodeURIComponent(redirectUri)}`
|
||||
return this.request<ApiResponse<{ token: Token }>>(
|
||||
'POST',
|
||||
`/spotify/token/generate`,
|
||||
{"authCode": code, "redirectUri": redirectUri},
|
||||
{'Content-Type': 'application/json'}
|
||||
);
|
||||
}
|
||||
|
||||
async fetchAllUser(): Promise<{ users: User[] }> {
|
||||
return this.request<{ users: User[] }>('GET', '/user');
|
||||
async fetchAllUser(): Promise<ApiResponse<{ users: User[] }>> {
|
||||
return this.request<ApiResponse<{ users: User[] }>>('GET', '/user');
|
||||
}
|
||||
|
||||
async fetchUserById(id: string): Promise<User> {
|
||||
return this.request<User>('GET', `/user/${id}`);
|
||||
async fetchUserById(id: string): Promise<ApiResponse<User>> {
|
||||
return this.request<ApiResponse<User>>('GET', `/user/${id}`);
|
||||
}
|
||||
|
||||
async getSelf(): Promise<User> {
|
||||
return this.request<User>('GET', '/user/me');
|
||||
async getSelf(): Promise<ApiResponse<User>> {
|
||||
return this.request<ApiResponse<User>>('GET', '/user/me');
|
||||
}
|
||||
|
||||
async changeSelfPassword(password: string, passwordConfirmation: string): Promise<{result: { success: boolean; message: string }}> {
|
||||
return this.request<{result: { success: boolean; message: string } }>(
|
||||
async changeSelfPassword(password: string, passwordConfirmation: string): Promise<ApiResponse<{ message: string }>> {
|
||||
return this.request<ApiResponse<{message: string}>>(
|
||||
'PUT',
|
||||
'/user/me/password',
|
||||
{password, passwordConfirmation},
|
||||
@@ -104,9 +107,9 @@ class RestService {
|
||||
);
|
||||
}
|
||||
|
||||
async updateSelfSpotifyConfig(spotifyConfig?: SpotifyConfig): Promise<{ success: boolean; message: string }> {
|
||||
async updateSelfSpotifyConfig(spotifyConfig?: SpotifyConfig): Promise<ApiResponse<{message: string}>> {
|
||||
const payload = spotifyConfig ?? {};
|
||||
return this.request<{ success: boolean; message: string }>(
|
||||
return this.request<ApiResponse<{message: string}>>(
|
||||
'PUT',
|
||||
'/user/me/spotify',
|
||||
payload,
|
||||
@@ -114,8 +117,12 @@ class RestService {
|
||||
);
|
||||
}
|
||||
|
||||
async login(username: string, password: string): Promise<{ success: boolean; token: string; message: string; id: "username" | "password" }> {
|
||||
return this.request<{ success: boolean; token: string; message: string; id: "username" | "password" }>(
|
||||
async removeSpotifyConfig(): Promise<ApiResponse<User>> {
|
||||
return this.request<ApiResponse<User>>('DELETE', '/user/me/spotify');
|
||||
}
|
||||
|
||||
async login(username: string, password: string): Promise<ApiResponse<{ message?: string, token?: string }>> {
|
||||
return this.request<ApiResponse<{message?: string, token?: string}>>(
|
||||
"POST",
|
||||
'/auth/login',
|
||||
{username, password},
|
||||
|
||||
Reference in New Issue
Block a user