RegistryAccount
uses useState
Who's signed in, in the registry's header. Nobody: "Sign in", which opens a dialog asking for their handle (React Aria's Form and TextField, as the forge's sign-in is) and sends the browser to sign in. Someone: their picture, which opens a menu (as the forge's is): who it is, their page, what's theirs in the feed, and signing out. Coming back from signing in, it finishes it.
import { handleOfDid, said, whoHref, whoIs } from "#codebase/tcmts/registry"; import type { Someone } from "#codebase/tcmts/registry"; import { Initial } from "#codebase/tcmts/registry/pages"; import { finishSignIn, registrySession, registrySignOut, startSignIn } from "#codebase/tcmts/registry/session"; import { at } from "#codebase/tcmts/share"; import { useEffect, useState } from "react"; import { Button, Dialog, DialogTrigger, FieldError, Form, Header, Heading, Input, Label, Menu, MenuItem, MenuSection, MenuTrigger, Popover, TextField, } from "react-aria-components"; export function RegistryAccount() { const [state, setState] = useState<{ at: "checking" | "out" | "in"; me?: Someone }>({ at: "checking", }); const [open, setOpen] = useState(false); const [handle, setHandle] = useState(""); const [problem, setProblem] = useState(""); const [busy, setBusy] = useState(false); useEffect(() => { void (async () => { try { if (await finishSignIn()) return; const s = await registrySession(); if (!s) return setState({ at: "out" }); // (their picture and name, if the registry knows them; their handle, from their DID document if not) const me = (await whoIs(s.did).catch(() => undefined)) ?? { did: s.did }; const named = me.handle ?? (await handleOfDid(s.did)); setState({ at: "in", me: named === s.did ? me : { ...me, handle: named } }); } catch (e) { setState({ at: "out" }); setProblem(`Signing in didn't work: ${(e as Error).message}`); setOpen(true); } })(); }, []); if (state.at === "checking") return <span className="account" />; const me = state.me; if (state.at === "in" && me) return ( <span className="account"> <MenuTrigger> <Button className="avatar-btn" aria-label={`Signed in as ${said(me)}`}> <Initial who={me} /> </Button> <Popover className="menu-pop" placement="bottom end" offset={6}> <Menu className="menu" aria-label="Account" onAction={(k) => { if (k === "out") void registrySignOut(); }} > <MenuSection> <Header className="menu-head"> Signed in as <b>{said(me)}</b> </Header> <MenuItem id="profile" href={whoHref(me)} className="opt"> Your page </MenuItem> <MenuItem id="following" href={at("/?show=following")} className="opt"> People I follow </MenuItem> <MenuItem id="mine" href={at("/?show=mine")} className="opt"> Libraries I use </MenuItem> </MenuSection> <MenuSection className="menu-end"> <MenuItem id="out" className="opt signout"> Sign out </MenuItem> </MenuSection> </Menu> </Popover> </MenuTrigger> </span> ); return ( <span className="account"> <DialogTrigger isOpen={open} onOpenChange={setOpen}> <Button className="btn sky">Sign in</Button> <Popover className="menu-pop signin-pop" placement="bottom end" offset={6}> <Dialog className="getit-body"> <Heading slot="title" className="getit-title"> Sign in with your handle </Heading> <p className="muted"> Your Bluesky handle, or any AT Protocol account's. Your own server signs you in; the registry asks only to write your stars. </p> <Form className="signin-form" onSubmit={(e) => { e.preventDefault(); setBusy(true); startSignIn(handle).catch((err: Error) => { setProblem(err.message); setBusy(false); }); }} > <TextField className="textfield" name="handle" autoComplete="username" isRequired value={handle} onChange={(v) => (setHandle(v), setProblem(""))} isInvalid={!!problem} > <Label>Handle</Label> <Input className="field" placeholder="you.bsky.social" /> <FieldError className="field-error">{problem}</FieldError> </TextField> <Button type="submit" className="btn sky" isPending={busy}> {busy ? "Signing in…" : "Continue"} </Button> </Form> </Dialog> </Popover> </DialogTrigger> </span> ); }
uses
- tcmts.registry.Someone
- tcmts.registry.handleOfDid
- tcmts.registry.pages.Initial
- tcmts.registry.said
- tcmts.registry.session.finishSignIn
- tcmts.registry.session.registrySession
- tcmts.registry.session.registrySignOut
- tcmts.registry.session.startSignIn
- tcmts.registry.whoHref
- tcmts.registry.whoIs
- tcmts.share.at