"use client" import { useState } from "react" import { XIcon } from "lucide-react" import { cn } from "@/lib/utils" import { useAuth } from "@/lib/auth-context" import { useHandleTypeahead, type TypeaheadActor, } from "@/hooks/use-handle-typeahead" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Button } from "@/components/ui/button" import { Combobox, ComboboxContent, ComboboxInput, ComboboxItem, ComboboxList, } from "@/components/ui/combobox" import { Field, FieldDescription, FieldGroup, FieldLabel, } from "@/components/ui/field" export function LoginForm({ className, externalError, ...props }: React.ComponentProps<"div"> & { externalError?: string | null }) { const [handle, setHandle] = useState("") const [selectedActor, setSelectedActor] = useState( null, ) const [dropdownOpen, setDropdownOpen] = useState(false) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const { login } = useAuth() const { actors } = useHandleTypeahead(selectedActor ? "" : handle) const actorsByHandle = new Map(actors.map((a) => [a.handle, a])) const showDropdown = dropdownOpen && actors.length > 0 async function handleSubmit(e: React.FormEvent) { e.preventDefault() const value = selectedActor?.handle ?? handle if (!value.trim()) return setLoading(true) setError(null) try { await login(value.trim().toLowerCase()) } catch (e: unknown) { setError(e instanceof Error ? e.message : "Login failed") setLoading(false) } } function clearSelection() { setSelectedActor(null) setHandle("") } return (

HappyView Admin

Sign in with your ATProto account to manage your AppView.
{(externalError || error) && (

{externalError || error}

)} Handle {selectedActor ? (
{selectedActor.avatar && ( )} {( selectedActor.displayName?.[0] ?? selectedActor.handle[0] ).toUpperCase()}
{selectedActor.displayName && ( {selectedActor.displayName} )} @{selectedActor.handle}
) : ( { if (details.reason === "input-change") { setHandle(value) } }} onValueChange={(value) => { if (value) { const actor = actorsByHandle.get(value as string) if (actor) { setSelectedActor(actor) setHandle(actor.handle) } else { setHandle(value as string) } } }} open={showDropdown} onOpenChange={setDropdownOpen} items={actors.map((a) => a.handle)} filter={null} > {(item: string) => { const actor = actorsByHandle.get(item) return ( {actor?.avatar && ( )} {( actor?.displayName?.[0] ?? item[0] ).toUpperCase()}
{actor?.displayName && ( {actor.displayName} )} @{item}
) }}
)}
) }