Something went wrong. Try again.
an independent Bluesky client using Constellation, PDS Queries, and other services reddwarf.app
frontend spa bluesky reddwarf microcosm client app
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617import * as ATPAPI from "@atproto/api";import { type QueryFunctionContext, queryOptions, useInfiniteQuery, useQuery, type UseQueryResult} from "@tanstack/react-query";
export function constructIdentityQuery(didorhandle?: string) { return queryOptions({ queryKey: ["identity", didorhandle], queryFn: async () => { if (!didorhandle) return undefined as undefined const res = await fetch( `https://slingshot.microcosm.blue/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) { return useQuery(constructIdentityQuery(didorhandle));}
export function constructPostQuery(uri?: string) { return queryOptions({ queryKey: ["post", uri], queryFn: async () => { if (!uri) return undefined as undefined const res = await fetch( `https://slingshot.microcosm.blue/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) { return useQuery(constructPostQuery(uri));}
export function constructProfileQuery(uri?: string) { return queryOptions({ queryKey: ["profile", uri], queryFn: async () => { if (!uri) return undefined as undefined const res = await fetch( `https://slingshot.microcosm.blue/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) { return useQuery(constructProfileQuery(uri));}
// 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?:{ method: | "/links" | "/links/distinct-dids" | "/links/count" | "/links/count/distinct-dids" | "/links/all" | "undefined", target: string, collection?: string, path?: string, cursor?: string, dids?: string[]}) { // : QueryOptions< // | linksRecordsResponse // | linksDidsResponse // | linksCountResponse // | linksAllResponse // | undefined, // Error // > return queryOptions({ queryKey: ["constellation", query?.method, query?.target, query?.collection, query?.path, query?.cursor, query?.dids] 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://constellation.microcosm.blue${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, });}export function useQueryConstellation(query: { method: "/links"; target: string; collection: string; path: string; cursor?: string; dids?: string[];}): UseQueryResult<linksRecordsResponse, Error>;export function useQueryConstellation(query: { method: "/links/distinct-dids"; target: string; collection: string; path: string; cursor?: string;}): UseQueryResult<linksDidsResponse, Error>;export function useQueryConstellation(query: { method: "/links/count"; target: string; collection: string; path: string; cursor?: string;}): UseQueryResult<linksCountResponse, Error>;export function useQueryConstellation(query: { method: "/links/count/distinct-dids"; target: string; collection: string; path: string; cursor?: string;}): UseQueryResult<linksCountResponse, Error>;export function useQueryConstellation(query: { method: "/links/all"; target: string;}): UseQueryResult<linksAllResponse, Error>;export function useQueryConstellation(): undefined;export function useQueryConstellation(query: { method: "undefined"; target: string;}): 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[];}): | UseQueryResult< | linksRecordsResponse | linksDidsResponse | linksCountResponse | linksAllResponse | undefined, Error > | undefined { //if (!query) return; return useQuery( constructConstellationQuery(query) );}
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;};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 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"); return res.json(); }, });}export function useQueryPreferences(options: { agent?: ATPAPI.Agent | undefined, pdsUrl?: string | undefined}) { return useQuery(constructPreferencesQuery(options.agent, options.pdsUrl));}
export function constructArbitraryQuery(uri?: string) { return queryOptions({ queryKey: ["arbitrary", uri], queryFn: async () => { if (!uri) return undefined as undefined const res = await fetch( `https://slingshot.microcosm.blue/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) { return useQuery(constructArbitraryQuery(uri));}
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) { return queryOptions({ queryKey: ['authorFeed', did], 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=app.bsky.feed.post&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) { const { queryKey, queryFn } = constructAuthorFeedQuery(did!, pdsUrl!); 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;}) { const { feedUri, agent, isAuthed, pdsUrl, feedServiceDid } = options; return queryOptions({ queryKey: ["feedSkeleton", feedUri, { isAuthed, did: agent?.did }], queryFn: async ({ pageParam }: QueryFunctionContext): Promise<FeedSkeletonPage> => { const cursorParam = pageParam ? `&cursor=${pageParam}` : ""; if (isAuthed) { 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://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;}) { 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.feedServiceDid : true), });}