movie-web/src/pages/parts/auth/LoginFormPart.tsx

109 lines
3.4 KiB
TypeScript

import { useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { useAsyncFn } from "react-use";
import type { AsyncReturnType } from "type-fest";
import { verifyValidMnemonic } from "@/backend/accounts/crypto";
import { Button } from "@/components/buttons/Button";
import { BrandPill } from "@/components/layout/BrandPill";
import {
LargeCard,
LargeCardButtons,
LargeCardText,
} from "@/components/layout/LargeCard";
import { MwLink } from "@/components/text/Link";
import { AuthInputBox } from "@/components/text-inputs/AuthInputBox";
import { useAuth } from "@/hooks/auth/useAuth";
import { useBookmarkStore } from "@/stores/bookmarks";
import { useProgressStore } from "@/stores/progress";
interface LoginFormPartProps {
onLogin?: () => void;
}
export function LoginFormPart(props: LoginFormPartProps) {
const [mnemonic, setMnemonic] = useState("");
const [device, setDevice] = useState("");
const { login, restore, importData } = useAuth();
const progressItems = useProgressStore((store) => store.items);
const bookmarkItems = useBookmarkStore((store) => store.bookmarks);
const { t } = useTranslation();
const [result, execute] = useAsyncFn(
async (inputMnemonic: string, inputdevice: string) => {
if (!verifyValidMnemonic(inputMnemonic))
throw new Error(t("auth.login.validationError") ?? undefined);
const validatedDevice = inputdevice.trim();
if (validatedDevice.length === 0)
throw new Error(t("auth.login.deviceLengthError") ?? undefined);
let account: AsyncReturnType<typeof login>;
try {
account = await login({
mnemonic: inputMnemonic,
userData: {
device: validatedDevice,
},
});
} catch (err) {
if ((err as any).status === 401)
throw new Error(t("auth.login.validationError") ?? undefined);
throw err;
}
await importData(account, progressItems, bookmarkItems);
await restore(account);
props.onLogin?.();
},
[props, login, restore, t]
);
return (
<LargeCard top={<BrandPill backgroundClass="bg-[#161527]" />}>
<LargeCardText title={t("auth.login.title")}>
{t("auth.login.description")}
</LargeCardText>
<div className="space-y-4">
<AuthInputBox
label={t("auth.login.passphraseLabel") ?? undefined}
value={mnemonic}
autoComplete="username"
name="username"
onChange={setMnemonic}
placeholder={t("auth.login.passphrasePlaceholder") ?? undefined}
passwordToggleable
/>
<AuthInputBox
label={t("auth.deviceNameLabel") ?? undefined}
value={device}
onChange={setDevice}
placeholder={t("auth.deviceNamePlaceholder") ?? undefined}
/>
{result.error && !result.loading ? (
<p className="text-authentication-errorText">
{result.error.message}
</p>
) : null}
</div>
<LargeCardButtons>
<Button
theme="purple"
loading={result.loading}
onClick={() => execute(mnemonic, device)}
>
{t("auth.login.submit")}
</Button>
</LargeCardButtons>
<p className="text-center mt-6">
<Trans i18nKey="auth.createAccount">
<MwLink to="/register">.</MwLink>
</Trans>
</p>
</LargeCard>
);
}