diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 80e9d1a..2fdd84f 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -13,8 +13,10 @@ export default function HomeScreen() { const {theme} = useTheme(); useEffect(() => { - setIdle(authenticatedUser!.lastState?.global.mode === "idle") - }, []) + if (authenticatedUser) { + setIdle(authenticatedUser.lastState?.global.mode === "idle") + } + }, [authenticatedUser]); return ( diff --git a/app/(tabs)/modes/text.tsx b/app/(tabs)/modes/text.tsx index 364fdcc..3de23c3 100644 --- a/app/(tabs)/modes/text.tsx +++ b/app/(tabs)/modes/text.tsx @@ -9,8 +9,9 @@ import {View} from "react-native"; export default function TextScreen() { const {authenticatedUser} = useAuth(); - const [textProps, setTextProps] = useState(authenticatedUser!.lastState?.text); - + const [textProps, setTextProps] = useState( + authenticatedUser?.lastState?.text! || { text: '', color: [255, 255, 255] } + ); return ( @@ -20,7 +21,7 @@ export default function TextScreen() { value={textProps?.text} onChangeText={(value: string) => { setTextProps(prev => ({ - ...prev!, + ...prev, text: value })); }} @@ -29,7 +30,7 @@ export default function TextScreen() { setTextProps(prev => ({ ...prev!, color: rgb }))} - defaultColor={textProps?.color} + defaultColor={textProps.color} /> diff --git a/app/(tabs)/settings.tsx b/app/(tabs)/settings.tsx index 738b231..e2fc01b 100644 --- a/app/(tabs)/settings.tsx +++ b/app/(tabs)/settings.tsx @@ -14,6 +14,7 @@ import {useRouter} from "expo-router"; export default function SettingsScreen() { const {token: jwtToken, authenticatedUser, logout, refreshUser} = useAuth(); const router = useRouter(); + console.log("Mashallah", jwtToken); const handleAuthSuccess = (token: Token) => { const spotifyConfig = { @@ -37,11 +38,11 @@ export default function SettingsScreen() { Einen wunderschönen guten Tag, {authenticatedUser?.name} - + {!!authenticatedUser?.spotifyConfig && ( { const rest = new RestService(jwtToken); rest.removeSpotifyConfig().then((result) => { diff --git a/app/login.tsx b/app/login.tsx index c76b04a..255858b 100644 --- a/app/login.tsx +++ b/app/login.tsx @@ -13,7 +13,6 @@ import {useRouter} from "expo-router"; import ThemeToggleButton from "@/src/components/ThemeToggleButton"; import PasswordInput from "@/src/components/PasswordInput"; - export default function LoginScreen() { const {isAuthenticated, login, logout, error} = useAuth(); const router = useRouter(); @@ -55,8 +54,8 @@ export default function LoginScreen() { returnKeyType="next" value={username} onChangeText={setUsername} - error={!!error && error?.toLowerCase().includes("user") } - errorText={error!} + error={!!error && error?.field === "username" } + errorText={error?.message} autoCapitalize="none" /> @@ -65,8 +64,8 @@ export default function LoginScreen() { returnKeyType="done" value={password} onChangeText={setPassword} - error={!!error && error?.toLowerCase().includes("pass") } - errorText={error!} + error={!!error && error?.field === "password" } + errorText={error?.message} autoComplete="password" /> diff --git a/src/components/ImagePicker.tsx b/src/components/ImagePicker.tsx index d9e6914..e9e6017 100644 --- a/src/components/ImagePicker.tsx +++ b/src/components/ImagePicker.tsx @@ -15,25 +15,29 @@ export default function CustomImagePicker({onSuccess, onFailure, onCanceled}: Pr const [image, setImage] = useState(null); const pickImage = async () => { - // No permissions request is necessary for launching the image library - let result = ImagePicker.launchImageLibraryAsync({ - mediaTypes: ['images', 'videos'], - allowsEditing: true, - aspect: [4, 3], - quality: 1, - base64: true, - }).then((result) => { + try { + const result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ['images', 'videos'], + allowsEditing: true, + aspect: [4, 3], + quality: 1, + base64: true, + }); + if (result.canceled) { onCanceled(); } else { setImage(result.assets[0].uri); onSuccess(result); } - }).catch((error) => { - onFailure(error); - }); - console.log(result); - } + } catch (error) { + if (error instanceof Error) { + onFailure(error); + } else { + onFailure(new Error('An unknown error occurred during image picking.')); + } + } + }; return ( diff --git a/src/components/SpotifyAuthButton.tsx b/src/components/SpotifyAuthButton.tsx index dd138d7..22e6a33 100644 --- a/src/components/SpotifyAuthButton.tsx +++ b/src/components/SpotifyAuthButton.tsx @@ -5,7 +5,7 @@ import ThemedButton from "@/src/components/themed/ThemedButton"; interface SpotifyAuthButtonProps { onAuthSuccess: (token: Token) => void; - jwtToken: string; + jwtToken: string | null; disabled: boolean; } diff --git a/src/context/AuthProvider.tsx b/src/context/AuthProvider.tsx index a18bb81..da7a8e2 100644 --- a/src/context/AuthProvider.tsx +++ b/src/context/AuthProvider.tsx @@ -11,7 +11,7 @@ type AuthContextType = { login: (username: string, password: string) => Promise; logout: () => Promise; authenticatedUser: User | null; - error: string | null; + error: { field: string, message: string } | null; loading: boolean; refreshUser: () => Promise; }; @@ -22,7 +22,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({children} const [isAuthenticated, setIsAuthenticated] = useState(null); const [authenticatedUser, setAuthenticatedUser] = useState(null); const [token, setToken] = useState(null); - const [error, setError] = useState(null); + const [error, setError] = useState<{ field: string, message: string } | null>(null); const [loading, setLoading] = useState(true); useEffect(() => { @@ -53,7 +53,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({children} setToken(null); setIsAuthenticated(false); setAuthenticatedUser(null); - setError("Token invalid"); + setError({field: "general", message:"Token is invalid."}); return null; } const user = response.data; @@ -71,8 +71,9 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({children} const response = await new RestService(null).login(username, password); if (!response.ok) { - console.error("Login failed:", response.data.message); - setError(response.data.message!); + console.error("Login failed:", response.data); + const message = response.data.message!; + setError({field: response.data.details?.field!, message}); setIsAuthenticated(false); setLoading(false); return; diff --git a/src/hooks/useSpotifyAuth.ts b/src/hooks/useSpotifyAuth.ts index 1c40dd6..ad5b48b 100644 --- a/src/hooks/useSpotifyAuth.ts +++ b/src/hooks/useSpotifyAuth.ts @@ -15,7 +15,7 @@ interface UseSpotifyAuthResult { export const useSpotifyAuth = ( onAuthSuccess: (token: Token) => void, - jwtToken: string, + jwtToken: string | null, ): UseSpotifyAuthResult => { const [request, response, promptAsync] = useAuthRequest( { diff --git a/src/services/RestService.ts b/src/services/RestService.ts index eee164c..cad0b6d 100644 --- a/src/services/RestService.ts +++ b/src/services/RestService.ts @@ -82,8 +82,10 @@ class RestService { return this.request>('GET', '/user/me'); } - async changeSelfPassword(password: string, passwordConfirmation: string): Promise> { - return this.request>( + async changeSelfPassword(password: string, passwordConfirmation: string): Promise> { + return this.request>( 'PUT', '/user/me/password', {password, passwordConfirmation}, @@ -109,9 +111,9 @@ class RestService { ); } - async updateSelfSpotifyConfig(spotifyConfig?: SpotifyConfig): Promise> { + async updateSelfSpotifyConfig(spotifyConfig?: SpotifyConfig): Promise> { const payload = spotifyConfig ?? {}; - return this.request>( + return this.request>( 'PUT', '/user/me/spotify', payload, @@ -123,8 +125,18 @@ class RestService { return this.request>('DELETE', '/user/me/spotify'); } - async login(username: string, password: string): Promise> { - return this.request>( + async login(username: string, password: string): Promise> { + return this.request>( "POST", '/auth/login', {username, password},