import { type ReactElement, useState } from "react"; import { AuthCard, authStyles, useSingleSubmission } from "./AuthCard.js"; export interface VerificationNoticeProps { /** * The address the person just tried to sign in with, when this notice followed a sign-in attempt. * * Present only because they typed it moments ago: it is never learned from the server, and the * resend it enables reveals nothing, because the server answers identically for an address with no * account (ADR 0038). */ readonly email?: string; readonly onResend: (email: string, signal: AbortSignal) => Promise; readonly onReturnToSignIn: () => void; } /** Shown after a correct password on an address that has not been verified. No session exists at * this point — the identity system refused to create one — so nothing here is authenticated. */ export function VerificationNotice(props: VerificationNoticeProps): ReactElement { const [email, setEmail] = useState(props.email ?? ""); const [sent, setSent] = useState(false); const { errorMessage, pending, submit } = useSingleSubmission( "Unable to send a verification email right now.", ); return (

Your password was accepted, but this address has not been confirmed yet. Open the verification link we sent you, then sign in again.

{sent ? (

If an unverified Ekklesia account exists for that address, a new verification link is on its way. It expires in one hour.

) : (
void submit(event, async (signal) => { await props.onResend(email, signal); setSent(true); }) } style={authStyles.form} > {errorMessage ? ( ) : undefined}
)}
); } export interface VerificationResultProps { readonly outcome: | { readonly kind: "verified" } | { readonly kind: "failed"; readonly expired: boolean }; readonly onReturnToSignIn: () => void; } /** * Where an emailed verification link lands. * * The identity system has already acted by the time this renders — it verified the address and * redirected here, or refused and redirected here with a reason. Nothing on this screen is * authenticated: following a link deliberately does not mint a session (ADR 0038), so the only * offer is to go and sign in. */ export function VerificationResult(props: VerificationResultProps): ReactElement { if (props.outcome.kind === "verified") { return (

This address is confirmed. Sign in to Ekklesia to continue.

); } return (

{props.outcome.expired ? "Verification links last one hour. Sign in with your password and Ekklesia will send a new one." : "This verification link could not be used. Sign in with your password and Ekklesia will send a new one."}

); } export interface ResetLinkInvalidProps { readonly onReturnToSignIn: () => void; } /** Where an expired or malformed password-reset link lands. It offers the recovery form again * rather than a dead end, and says nothing about whether an account exists. */ export function ResetLinkInvalid(props: ResetLinkInvalidProps): ReactElement { return (

Password reset links last one hour and can be used once. Request a new one from the sign-in page.

); }