"use client" import { useEffect, useState } from "react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { completeSetup, plcRequest, plcSubmit, plcRegister } from "@/lib/api" import { docsUrl } from "@/lib/docs" import { HelpTip } from "./help-tip" interface SetupVerifyProps { mode: string onComplete: () => void onBack?: () => void } // ─── did:web ──────────────────────────────────────────────────────────────── function VerifyDidWeb({ onComplete, onBack }: { onComplete: () => void; onBack?: () => void }) { const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [didId, setDidId] = useState(null) const [fetching, setFetching] = useState(true) const [fetchError, setFetchError] = useState(false) useEffect(() => { fetch("/.well-known/did.json") .then((res) => { if (!res.ok) throw new Error() return res.json() }) .then((doc) => { if (doc?.id) setDidId(doc.id) }) .catch(() => setFetchError(true)) .finally(() => setFetching(false)) }, []) const handleConfirm = async () => { setLoading(true) setError(null) try { await completeSetup(); onComplete() } catch (e) { setError(e instanceof Error ? e.message : "Failed to complete setup. Check your backend connection and try again.") } finally { setLoading(false) } } return ( Review your domain identity A signing key has been generated and your identity document is ready. Learn more {fetching ? (

Checking identity document…

) : (
{fetchError && (
Could not load your DID document from /.well-known/did.json. Your identity may not be configured correctly.
)} {didId && (
Identity
{didId}
)}
Document URL
/.well-known/did.json
Signing key
P-256 keypair, encrypted at rest
)}

You can add service entries after setup from the Service Identity settings page.

{error &&

{error}

}
{onBack ? ( ) :
}
) } // ─── attach_account ────────────────────────────────────────────────────────── function VerifyAttachAccount({ onComplete, onBack }: { onComplete: () => void; onBack?: () => void }) { const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [token, setToken] = useState("") const [codeSent, setCodeSent] = useState(false) const [sendingCode, setSendingCode] = useState(false) useEffect(() => { let cancelled = false setSendingCode(true) setError(null) plcRequest() .then(() => { if (!cancelled) setCodeSent(true) }) .catch((e) => { if (!cancelled) setError(e instanceof Error ? e.message : "Failed to send confirmation code. Check your connection and try again.") }) .finally(() => { if (!cancelled) setSendingCode(false) }) return () => { cancelled = true } }, []) const handleSendCode = async () => { setSendingCode(true) setError(null) try { await plcRequest() setCodeSent(true) } catch (e) { setError(e instanceof Error ? e.message : "Failed to send confirmation code. Check your connection and try again.") } finally { setSendingCode(false) } } const handleSubmitToken = async () => { setLoading(true) setError(null) try { await plcSubmit(token) await completeSetup() onComplete() } catch (e) { setError(e instanceof Error ? e.message : "Verification failed. Check the code and try again.") } finally { setLoading(false) } } return ( Enter your confirmation code {codeSent ? "A code has been sent to the email address on this account." : "We'll send a confirmation code to the email address on this account."}{" "} Learn more {!codeSent ? ( ) : (
{ e.preventDefault(); if (token && !loading) handleSubmitToken() }} className="space-y-4">
setToken(e.target.value)} className="mt-1.5" aria-required="true" />
{error &&

{error}

}
{onBack ? ( ) :
}
)} {!codeSent && error &&

{error}

} ) } // ─── did:plc ───────────────────────────────────────────────────────────────── function VerifyDidPlc({ onComplete, onBack }: { onComplete: () => void; onBack?: () => void }) { const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [registering, setRegistering] = useState(true) const [registeredDid, setRegisteredDid] = useState(null) const [regError, setRegError] = useState(null) const [keyDownloaded, setKeyDownloaded] = useState(false) const [downloading, setDownloading] = useState(false) const [downloadError, setDownloadError] = useState(null) useEffect(() => { plcRegister() .then((result) => { setRegisteredDid(result.did) }) .catch((e) => { setRegError(e instanceof Error ? e.message : "Registration failed. Check your backend connection and try again.") }) .finally(() => setRegistering(false)) }, []) const handleDownloadKey = async () => { setDownloading(true) setDownloadError(null) try { const res = await fetch("/api/setup/rotation-key") if (!res.ok) throw new Error("Server returned an error") const blob = await res.blob() const url = URL.createObjectURL(blob) const a = document.createElement("a") a.href = url const disposition = res.headers.get("Content-Disposition") const filenameMatch = disposition?.match(/filename="?([^";\s]+)"?/) a.download = filenameMatch?.[1] ?? "rotation-key.json" document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) setKeyDownloaded(true) } catch { setDownloadError("Failed to download the rotation key. Check your connection and try again.") } finally { setDownloading(false) } } const handleConfirm = async () => { setLoading(true) setError(null) try { await completeSetup(); onComplete() } catch (e) { setError(e instanceof Error ? e.message : "Failed to complete setup. Check your backend connection and try again.") } finally { setLoading(false) } } if (registering) { return ( Registering your identity…

Creating your identity in the AT Protocol directory. This usually takes a few seconds.

) } if (regError) { return ( Registration failed

{regError}

{onBack && ( )}
) } return ( Save your rotation key Your identity is registered. Download the rotation key now — you won't be able to access it again after this step. Learn more
{registeredDid && (
Identity
{registeredDid}
)}
Signing key
P-256 keypair, encrypted at rest
Rotation key
Generated separately — download it below

If you lose this key and this HappyView instance goes down, you won't be able to recover or update your identity.

{downloadError &&

{downloadError}

} {!keyDownloaded ? (

You must download the rotation key before continuing.

) : (

Store this file somewhere safe and offline — a password manager, encrypted USB drive, or secure backup.

)} {error &&

{error}

}
{onBack ? ( ) :
}
) } // ─── Root dispatcher ───────────────────────────────────────────────────────── export function SetupVerify({ mode, onComplete, onBack }: SetupVerifyProps) { if (mode === "did_web") return if (mode === "attach_account") return if (mode === "did_plc") return return null }