Something went wrong. Try again.
a tool for shared writing and social publishing
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869import { Agent } from "@atproto/api";import { useRef, useState } from "react";import { useDebouncedEffect } from "src/hooks/useDebouncedEffect";
type ActorSuggestion = { handle: string; did: string; displayName?: string; avatar?: string;};
// Shared Bluesky actor typeahead state: a debounced search against the public// AppView with a request-id guard so stale responses are dropped. Each consumer// supplies its own trigger UI and submit handling. `transformQuery` lets a// caller normalize the input (e.g. strip a leading @) before searching.export function useActorTypeahead(opts?: { debounceMs?: number; transformQuery?: (value: string) => string;}) { let [handleValue, setHandleValue] = useState(""); let [suggestions, setSuggestions] = useState<ActorSuggestion[]>([]); let [dropdownOpen, setDropdownOpen] = useState(false); let [highlighted, setHighlighted] = useState<string | undefined>(undefined); let requestIdRef = useRef(0);
useDebouncedEffect( async () => { let query = opts?.transformQuery ? opts.transformQuery(handleValue) : handleValue; if (!query) { setSuggestions([]); setDropdownOpen(false); return; } let requestId = ++requestIdRef.current; let agent = new Agent("https://public.api.bsky.app"); try { let result = await agent.searchActorsTypeahead({ q: query, limit: 8 }); if (requestId !== requestIdRef.current) return; let actors = result.data.actors.map((actor) => ({ handle: actor.handle, did: actor.did, displayName: actor.displayName, avatar: actor.avatar, })); setSuggestions(actors); setDropdownOpen(actors.length > 0); } catch { setSuggestions([]); setDropdownOpen(false); } }, opts?.debounceMs ?? 300, [handleValue], );
return { handleValue, setHandleValue, suggestions, setSuggestions, dropdownOpen, setDropdownOpen, highlighted, setHighlighted, };}