Create login and register function

This commit is contained in:
Zoe Roux 2023-02-01 02:50:11 +09:00 committed by Zoe Roux
parent 2b87aacc58
commit ffbf4be19e
5 changed files with 66 additions and 43 deletions

View File

@ -39,9 +39,9 @@ export const kyooUrl =
? process.env.KYOO_URL ?? "http://localhost:5000" ? process.env.KYOO_URL ?? "http://localhost:5000"
: "/api"; : "/api";
const queryFn = async <Data,>( export const queryFn = async <Data,>(
type: z.ZodType<Data>, context: QueryFunctionContext | { path: string[]; body?: object; method: "GET" | "POST" },
context: QueryFunctionContext, type?: z.ZodType<Data>,
): Promise<Data> => { ): Promise<Data> => {
if (!kyooUrl) console.error("Kyoo's url is not defined."); if (!kyooUrl) console.error("Kyoo's url is not defined.");
@ -50,10 +50,21 @@ const queryFn = async <Data,>(
resp = await fetch( resp = await fetch(
[kyooUrl] [kyooUrl]
.concat( .concat(
context.pageParam ? [context.pageParam] : (context.queryKey.filter((x) => x) as string[]), "path" in context
? context.path
: context.pageParam
? [context.pageParam]
: (context.queryKey.filter((x) => x) as string[]),
) )
.join("/") .join("/")
.replace("/?", "?"), .replace("/?", "?"),
{
// @ts-ignore
method: context.method,
// @ts-ignore
body: context.body ? JSON.stringify(context.body) : undefined,
headers: "body" in context ? { "Content-Type": "application/json" } : undefined,
},
); );
} catch (e) { } catch (e) {
console.log("Fetch error", e); console.log("Fetch error", e);
@ -81,6 +92,7 @@ const queryFn = async <Data,>(
console.error("Invald json from kyoo", e); console.error("Invald json from kyoo", e);
throw { errors: ["Invalid repsonse from kyoo"] }; throw { errors: ["Invalid repsonse from kyoo"] };
} }
if (!type) return data;
const parsed = await type.safeParseAsync(data); const parsed = await type.safeParseAsync(data);
if (!parsed.success) { if (!parsed.success) {
console.log("Parse error: ", parsed.error); console.log("Parse error: ", parsed.error);
@ -137,7 +149,7 @@ const toQueryKey = <Data,>(query: QueryIdentifier<Data>) => {
export const useFetch = <Data,>(query: QueryIdentifier<Data>) => { export const useFetch = <Data,>(query: QueryIdentifier<Data>) => {
return useQuery<Data, KyooErrors>({ return useQuery<Data, KyooErrors>({
queryKey: toQueryKey(query), queryKey: toQueryKey(query),
queryFn: (ctx) => queryFn(query.parser, ctx), queryFn: (ctx) => queryFn(ctx, query.parser),
}); });
}; };
@ -149,7 +161,7 @@ export const useInfiniteFetch = <Data,>(
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
const ret = useInfiniteQuery<Data[], KyooErrors>({ const ret = useInfiniteQuery<Data[], KyooErrors>({
queryKey: toQueryKey(query), queryKey: toQueryKey(query),
queryFn: (ctx) => queryFn(z.array(query.parser), ctx), queryFn: (ctx) => queryFn(ctx, z.array(query.parser)),
getNextPageParam: query.getNext, getNextPageParam: query.getNext,
...options, ...options,
}); });
@ -158,7 +170,7 @@ export const useInfiniteFetch = <Data,>(
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
const ret = useInfiniteQuery<Page<Data>, KyooErrors>({ const ret = useInfiniteQuery<Page<Data>, KyooErrors>({
queryKey: toQueryKey(query), queryKey: toQueryKey(query),
queryFn: (ctx) => queryFn(Paged(query.parser), ctx), queryFn: (ctx) => queryFn(ctx, Paged(query.parser)),
getNextPageParam: (page: Page<Data>) => page?.next || undefined, getNextPageParam: (page: Page<Data>) => page?.next || undefined,
}); });
return { ...ret, items: ret.data?.pages.flatMap((x) => x.items) }; return { ...ret, items: ret.data?.pages.flatMap((x) => x.items) };
@ -175,12 +187,12 @@ export const fetchQuery = async (queries: QueryIdentifier[]) => {
if (query.infinite) { if (query.infinite) {
return client.prefetchInfiniteQuery({ return client.prefetchInfiniteQuery({
queryKey: toQueryKey(query), queryKey: toQueryKey(query),
queryFn: (ctx) => queryFn(Paged(query.parser), ctx), queryFn: (ctx) => queryFn(ctx, Paged(query.parser)),
}); });
} else { } else {
return client.prefetchQuery({ return client.prefetchQuery({
queryKey: toQueryKey(query), queryKey: toQueryKey(query),
queryFn: (ctx) => queryFn(query.parser, ctx), queryFn: (ctx) => queryFn(ctx, query.parser),
}); });
} }
}), }),

View File

@ -26,7 +26,6 @@ import { ts } from "./utils";
export const Button = ({ export const Button = ({
text, text,
onPress,
...props ...props
}: { text: string } & ComponentProps<typeof PressableFeedback>) => { }: { text: string } & ComponentProps<typeof PressableFeedback>) => {
const { css } = useYoshiki(); const { css } = useYoshiki();

View File

@ -0,0 +1,35 @@
/*
* Kyoo - A portable and vast media library solution.
* Copyright (c) Kyoo.
*
* See AUTHORS.md and LICENSE file in the project root for full license information.
*
* Kyoo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Kyoo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import { KyooErrors, queryFn } from "@kyoo/models";
export const loginFunc = async (action: "register" | "login", body: object) => {
try {
const token = await queryFn({
path: ["auth", action],
method: "POST",
body: body,
});
return null;
} catch (e) {
return (e as KyooErrors).errors[0];
}
};

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 { KyooErrors, kyooUrl, QueryPage } from "@kyoo/models"; import { QueryPage } from "@kyoo/models";
import { Button, P, Input, ts, H1, A } from "@kyoo/primitives"; import { Button, P, Input, ts, H1, A } from "@kyoo/primitives";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -28,32 +28,7 @@ import { percent, px, useYoshiki } from "yoshiki/native";
import { DefaultLayout } from "../layout"; import { DefaultLayout } from "../layout";
import { FormPage } from "./form"; import { FormPage } from "./form";
import { PasswordInput } from "./password-input"; import { PasswordInput } from "./password-input";
import { loginFunc } from "./api";
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 [username, setUsername] = useState(""); const [username, setUsername] = useState("");
@ -88,7 +63,7 @@ export const LoginPage: QueryPage = () => {
<Button <Button
text={t("login.login")} text={t("login.login")}
onPress={async () => { onPress={async () => {
const { error } = await login(username, password); const error = await loginFunc("login", {username, password});
setError(error); setError(error);
}} }}
{...css({ {...css({

View File

@ -18,9 +18,9 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>. * along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { KyooErrors, kyooUrl, QueryPage } from "@kyoo/models"; import { QueryPage } from "@kyoo/models";
import { Button, P, Input, ts, H1, A } from "@kyoo/primitives"; import { Button, P, Input, ts, H1, A } from "@kyoo/primitives";
import { useEffect, useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Platform } from "react-native"; import { Platform } from "react-native";
import { Trans } from "react-i18next"; import { Trans } from "react-i18next";
@ -28,6 +28,7 @@ import { percent, px, useYoshiki } from "yoshiki/native";
import { DefaultLayout } from "../layout"; import { DefaultLayout } from "../layout";
import { FormPage } from "./form"; import { FormPage } from "./form";
import { PasswordInput } from "./password-input"; import { PasswordInput } from "./password-input";
import { loginFunc } from "./api";
export const RegisterPage: QueryPage = () => { export const RegisterPage: QueryPage = () => {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
@ -74,10 +75,11 @@ export const RegisterPage: QueryPage = () => {
)} )}
{error && <P {...css({ color: (theme) => theme.colors.red })}>{error}</P>} {error && <P {...css({ color: (theme) => theme.colors.red })}>{error}</P>}
<Button <Button
text={t("login.login")} text={t("login.register")}
disabled={error != null || password !== confirm} disabled={password !== confirm}
onPress={async () => { onPress={async () => {
const { error } = await register(email, username, password); console.log("toto")
const error = await loginFunc("register", { email, username, password });
setError(error); setError(error);
}} }}
{...css({ {...css({