import { Check, Pencil, X } from "lucide-react"; import { useState } from "react"; import { BackLink } from "@/client/components/ui/back-link"; import { Button } from "@/client/components/ui/button"; import { Card } from "@/client/components/ui/card"; import { PageHeader } from "@/client/components/ui/page-header"; import { Skeleton } from "@/client/components/ui/skeleton"; import { useRequireSession } from "@/client/hooks/use-auth-guard"; import { type SessionUser } from "@/client/lib/auth-client"; import { fallbackHandle } from "@/client/lib/handle"; import { apiJsonRequest, responseMessage } from "@/client/lib/http"; interface UsernameEditorProps { initial: string; onSaved: () => void | Promise; } // The page passes `key={currentHandle}` so a successful save (which // updates `currentHandle`) remounts this component, resetting the // editing/pending/error/value state without an effect-driven sync. const UsernameEditor = ({ initial, onSaved }: UsernameEditorProps) => { const [editing, setEditing] = useState(false); const [value, setValue] = useState(initial); const [pending, setPending] = useState(false); const [err, setErr] = useState(null); const cancel = () => { setValue(initial); setErr(null); setEditing(false); }; const save = async () => { setErr(null); setPending(true); try { const res = await apiJsonRequest("/api/account/handle", { preferredUsername: value }, { method: "POST" }); if (!res.ok) { setErr(await responseMessage(res, "Could not update username.")); return; } await onSaved(); setEditing(false); } finally { setPending(false); } }; return (

Username

Your preferred_username claim. Downstream apps may use it as a slug.

{editing && err ?

{err}

: null}
{editing ? ( <> setValue(e.target.value.toLowerCase())} maxLength={32} autoFocus autoCapitalize="off" autoCorrect="off" spellCheck={false} disabled={pending} aria-label="Username" className="h-9 w-48 rounded-lg border border-zinc-700/60 bg-zinc-800/80 px-3 font-mono text-sm text-zinc-100 focus:border-accent-500/50 focus:outline-none focus:ring-1 focus:ring-accent-500/30 disabled:cursor-not-allowed disabled:opacity-60" onKeyDown={(e) => { if (pending) return; if (e.key === "Escape") cancel(); if (e.key === "Enter") void save(); }} /> ) : ( <> {initial} )}
); }; const AccountProfilePageSkeleton = () => (
); export const AccountProfilePage = () => { const session = useRequireSession("/account/profile"); if (session.isPending || !session.data) { return ; } const sessionUser = session.data.user as SessionUser; const currentHandle = sessionUser.preferredUsername ?? fallbackHandle(sessionUser); return (
Account session.refetch()} />
); };