Adding login errors

This commit is contained in:
Zoe Roux 2023-01-30 02:14:42 +09:00 committed by Zoe Roux
parent e5027cf00d
commit b62b272492
4 changed files with 53 additions and 21 deletions

View File

@ -26,14 +26,14 @@ import { QueryClientProvider } from "@tanstack/react-query";
import i18next from "i18next"; import i18next from "i18next";
import { Stack } from "expo-router"; import { Stack } from "expo-router";
import { getLocales } from "expo-localization"; import { getLocales } from "expo-localization";
import * as SplashScreen from "expo-splash-screen"; import { SplashScreen } from "expo-router";
import { import {
useFonts, useFonts,
Poppins_300Light, Poppins_300Light,
Poppins_400Regular, Poppins_400Regular,
Poppins_900Black, Poppins_900Black,
} from "@expo-google-fonts/poppins"; } from "@expo-google-fonts/poppins";
import { useCallback, useLayoutEffect, useState } from "react"; import { useState } from "react";
import { useColorScheme } from "react-native"; import { useColorScheme } from "react-native";
import { initReactI18next } from "react-i18next"; import { initReactI18next } from "react-i18next";
import { useTheme } from "yoshiki/native"; import { useTheme } from "yoshiki/native";
@ -75,25 +75,12 @@ const ThemedStack = ({ onLayout }: { onLayout?: () => void }) => {
); );
}; };
SplashScreen.preventAutoHideAsync();
export default function Root() { export default function Root() {
const [queryClient] = useState(() => createQueryClient()); const [queryClient] = useState(() => createQueryClient());
const theme = useColorScheme(); const theme = useColorScheme();
const [fontsLoaded] = useFonts({ Poppins_300Light, Poppins_400Regular, Poppins_900Black }); const [fontsLoaded] = useFonts({ Poppins_300Light, Poppins_400Regular, Poppins_900Black });
useLayoutEffect(() => { if (!fontsLoaded) return <SplashScreen />;
// This does not seems to work on the global scope so why not.
SplashScreen.preventAutoHideAsync();
})
const onLayout = useCallback(async () => {
if (fontsLoaded) {
await SplashScreen.hideAsync();
}
}, [fontsLoaded]);
if (!fontsLoaded) return null;
return ( return (
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<ThemeSelector <ThemeSelector
@ -106,7 +93,7 @@ export default function Root() {
}} }}
> >
<PortalProvider> <PortalProvider>
<ThemedStack onLayout={onLayout} /> <ThemedStack />
</PortalProvider> </PortalProvider>
</ThemeSelector> </ThemeSelector>
</QueryClientProvider> </QueryClientProvider>

View File

@ -18,7 +18,7 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>. * along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { QueryPage } from "@kyoo/models"; import { KyooErrors, kyooUrl, QueryPage } from "@kyoo/models";
import { Button, P, Input, ts, H1, A, IconButton } from "@kyoo/primitives"; import { Button, P, Input, ts, H1, A, IconButton } from "@kyoo/primitives";
import { ComponentProps, useState } from "react"; import { ComponentProps, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -65,6 +65,32 @@ const PasswordInput = (props: ComponentProps<typeof Input>) => {
); );
}; };
const login = async (username: string, password: string) => {
let resp;
try {
resp = await fetch(`${kyooUrl}/auth/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
username,
password,
}),
});
} catch (e) {
console.error("Login error", e);
throw { errors: ["Could not reach Kyoo's server."] } as KyooErrors;
}
if (!resp.ok) {
const err = await resp.json() as KyooErrors;
return { type: "error", value: null, error: err.errors[0] };
}
const token = await resp.json();
// TODO: Save the token in the secure storage.
return { type: "value", value: token, error: null };
};
export const LoginPage: QueryPage = () => { export const LoginPage: QueryPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { css } = useYoshiki(); const { css } = useYoshiki();
@ -78,6 +104,10 @@ export const LoginPage: QueryPage = () => {
default: {}, default: {},
}); });
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
return ( return (
<ImageBackground <ImageBackground
source={{ uri: src }} source={{ uri: src }}
@ -110,12 +140,25 @@ export const LoginPage: QueryPage = () => {
<Input variant="big" /> <Input variant="big" />
</> </>
)} )}
<P {...css({ paddingLeft: ts(1) })}>{t("login.email")}</P> <P {...css({ paddingLeft: ts(1) })}>{t("login.username")}</P>
<Input autoComplete="email" variant="big" /> <Input
autoComplete="username"
variant="big"
onChangeText={(value) => setUsername(value)}
/>
<P {...css({ paddingLeft: ts(1) })}>{t("login.password")}</P> <P {...css({ paddingLeft: ts(1) })}>{t("login.password")}</P>
<PasswordInput autoComplete="password" variant="big" /> <PasswordInput
autoComplete="password"
variant="big"
onChangeText={(value) => setPassword(value)}
/>
{error && <P {...css({ color: (theme) => theme.colors.red })}>{error}</P>}
<Button <Button
text={t("login.login")} text={t("login.login")}
onPress={async () => {
const { error } = await login(username, password);
setError(error);
}}
{...css({ {...css({
m: ts(1), m: ts(1),
width: px(250), width: px(250),

View File

@ -55,6 +55,7 @@
"register": "Register", "register": "Register",
"server": "Server Address", "server": "Server Address",
"email": "Email", "email": "Email",
"username": "Username",
"password": "Password", "password": "Password",
"or-register": "Dont have an account? <1>Register</1>." "or-register": "Dont have an account? <1>Register</1>."
} }

View File

@ -55,6 +55,7 @@
"register": "Créer un compte", "register": "Créer un compte",
"server": "Addresse du serveur", "server": "Addresse du serveur",
"email": "Email", "email": "Email",
"username": "Username",
"password": "Mot de passe", "password": "Mot de passe",
"or-register": "Vous n'avez pas de compte ? <1>Inscrivez-vous</1>." "or-register": "Vous n'avez pas de compte ? <1>Inscrivez-vous</1>."
} }