import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import { useT } from "../i18n"; import { api, ApiError } from "../lib/api"; export function Login() { const t = useT(); const [handle, setHandle] = useState(""); const [password, setPassword] = useState(""); const [totp, setTotp] = useState(""); const [needTotp, setNeedTotp] = useState(false); const navigate = useNavigate(); const queryClient = useQueryClient(); // Only offer the signup link when the server would actually accept one. const registration = useQuery({ queryKey: ["registration"], queryFn: api.registrationStatus }); const mutation = useMutation({ mutationFn: () => api.login(handle, password, needTotp ? totp : undefined), onSuccess: (user) => { queryClient.setQueryData(["me"], user); navigate("/mail"); }, onError: (error) => { if (error instanceof ApiError && (error.code === "totp_required" || error.code === "totp_invalid")) { setNeedTotp(true); } }, }); const error = mutation.error instanceof ApiError ? mutation.error : null; // Don't show the "code required" error as a failure once we've revealed the field. const showError = error && !(needTotp && error.code === "totp_required"); return (

{t("auth.signInTitle")}

{ e.preventDefault(); mutation.mutate(); }} > setHandle(e.target.value)} disabled={needTotp} autoFocus /> setPassword(e.target.value)} disabled={needTotp} /> {needTotp && (
setTotp(e.target.value)} autoFocus />

{t("auth.totpPrompt")}

)} {showError &&

{error.message}

}

{t("auth.forgotPassword")}

{registration.data?.open && (

{t("auth.noAccount")}{" "} {t("auth.register")}

)}
); }