Something went wrong. Try again.
some bullshit forked from whey.party/red-dwarf
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731import * as ATPAPI from "@atproto/api";import { infiniteQueryOptions, QueryClient, type QueryFunctionContext, queryOptions, useInfiniteQuery, useQueries, useQuery, //useQueryClient, type UseQueryResult,} from "@tanstack/react-query";import { create, windowScheduler } from "@yornaath/batshit";import { useAtom } from "jotai";import { useMemo } from "react";
import { HOST_LABELMERGE } from "~/../policy";import type { Error as LabelMergeQueryLabelsOutputSchemaError, OutputSchema as LabelMergeQueryLabelsOutputSchema, QueryParams as LabelMergeQueryLabelsQueryParams,} from "~/api/labelmerge/types/app/reddwarf/labelmerge/queryLabels";import { useAuth } from "~/providers/UnifiedAuthProvider";
import { constellationURLAtom, lycanURLAtom, slingshotURLAtom } from "./atoms";
export function constructIdentityQuery( didorhandle?: string, slingshoturl?: string,) { return queryOptions({ queryKey: ["identity", didorhandle], queryFn: async () => { if (!didorhandle) return undefined as undefined; const res = await fetch( `https://${slingshoturl}/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${encodeURIComponent(didorhandle)}`, ); if (!res.ok) throw new Error("Failed to fetch post"); try { return (await res.json()) as { did: string; handle: string; pds: string; signing_key: string; }; } catch (_e) { return undefined; } }, staleTime: /*0,//*/ 5 * 60 * 1000, // 5 minutes gcTime: /*0//*/ 5 * 60 * 1000, });}export function useQueryIdentity(didorhandle: string): UseQueryResult< { did: string; handle: string; pds: string; signing_key: string; }, Error>;export function useQueryIdentity(): UseQueryResult<undefined, Error>;export function useQueryIdentity(didorhandle?: string): UseQueryResult< | { did: string; handle: string; pds: string; signing_key: string; } | undefined, Error>;export function useQueryIdentity(didorhandle?: string) { const [slingshoturl] = useAtom(slingshotURLAtom); return useQuery(constructIdentityQuery(didorhandle, slingshoturl));}
export function constructFastAVIdentityQuery( didorhandle?: string, slingshoturl?: string, queryClient?: QueryClient, enabled?: boolean) { return queryOptions({ queryKey: ["identity", didorhandle], queryFn: async () => { try { console.log("whathuh trying", ["savpq", didorhandle]) if (!queryClient) throw "whatever" const datas = queryClient.getQueriesData<SingularAVPostResult | undefined>({ queryKey: ["savpq", didorhandle], }) console.log("whathuh checking", datas) const data = datas[0][1]; if (!data) { throw "whatever" } //const parsedaturi = new ATPAPI.AtUri(data.uri) console.log("whathuh success") return { did: data.author.did, handle: data.author.handle } } catch { console.log("whathuh failure") if (!didorhandle) return undefined as undefined; const res = await fetch( `https://${slingshoturl}/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${encodeURIComponent(didorhandle)}`, ); if (!res.ok) throw new Error("Failed to fetch post"); try { return (await res.json()) as { did: string; handle: string; pds: string; signing_key: string; }; } catch (_e) { return undefined; } } }, enabled, staleTime: /*0,//*/ 5 * 60 * 1000, // 5 minutes gcTime: /*0//*/ 5 * 60 * 1000, });}
export function useQueryFastAVIdentity(didorhandle?: string, slingshoturl?: string, queryClient?: QueryClient, enabled: boolean = true) { return useQuery(constructFastAVIdentityQuery(didorhandle, slingshoturl, queryClient, enabled));}
export function constructPostQuery(uri?: string, slingshoturl?: string) { return queryOptions({ queryKey: ["post", uri], queryFn: async () => { if (!uri) return undefined as undefined; const res = await fetch( `https://${slingshoturl}/xrpc/com.bad-example.repo.getUriRecord?at_uri=${encodeURIComponent(uri)}`, ); let data: any; try { data = await res.json(); } catch { return undefined; } if (res.status === 400) return undefined; if ( data?.error === "InvalidRequest" && data.message?.includes("Could not find repo") ) { return undefined; // cache “not found” } try { if (!res.ok) throw new Error("Failed to fetch post"); return data as { uri: string; cid: string; value: any; }; } catch (_e) { return undefined; } }, retry: (failureCount, error) => { // dont retry 400 errors if ((error as any)?.message?.includes("400")) return false; return failureCount < 2; }, staleTime: /*0,//*/ 5 * 60 * 1000, // 5 minutes gcTime: /*0//*/ 5 * 60 * 1000, });}export function useQueryPost(uri: string): UseQueryResult< { uri: string; cid: string; value: ATPAPI.AppBskyFeedPost.Record; }, Error>;export function useQueryPost(): UseQueryResult<undefined, Error>;export function useQueryPost(uri?: string): UseQueryResult< | { uri: string; cid: string; value: ATPAPI.AppBskyFeedPost.Record; } | undefined, Error>;export function useQueryPost(uri?: string) { const [slingshoturl] = useAtom(slingshotURLAtom); return useQuery(constructPostQuery(uri, slingshoturl));}
export function constructProfileQuery(uri?: string, slingshoturl?: string) { return queryOptions({ queryKey: ["profile", uri], queryFn: async () => { if (!uri) return undefined as undefined; const res = await fetch( `https://${slingshoturl}/xrpc/com.bad-example.repo.getUriRecord?at_uri=${encodeURIComponent(uri)}`, ); let data: any; try { data = await res.json(); } catch { return undefined; } if (res.status === 400) return undefined; if ( data?.error === "InvalidRequest" && data.message?.includes("Could not find repo") ) { return undefined; // cache “not found” } try { if (!res.ok) throw new Error("Failed to fetch post"); return data as { uri: string; cid: string; value: any; }; } catch (_e) { return undefined; } }, retry: (failureCount, error) => { // dont retry 400 errors if ((error as any)?.message?.includes("400")) return false; return failureCount < 2; }, staleTime: /*0,//*/ 5 * 60 * 1000, // 5 minutes gcTime: /*0//*/ 5 * 60 * 1000, });}export function useQueryProfile(uri: string): UseQueryResult< { uri: string; cid: string; value: ATPAPI.AppBskyActorProfile.Record; }, Error>;export function useQueryProfile(): UseQueryResult<undefined, Error>;export function useQueryProfile(uri?: string): UseQueryResult< | { uri: string; cid: string; value: ATPAPI.AppBskyActorProfile.Record; } | undefined, Error>;export function useQueryProfile(uri?: string) { const [slingshoturl] = useAtom(slingshotURLAtom); return useQuery(constructProfileQuery(uri, slingshoturl));}
// export function constructConstellationQuery(// method: "/links",// target: string,// collection: string,// path: string,// cursor?: string// ): QueryOptions<linksRecordsResponse, Error>;// export function constructConstellationQuery(// method: "/links/distinct-dids",// target: string,// collection: string,// path: string,// cursor?: string// ): QueryOptions<linksDidsResponse, Error>;// export function constructConstellationQuery(// method: "/links/count",// target: string,// collection: string,// path: string,// cursor?: string// ): QueryOptions<linksCountResponse, Error>;// export function constructConstellationQuery(// method: "/links/count/distinct-dids",// target: string,// collection: string,// path: string,// cursor?: string// ): QueryOptions<linksCountResponse, Error>;// export function constructConstellationQuery(// method: "/links/all",// target: string// ): QueryOptions<linksAllResponse, Error>;export function constructConstellationQuery(query?: { constellation: string; method: | "/links" | "/links/distinct-dids" | "/links/count" | "/links/count/distinct-dids" | "/links/all" | "undefined"; target: string; collection?: string; path?: string; cursor?: string; dids?: string[]; customkey?: string; enabled?: boolean;}) { // : QueryOptions< // | linksRecordsResponse // | linksDidsResponse // | linksCountResponse // | linksAllResponse // | undefined, // Error // > return queryOptions({ enabled: query?.enabled, queryKey: [ "constellation", query?.method, query?.target, query?.collection, query?.path, query?.cursor, query?.dids, query?.customkey, ] as const, queryFn: async () => { if (!query || query.method === "undefined") return undefined as undefined; const method = query.method; const target = query.target; const collection = query?.collection; const path = query?.path; const cursor = query.cursor; const dids = query?.dids; const res = await fetch( `https://${query.constellation}${method}?target=${encodeURIComponent(target)}${collection ? `&collection=${encodeURIComponent(collection)}` : ""}${path ? `&path=${encodeURIComponent(path)}` : ""}${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}${dids ? dids.map((did) => `&did=${encodeURIComponent(did)}`).join("") : ""}`, ); if (!res.ok) throw new Error("Failed to fetch post"); try { switch (method) { case "/links": return (await res.json()) as linksRecordsResponse; case "/links/distinct-dids": return (await res.json()) as linksDidsResponse; case "/links/count": return (await res.json()) as linksCountResponse; case "/links/count/distinct-dids": return (await res.json()) as linksCountResponse; case "/links/all": return (await res.json()) as linksAllResponse; default: return undefined; } } catch (_e) { return undefined; } }, // enforce short lifespan staleTime: /*0,//*/ 5 * 60 * 1000, // 5 minutes gcTime: /*0//*/ 5 * 60 * 1000, });}// todo do more of these instead of overloads since overloads sucks so much apparentlyexport function useQueryConstellationLinksCountDistinctDids(query?: { method: "/links/count/distinct-dids"; target: string; collection: string; path: string; cursor?: string;}): UseQueryResult<linksCountResponse, Error> | undefined { //if (!query) return; const [constellationurl] = useAtom(constellationURLAtom); const queryres = useQuery( constructConstellationQuery( query && { constellation: constellationurl, ...query }, ), ) as unknown as UseQueryResult<linksCountResponse, Error>; if (!query) { return undefined as undefined; } return queryres as UseQueryResult<linksCountResponse, Error>;}
export function useQueryConstellation(query: { method: "/links"; target: string; collection: string; path: string; cursor?: string; dids?: string[]; customkey?: string; enabled?: boolean;}): UseQueryResult<linksRecordsResponse, Error>;export function useQueryConstellation(query: { method: "/links/distinct-dids"; target: string; collection: string; path: string; cursor?: string; customkey?: string; enabled?: boolean;}): UseQueryResult<linksDidsResponse, Error>;export function useQueryConstellation(query: { method: "/links/count"; target: string; collection: string; path: string; cursor?: string; customkey?: string; enabled?: boolean;}): UseQueryResult<linksCountResponse, Error>;export function useQueryConstellation(query: { method: "/links/count/distinct-dids"; target: string; collection: string; path: string; cursor?: string; customkey?: string; enabled?: boolean;}): UseQueryResult<linksCountResponse, Error>;export function useQueryConstellation(query: { method: "/links/all"; target: string; customkey?: string; enabled?: boolean;}): UseQueryResult<linksAllResponse, Error>;export function useQueryConstellation(): undefined;export function useQueryConstellation(query: { method: "undefined"; target: string; customkey?: string; enabled?: boolean;}): undefined;export function useQueryConstellation(query?: { method: | "/links" | "/links/distinct-dids" | "/links/count" | "/links/count/distinct-dids" | "/links/all" | "undefined"; target: string; collection?: string; path?: string; cursor?: string; dids?: string[]; customkey?: string; enabled?: boolean;}): | UseQueryResult< | linksRecordsResponse | linksDidsResponse | linksCountResponse | linksAllResponse | undefined, Error > | undefined { //if (!query) return; const [constellationurl] = useAtom(constellationURLAtom); const res = useQuery( constructConstellationQuery( query && { constellation: constellationurl, ...query }, ), ); return res}
export type linksRecord = { did: string; collection: string; rkey: string;};export type linksRecordsResponse = { total: string; linking_records: linksRecord[]; cursor?: string;};type linksDidsResponse = { total: string; linking_dids: string[]; cursor?: string;};type linksCountResponse = { total: string;};export type linksAllResponse = { links: Record< string, Record< string, { records: number; distinct_dids: number; } > >;};
export function constructFeedSkeletonQuery(options?: { feedUri: string; agent?: ATPAPI.Agent; isAuthed: boolean; pdsUrl?: string; feedServiceDid?: string;}) { return queryOptions({ // The query key includes all dependencies to ensure it refetches when they change queryKey: [ "feedSkeleton", options?.feedUri, { isAuthed: options?.isAuthed, did: options?.agent?.did }, ], queryFn: async () => { if (!options) return undefined as undefined; const { feedUri, agent, isAuthed, pdsUrl, feedServiceDid } = options; if (isAuthed) { // Authenticated flow if (!agent || !pdsUrl || !feedServiceDid) { throw new Error( "Missing required info for authenticated feed fetch.", ); } const url = `${pdsUrl}/xrpc/app.bsky.feed.getFeedSkeleton?feed=${encodeURIComponent(feedUri)}`; const res = await agent.fetchHandler(url, { method: "GET", headers: { "atproto-proxy": `${feedServiceDid}#bsky_fg`, "Content-Type": "application/json", }, }); if (!res.ok) throw new Error(`Authenticated feed fetch failed: ${res.statusText}`); return (await res.json()) as ATPAPI.AppBskyFeedGetFeedSkeleton.OutputSchema; } else { // Unauthenticated flow (using a public PDS/AppView) const url = `https://discover.bsky.app/xrpc/app.bsky.feed.getFeedSkeleton?feed=${encodeURIComponent(feedUri)}`; const res = await fetch(url); if (!res.ok) throw new Error(`Public feed fetch failed: ${res.statusText}`); return (await res.json()) as ATPAPI.AppBskyFeedGetFeedSkeleton.OutputSchema; } }, //enabled: !!feedUri && (isAuthed ? !!agent && !!pdsUrl && !!feedServiceDid : true), });}
export function useQueryFeedSkeleton(options?: { feedUri: string; agent?: ATPAPI.Agent; isAuthed: boolean; pdsUrl?: string; feedServiceDid?: string;}) { return useQuery(constructFeedSkeletonQuery(options));}
export function constructRecordQuery( did?: string, collection?: string, rkey?: string, pdsUrl?: string,) { return queryOptions({ queryKey: ["record", did, collection, rkey], queryFn: async () => { if (!did || !collection || !rkey || !pdsUrl) return undefined as undefined; const url = `${pdsUrl}/xrpc/com.atproto.repo.getRecord?repo=${encodeURIComponent(did)}&collection=${encodeURIComponent(collection)}&rkey=${encodeURIComponent(rkey)}`; const res = await fetch(url); if (!res.ok) throw new Error("Failed to fetch record"); try { return (await res.json()) as { uri: string; cid: string; value: any; }; } catch (_e) { return undefined; } }, staleTime: 5 * 60 * 1000, // 5 minutes gcTime: 5 * 60 * 1000, });}
export function useQueryRecord( did?: string, collection?: string, rkey?: string, pdsUrl?: string,) { return useQuery(constructRecordQuery(did, collection, rkey, pdsUrl));}
export function constructPreferencesQuery( agent?: ATPAPI.Agent | undefined, pdsUrl?: string | undefined,) { return queryOptions({ queryKey: ["preferences", agent?.did], queryFn: async () => { if (!agent || !pdsUrl) throw new Error("Agent or PDS URL not available"); const url = `${pdsUrl}/xrpc/app.bsky.actor.getPreferences`; const res = await agent.fetchHandler(url, { method: "GET" }); if (!res.ok) throw new Error("Failed to fetch preferences"); // todo, i just gave it real types (atproto api types) so theres gonna be a bunch of errors so pls fix thx return (await res.json()) as ATPAPI.AppBskyActorGetPreferences.OutputSchema; }, });}export function useQueryPreferences(options: { agent?: ATPAPI.Agent | undefined; pdsUrl?: string | undefined;}) { return useQuery(constructPreferencesQuery(options.agent, options.pdsUrl));}
export function constructArbitraryQuery(uri?: string, slingshoturl?: string) { return queryOptions({ queryKey: ["arbitrary", uri], queryFn: async () => { if (!uri) return undefined as undefined; const res = await fetch( `https://${slingshoturl}/xrpc/com.bad-example.repo.getUriRecord?at_uri=${encodeURIComponent(uri)}`, ); let data: any; try { data = await res.json(); } catch { return undefined; } if (res.status === 400) return undefined; if ( data?.error === "InvalidRequest" && data.message?.includes("Could not find repo") ) { return undefined; // cache “not found” } try { if (!res.ok) throw new Error("Failed to fetch post"); return data as { uri: string; cid: string; value: any; }; } catch (_e) { return undefined; } }, retry: (failureCount, error) => { // dont retry 400 errors if ((error as any)?.message?.includes("400")) return false; return failureCount < 2; }, staleTime: /*0,//*/ 5 * 60 * 1000, // 5 minutes gcTime: /*0//*/ 5 * 60 * 1000, });}export function useQueryArbitrary(uri: string): UseQueryResult< { uri: string; cid: string; value: any; }, Error>;export function useQueryArbitrary(): UseQueryResult<undefined, Error>;export function useQueryArbitrary(uri?: string): UseQueryResult< | { uri: string; cid: string; value: any; } | undefined, Error>;export function useQueryArbitrary(uri?: string) { const [slingshoturl] = useAtom(slingshotURLAtom); return useQuery(constructArbitraryQuery(uri, slingshoturl));}
export function constructFallbackNothingQuery() { return queryOptions({ queryKey: ["nothing"], queryFn: async () => { return undefined; }, });}
type ListRecordsResponse = { cursor?: string; records: { uri: string; cid: string; value: ATPAPI.AppBskyFeedPost.Record; }[];};
export function constructAuthorFeedQuery( did: string, pdsUrl: string, collection: string = "app.bsky.feed.post",) { return queryOptions({ queryKey: ["authorFeed", did, collection], queryFn: async ({ pageParam }: QueryFunctionContext) => { const limit = 25;
const cursor = pageParam as string | undefined; const cursorParam = cursor ? `&cursor=${cursor}` : "";
const url = `${pdsUrl}/xrpc/com.atproto.repo.listRecords?repo=${did}&collection=${collection}&limit=${limit}${cursorParam}`;
const res = await fetch(url); if (!res.ok) throw new Error("Failed to fetch author's posts");
return res.json() as Promise<ListRecordsResponse>; }, });}
export function useInfiniteQueryAuthorFeed( did: string | undefined, pdsUrl: string | undefined, collection?: string,) { const { queryKey, queryFn } = constructAuthorFeedQuery( did!, pdsUrl!, collection, );
return useInfiniteQuery({ queryKey, queryFn, initialPageParam: undefined as never, // ???? what is this shit getNextPageParam: (lastPage) => lastPage.cursor as null | undefined, enabled: !!did && !!pdsUrl, });}
type FeedSkeletonPage = ATPAPI.AppBskyFeedGetFeedSkeleton.OutputSchema;
export function constructInfiniteFeedSkeletonQuery(options: { feedUri: string; agent?: ATPAPI.Agent; isAuthed: boolean; pdsUrl?: string; feedServiceDid?: string; // todo the hell is a unauthedfeedurl unauthedfeedurl?: string;}) { const { feedUri, agent, isAuthed, pdsUrl, feedServiceDid, unauthedfeedurl } = options;
return queryOptions({ queryKey: ["feedSkeleton", feedUri, { isAuthed, did: agent?.did }],
queryFn: async ({ pageParam, }: QueryFunctionContext): Promise<FeedSkeletonPage> => { const cursorParam = pageParam ? `&cursor=${pageParam}` : "";
if (isAuthed && !unauthedfeedurl) { if (!agent || !pdsUrl || !feedServiceDid) { throw new Error( "Missing required info for authenticated feed fetch.", ); } const url = `${pdsUrl}/xrpc/app.bsky.feed.getFeedSkeleton?feed=${encodeURIComponent(feedUri)}${cursorParam}`; const res = await agent.fetchHandler(url, { method: "GET", headers: { "atproto-proxy": `${feedServiceDid}#bsky_fg`, "Content-Type": "application/json", }, }); if (!res.ok) throw new Error(`Authenticated feed fetch failed: ${res.statusText}`); return (await res.json()) as FeedSkeletonPage; } else { const url = `https://${unauthedfeedurl ? unauthedfeedurl : "discover.bsky.app"}/xrpc/app.bsky.feed.getFeedSkeleton?feed=${encodeURIComponent(feedUri)}${cursorParam}`; const res = await fetch(url); if (!res.ok) throw new Error(`Public feed fetch failed: ${res.statusText}`); return (await res.json()) as FeedSkeletonPage; } }, });}
export function useInfiniteQueryFeedSkeleton(options: { feedUri: string; agent?: ATPAPI.Agent; isAuthed: boolean; pdsUrl?: string; feedServiceDid?: string; unauthedfeedurl?: string;}) { const { queryKey, queryFn } = constructInfiniteFeedSkeletonQuery(options);
return { ...useInfiniteQuery({ queryKey, queryFn, initialPageParam: undefined as never, getNextPageParam: (lastPage) => lastPage.cursor as null | undefined, staleTime: Infinity, refetchOnWindowFocus: false, enabled: !!options.feedUri && (options.isAuthed ? ((!!options.agent && !!options.pdsUrl) || !!options.unauthedfeedurl) && !!options.feedServiceDid : true), }), queryKey: queryKey, };}
export function yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks(query?: { constellation: string; method: "/links"; target?: string; collection: string; path: string; staleMult?: number;}) { const safemult = query?.staleMult ?? 1; // console.log( // 'yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks', // query, // )
return infiniteQueryOptions({ enabled: !!query?.target, queryKey: [ "reddwarf_constellation", query?.method, query?.target, query?.collection, query?.path, ] as const,
queryFn: async ({ pageParam }: { pageParam?: string }) => { if (!query || !query?.target) return undefined;
const method = query.method; const target = query.target; const collection = query.collection; const path = query.path; const cursor = pageParam;
const res = await fetch( `https://${query.constellation}${method}?target=${encodeURIComponent(target)}${ collection ? `&collection=${encodeURIComponent(collection)}` : "" }${path ? `&path=${encodeURIComponent(path)}` : ""}${ cursor ? `&cursor=${encodeURIComponent(cursor)}` : "" }`, );
if (!res.ok) throw new Error("Failed to fetch");
return (await res.json()) as linksRecordsResponse; },
getNextPageParam: (lastPage) => { return (lastPage as any)?.cursor ?? undefined; }, initialPageParam: undefined, staleTime: 5 * 60 * 1000 * safemult, gcTime: 5 * 60 * 1000 * safemult, });}
export function useQueryLycanStatus() { const [lycanurl] = useAtom(lycanURLAtom); const { agent, status } = useAuth(); const { data: identity } = useQueryIdentity(agent?.did); return useQuery( constructLycanStatusCheckQuery({ agent: agent || undefined, isAuthed: status === "signedIn", pdsUrl: identity?.pds, feedServiceDid: "did:web:" + lycanurl, }), );}
export function constructLycanStatusCheckQuery(options: { agent?: ATPAPI.Agent; isAuthed: boolean; pdsUrl?: string; feedServiceDid?: string;}) { const { agent, isAuthed, pdsUrl, feedServiceDid } = options;
return queryOptions({ queryKey: ["lycanStatus", { isAuthed, did: agent?.did }],
queryFn: async () => { if (isAuthed && agent && pdsUrl && feedServiceDid) { const url = `${pdsUrl}/xrpc/blue.feeds.lycan.getImportStatus`; const res = await agent.fetchHandler(url, { method: "GET", headers: { "atproto-proxy": `${feedServiceDid}#lycan`, "Content-Type": "application/json", }, }); if (!res.ok) throw new Error( `Authenticated lycan status fetch failed: ${res.statusText}`, ); return (await res.json()) as statuschek; } return undefined; }, });}
type statuschek = { [key: string]: unknown; error?: "MethodNotImplemented"; message?: "Method Not Implemented"; status?: "finished" | "in_progress"; position?: string; progress?: number;};
//{"status":"in_progress","position":"2025-08-30T06:53:18Z","progress":0.0878319661441268}type importtype = { message?: "Import has already started" | "Import has been scheduled";};
export function constructLycanRequestIndexQuery(options: { agent?: ATPAPI.Agent; isAuthed: boolean; pdsUrl?: string; feedServiceDid?: string;}) { const { agent, isAuthed, pdsUrl, feedServiceDid } = options;
return queryOptions({ queryKey: ["lycanIndex", { isAuthed, did: agent?.did }],
queryFn: async () => { if (isAuthed && agent && pdsUrl && feedServiceDid) { const url = `${pdsUrl}/xrpc/blue.feeds.lycan.startImport`; const res = await agent.fetchHandler(url, { method: "POST", headers: { "atproto-proxy": `${feedServiceDid}#lycan`, "Content-Type": "application/json", }, }); if (!res.ok) throw new Error( `Authenticated lycan status fetch failed: ${res.statusText}`, ); return (await res.json()) as importtype; } return undefined; }, });}
type LycanSearchPage = { terms: string[]; posts: string[]; cursor?: string;};
export function useInfiniteQueryLycanSearch(options: { query: string; type: "likes" | "pins" | "reposts" | "quotes";}) { const [lycanurl] = useAtom(lycanURLAtom); const { agent, status } = useAuth(); const { data: identity } = useQueryIdentity(agent?.did);
const { queryKey, queryFn } = constructLycanSearchQuery({ agent: agent || undefined, isAuthed: status === "signedIn", pdsUrl: identity?.pds, feedServiceDid: "did:web:" + lycanurl, query: options.query, type: options.type, });
return { ...useInfiniteQuery({ queryKey, queryFn, initialPageParam: undefined as never, getNextPageParam: (lastPage) => lastPage?.cursor as null | undefined, //staleTime: Infinity, refetchOnWindowFocus: false, // enabled: // !!options.feedUri && // (options.isAuthed // ? ((!!options.agent && !!options.pdsUrl) || // !!options.unauthedfeedurl) && // !!options.feedServiceDid // : true), }), queryKey: queryKey, };}
export function constructLycanSearchQuery(options: { agent?: ATPAPI.Agent; isAuthed: boolean; pdsUrl?: string; feedServiceDid?: string; type: "likes" | "pins" | "reposts" | "quotes"; query: string;}) { const { agent, isAuthed, pdsUrl, feedServiceDid, type, query } = options;
return infiniteQueryOptions({ queryKey: ["lycanSearch", query, type, { isAuthed, did: agent?.did }],
queryFn: async ({ pageParam, }: QueryFunctionContext): Promise<LycanSearchPage | undefined> => { if (isAuthed && agent && pdsUrl && feedServiceDid) { const url = `${pdsUrl}/xrpc/blue.feeds.lycan.searchPosts?query=${query}&collection=${type}${pageParam ? `&cursor=${pageParam}` : ""}`; const res = await agent.fetchHandler(url, { method: "GET", headers: { "atproto-proxy": `${feedServiceDid}#lycan`, "Content-Type": "application/json", }, }); if (!res.ok) throw new Error( `Authenticated lycan status fetch failed: ${res.statusText}`, ); return (await res.json()) as LycanSearchPage; } return undefined; }, initialPageParam: undefined as never, getNextPageParam: (lastPage) => lastPage?.cursor as null | undefined, });}
// HOST_LABELMERGE
export async function innerLabelMergeQueryFn(options: LabelMergeQueryLabelsQueryParams): Promise<LabelMergeQueryLabelsOutputSchema | undefined> { const { s, l, strict } = options; const params = new URLSearchParams(); s.forEach((v) => params.append("s", v)); l.forEach((v) => params.append("l", v)); if (strict) { params.append("strict", "true"); } const qs = params.toString();
const url = `${HOST_LABELMERGE}/xrpc/app.reddwarf.labelmerge.queryLabels?` + qs; console.log("LabelMerge URL", url); const res = await fetch(url); if (!res.ok) throw new Error(`Labelmerge fetch failed: ${res.statusText}`); return (await res.json()) as LabelMergeQueryLabelsOutputSchema;}
export function constructLabelMergeQuery( options: LabelMergeQueryLabelsQueryParams,) { const { s, l, strict } = options;
return queryOptions({ queryKey: [ "LabelMergeQueryLabelsQuery", [...s].sort().join(","), [...l].sort().join(","), strict, ],
enabled: Array.isArray(s) && s.length > 0 && Array.isArray(l) && l.length > 0,
queryFn: ()=>innerLabelMergeQueryFn(options), });}export function useQueryLabelMerge(options: LabelMergeQueryLabelsQueryParams) { return useQuery(constructLabelMergeQuery(options));}
export type PartialLabelQuery = { s: string; l: string[];};export type SingularLabelQuery = { s: string; l: string;};
export type SingularLabelResult = { labels?: ATPAPI.ComAtprotoLabelDefs.Label; error?: LabelMergeQueryLabelsOutputSchemaError;}; //ATPAPI.ComAtprotoLabelDefs.Label | LabelMergeQueryLabelsOutputSchemaError | nullexport type PartialLabelResult = { subject: string; labels?: ATPAPI.ComAtprotoLabelDefs.Label[]; error?: LabelMergeQueryLabelsOutputSchemaError[];};
function flattenLabelQueries( partials: PartialLabelQuery[],): SingularLabelQuery[] { return partials.flatMap((p) => p.l.map((label) => ({ s: p.s, l: label })));}
// batShitQueryClientexport const unpersistedQueryClient = new QueryClient(/*{ defaultOptions: { queries: { staleTime: 5 * 60 * 1000, // 5 minutes //cacheTime: 10 * 60 * 1000, // 10 minutes gcTime: 5 * 60 * 1000, }, },}*/);
interface MicroSingleResult { l: string, t: number,}
const labelmerge = create( /*<Record<String,SingularLabelResult>[], SingularLabelQuery>*/ { // The fetcher resolves the list of queries(here just a list of user ids as number) to one single api call. fetcher: async (slqa: SingularLabelQuery[]) => { // Use a shared QueryClient if possible; creating a new one per fetch is usually not needed
// Deduplicate, but don’t sort const sarr = Array.from(new Set(slqa.map((slq) => slq.s))); const larr = Array.from(new Set(slqa.map((slq) => slq.l)));
//const result = await batShitQueryClient.fetchQuery( // constructLabelMergeQuery({ s: sarr, l: larr }), //); const result = await innerLabelMergeQueryFn({s:sarr, l: larr}) //const qfn = constructLabelMergeQuery({ s: sarr, l: larr }).queryFn //const result = await (qfn ? qfn() : ()=>{}) if (!result) return [];
// Build maps for quick lookup const errmap = new Map<string, LabelMergeQueryLabelsOutputSchemaError>(); const resmap = new Map<string, ATPAPI.ComAtprotoLabelDefs.Label>();
result.error?.forEach((err) => errmap.set(err.s, err)); result.labels?.forEach((label) => resmap.set(`${label.src}::${label.uri}`, label));
// Map back to the original queries const output: Record<string, SingularLabelResult>[] = slqa.map((slq) => { const key = `${slq.l}::${slq.s}`; // or just slq.l if you prefer
const err = errmap.get(slq.l); const label = resmap.get(key);
if (err) return { [key]: { error: err } }; if (label) return { [key]: { labels: label } };
// if result is neither, it means the subject is free of labels return { [key]: { labels: undefined} }; // idiot // return { // [key]: { error: { // s: slq.l, // e: `!internal-bslm-unknown: ${slq.s}` // }} // }; });
return output; }, // when we call users.fetch, this will resolve the correct user using the field `id` resolver: (rslra, slq) => { if (rslra.length < 1) { return undefined; } // const result: SingularLabelResult | undefined = slra.find((slr, i) => { // // find if error first // const error = slr.error; // const label = slr.labels; // if (error) { // if (slq.l === error.s) { // return slq; // } // } else if (label) { // // if not error // if (slq.l === label.src && slq.s === label.uri) { // return slq; // } // // else unhandled not found // } else { // return undefined; // } // return undefined; // }); const outputMap: Record<string, SingularLabelResult> = Object.assign({}, ...rslra) const key = `${slq.l}::${slq.s}`; // or just slq.l if you prefer const result: SingularLabelResult | undefined = outputMap[key] return result; }, scheduler: windowScheduler(10 * 100), // 1 second },);
// const labelmergepartial = create/*<PartialLabelResult[], PartialLabelQuery>*/({// // The fetcher resolves the list of queries(here just a list of user ids as number) to one single api call.// fetcher: async (plqa: PartialLabelQuery[]) => {// const singulars = flattenLabelQueries(plqa); // SingularLabelQuery[]// const singularResults = await Promise.all(singulars.map(q => labelmerge.fetch(q)));
// // Now we need to **group singularResults back by the original PartialLabelQuery.s**// // so that each PartialLabelQuery gets a PartialLabelResult (LabelMergeQueryLabelsOutputSchema)// const grouped: Record<string, SingularLabelResult[]> = {};// singulars.forEach((q, i) => {// if (!grouped[q.s]) grouped[q.s] = [];// if (singularResults[i]) {// grouped[q.s].push(singularResults[i]);// } else {// grouped[q.s].push({});// }// });
// // Convert grouped record to your PartialLabelResult format// const result: PartialLabelResult[] = Object.entries(grouped).map(([s, labels]) => {// const cleanLabels = labels// .map(l => l?.labels)// .filter((l): l is ATPAPI.ComAtprotoLabelDefs.Label => !!l?.val)// const cleanErrors = labels// .map(l => l?.error)// .filter((e): e is LabelMergeQueryLabelsOutputSchemaError => !!e?.s)// return {// subject: s,// labels: cleanLabels,// error: cleanErrors ? cleanErrors : undefined// }// });// return result// },// resolver: (plra, plq) => {// if (plra.length < 1) {// return undefined// }// const subject = plq.s;// const result: PartialLabelResult | undefined = plra.find((plr,i)=>{// return plr.subject === subject;// })// return result// },// // this will batch all calls to users.fetch that are made within 10 milliseconds.// scheduler: windowScheduler(10*100) // 1 second// })
// export const useQueryLabel = (s: string, la: string[]) => {// return useQuery({// queryKey: ["useQueryLabel (single) sla", s, la],// queryFn: async () => {// return labelmergepartial.fetch({ s, l: la })// },// })// }
/** * todo: * - [x] switch from useQuery to normal custom hook and switch from Promise.All to useQueries * - [ ] Move neg normalization to the batshit unmerging, and make the cache labels only (pre sorted) * - [ ] Also do signature verification on the constructSingularQuery */
// also the cache hits from constructSingularLabelQuery is not being sent fast because// it waits for all of them first// but also if we send it fast would it cause even worse synchronous traffic jams downstream ?// export const useQueryLabels = (subjects: string[], labelers: string[]) => {// const queryClient = useQueryClient();// return useQuery({// queryKey: ["useQueryLabelFull", subjects, labelers],// queryFn: async (): Promise<LabelMergeQueryLabelsOutputSchema> => {// // Build all singular queries// const singulars: SingularLabelQuery[] = subjects.flatMap((s) =>// labelers.map((l) => ({ s, l })),// );
// // Fetch all results in parallel// const results = await Promise.all(// // singulars.map((q) =>// // queryClient.fetchQuery(constructSingularLabelQuery(q)),// // ),// singulars.map((q) =>// queryClient.fetchQuery(constructSingularLabelQuery(q)).catch((e: Error)=>{// return {// error: {// s: q.l,// e: e.message.toString(),// }// } as SingularLabelResult// }),// ),// // singulars.map(q => queryClient.fetchQuery(constructSingularLabelQuery(q)).catch((err:SingularLabelResult)=>{// // return err// // }))// //singulars.map(q => labelmerge.fetch(q).catch(err => ({ error: err } as SingularLabelResult)))// );
// const labels = Array.from(// new Map(// results// .map(r => r?.labels)// .filter((l): l is ATPAPI.ComAtprotoLabelDefs.Label => !!l?.src)// .map(l => [`${l.src}::${l.uri}`, l])// ).values()// );// const errors = Array.from(// new Map(// results// .map(r => r?.error)// .filter(// (e): e is LabelMergeQueryLabelsOutputSchemaError =>// !!e && typeof e.s === "string"// )// .map(e => [`${e.s}::${e.e ?? ""}`, e])// ).values()// );
// const result: LabelMergeQueryLabelsOutputSchema = {// labels: labels,// error: errors.length < 1 ? undefined : errors,// };
// return result;// },// });// };
function buildSingularQueries(subjects: string[], labelers: string[]) { return subjects.flatMap((s) => labelers.map((l) => ({ s, l, })), );}
export function useQueryLabels(subjects: string[], labelers: string[]) { const singulars = useMemo( () => buildSingularQueries(subjects, labelers), [subjects, labelers], );
const queries = useQueries({ queries: singulars.map((q) => constructSingularLabelQuery(q), ), });
// derive merged state synchronously const labels = useMemo(() => { return Array.from( new Map( queries .map((q) => q.data?.labels) .filter( (l): l is ATPAPI.ComAtprotoLabelDefs.Label => !!l?.src, ) .map((l) => [`${l.src}::${l.uri}`, l]), ).values(), ); }, [queries]);
const errors = useMemo(() => { return Array.from( new Map( queries .map((q) => q.data?.error) .flat() .filter( (e): e is LabelMergeQueryLabelsOutputSchemaError => !!e && typeof e.s === "string", ) .map((e) => [`${e.s}::${e.e ?? ""}`, e]), ).values(), ); }, [queries]);
const isLoading = queries.some((q) => q.isLoading); const isError = queries.some((q) => q.isError); const isFetching = queries.some((q) => q.isFetching);
return { data: { labels, error: errors.length ? errors : undefined, }, isLoading, isError, isFetching, };}
export function constructSingularLabelQuery(options: SingularLabelQuery) { const { s, l } = options;
return queryOptions({ queryKey: ["__volatile","slq", s, l],
enabled: !!s && !!l,
queryFn: async (): Promise<SingularLabelResult | undefined> => { // const result = (await labelmerge.fetch(options).catch(err => {throw { error: err } as SingularLabelResult})) as SingularLabelResult // if (result.error) { // throw result.error // } // return result; const result = (await labelmerge .fetch(options) .catch( (err) => ({ error: err }) as SingularLabelResult, )) as SingularLabelResult; if (result === undefined) { throw new Error("what the hell happened") } return result; },
staleTime: 5 * 60 * 1000, // 5 minutes gcTime: 5 * 60 * 1000, });}export function useQuerySingularLabelQuery(options: SingularLabelQuery) { return useQuery(constructSingularLabelQuery(options));}
type SingularAVPostQuery = { aturi: string, avurl: string, instantBypass?: boolean,}type SingularAVPostResult = ATPAPI.AppBskyFeedDefs.PostView
type AVPostQueryPostsQueryParams = { aturis: string[], avurl: string,}
const MAX_URIS_PER_REQUEST = 25;function chunk<T>(arr: T[], size: number): T[][] { const result: T[][] = []; for (let i = 0; i < arr.length; i += size) { result.push(arr.slice(i, i + size)); } return result;}
export async function innerAVPostsQueryFn( options: AVPostQueryPostsQueryParams): Promise<ATPAPI.AppBskyFeedGetPosts.OutputSchema | undefined> { const { aturis, avurl } = options;
if (!aturis?.length) return undefined;
const batches = chunk(aturis, MAX_URIS_PER_REQUEST);
const responses = await Promise.all( batches.map(async (batch) => { const params = new URLSearchParams(); batch.forEach((uri) => params.append("uris", uri));
const url = `${avurl}/xrpc/app.bsky.feed.getPosts?${params.toString()}`;
const res = await fetch(url); if (!res.ok) { throw new Error(`Labelmerge fetch failed: ${res.status} ${res.statusText}`); }
return (await res.json()) as ATPAPI.AppBskyFeedGetPosts.OutputSchema; }) );
// Merge all posts into one response const merged: ATPAPI.AppBskyFeedGetPosts.OutputSchema = { posts: responses.flatMap((r) => r.posts ?? []), };
return merged;}
const postquerymerge = create( /*<Record<String,SingularLabelResult>[], SingularLabelQuery>*/ { // The fetcher resolves the list of queries(here just a list of user ids as number) to one single api call. fetcher: async (savpqa: SingularAVPostQuery[]) => { // Use a shared QueryClient if possible; creating a new one per fetch is usually not needed
// Deduplicate, but don’t sort const sarr = Array.from(new Set(savpqa.map((savpq) => savpq.aturi)));
//const result = await batShitQueryClient.fetchQuery( // constructLabelMergeQuery({ s: sarr, l: larr }), //); const result = await innerAVPostsQueryFn({aturis: sarr, avurl: savpqa.at(-1)?.avurl || savpqa[0].avurl}) //const qfn = constructLabelMergeQuery({ s: sarr, l: larr }).queryFn //const result = await (qfn ? qfn() : ()=>{}) if (!result) return [];
// Build maps for quick lookup //const errmap = new Map<string, LabelMergeQueryLabelsOutputSchemaError>(); // const resmap = new Map<string, SingularAVPostResult>();
// result.posts?.forEach((post) => resmap.set(post.uri, post));
// // Map back to the original queries // const output: Record<string, SingularAVPostResult>[] = savpqa.map((savpq) => { // const key = savpq.aturi; // or just slq.l if you prefer
// //const err = errmap.get(slq.l); // const post = resmap.get(key);
// //if (err) return { [key]: { error: err } }; // if (post) return { [key]: { labels: label } };
// // if result is neither, it means the subject is free of labels // return { // [key]: { labels: undefined} // }; // // idiot // // return { // // [key]: { error: { // // s: slq.l, // // e: `!internal-bslm-unknown: ${slq.s}` // // }} // // }; // }); const output = result.posts;
return output; }, // when we call users.fetch, this will resolve the correct user using the field `id` resolver: (rslra, savpq) => { if (rslra.length < 1) { return undefined; } // const result: SingularLabelResult | undefined = slra.find((slr, i) => { // // find if error first // const error = slr.error; // const label = slr.labels; // if (error) { // if (slq.l === error.s) { // return slq; // } // } else if (label) { // // if not error // if (slq.l === label.src && slq.s === label.uri) { // return slq; // } // // else unhandled not found // } else { // return undefined; // } // return undefined; // }); //const outputMap: Record<string, SingularLabelResult> = Object.assign({}, ...rslra) //const key = `${slq.l}::${slq.s}`; // or just slq.l if you prefer const item = rslra.find(obj => obj.uri === savpq.aturi); const result: SingularAVPostResult | undefined = item//outputMap[key] return result; }, scheduler: windowScheduler(10 * 100), // 1 second },);
export function constructSingularAVPostQuery(options: SingularAVPostQuery) { const { aturi, avurl, instantBypass } = options; const parsedaturi = new ATPAPI.AtUri(aturi)
return queryOptions({ queryKey: ["savpq", parsedaturi.host, /*"__volatile", */aturi],
enabled: !!aturi && !!avurl,
queryFn: async (): Promise<SingularAVPostResult | undefined> => { // const result = (await labelmerge.fetch(options).catch(err => {throw { error: err } as SingularLabelResult})) as SingularLabelResult // if (result.error) { // throw result.error // } // return result; if (instantBypass) { const params = new URLSearchParams(); params.append("uris", aturi) const url = `${avurl}/xrpc/app.bsky.feed.getPosts?${params.toString()}`;
const res = await fetch(url); if (!res.ok) { throw new Error(`Labelmerge fetch failed: ${res.status} ${res.statusText}`); }
const result = (await res.json()) as ATPAPI.AppBskyFeedGetPosts.OutputSchema; return result.posts[0] } const result = (await postquerymerge .fetch(options))as SingularAVPostResult; // .catch( // (err) => ({ error: err }) as SingularAVPostResult, // )) as SingularAVPostResult; if (result === undefined) { throw new Error("what the hell happened") } return result; },
staleTime: 5 * 60 * 1000, // 5 minutes gcTime: 5 * 60 * 1000, });}
export function useQuerySingularAVPostQuery(options: SingularAVPostQuery) { return useQuery(constructSingularAVPostQuery(options));}
// conven
/** * Doesnt work * @deprecated doesnt work */export function constructGetProfilesQuery( disabled?: boolean, ids?: string[], appviewdid?: string, agent?: ATPAPI.Agent) { // Ensure we have a valid agent and actual IDs (not an array of undefined) const hasValidIds = ids && ids.length > 0 && ids.every(id => !!id); const canRun = !disabled && !!agent && !!appviewdid && hasValidIds;
return queryOptions({ enabled: !!canRun, queryKey: ["getprofiles", ids, appviewdid], queryFn: async () => { // Guard clause inside the fetcher if (!agent || !ids || ids.length === 0) { throw new Error("Query attempted to run without required credentials"); }
const decodedids = ids.map(id => decodeURIComponent(id)); const res = await agent.app.bsky.actor.getProfiles({ actors: decodedids });
if (!res.success) throw new Error("Failed to fetch profiles"); return res.data; }, staleTime: 5 * 60 * 1000, gcTime: 5 * 60 * 1000, });}
/** * Doesnt work * @deprecated doesnt work */export function useQueryGetProfiles( disabled?: boolean, ids?: string[], appviewdid?: string, agent?: ATPAPI.Agent) { return useQuery(constructGetProfilesQuery(disabled, ids, appviewdid, agent));}
// modified from bluesky social-app /src/state/queries/profile.ts line 60const RQKEY_ROOT = 'profile'const RQKEY = (did: string) => [RQKEY_ROOT, did]
export function useProfileQuery({ did, staleTime = 5 * 60 * 1000,}: { did: string | undefined staleTime?: number}) { // same issue as the one in c.$did.tsx, you should really consider making the useAuth() hook handle unauthed agent calls // todo dont make new public agents all the fucking time //const defaultagent = new ATPAPI.AtpAgent({ service: 'https://public.api.bsky.app' }); const { agent, status } = useAuth(); //const agent = goodagent || defaultagent; //const {getUnstableProfile} = useUnstableProfileViewCache() return useQuery<ATPAPI.AppBskyActorDefs.ProfileViewDetailed>({ // WARNING // this staleTime is load-bearing // if you remove it, the UI infinite-loops // -prf staleTime, refetchOnWindowFocus: true, queryKey: [RQKEY(did ?? ''), status, `profile:${agent?.did || "unauthed"}`], queryFn: async () => { const res = await agent!.getProfile({actor: did ?? ''}) return res.data }, // placeholderData: () => { // if (!did) return // return getUnstableProfile(did) as ATPAPI.AppBskyActorDefs.ProfileViewDetailed // }, enabled: !!did && (status === "signedIn" || status === "signedOut"), })}