an independent Bluesky client using Constellation, PDS Queries, and other services reddwarf.app
frontend spa bluesky reddwarf microcosm client app
99
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 69f8676a123d7c9cc5cfbd214e1206255b750313 163 lines 4.1 kB view raw
1import { type Agent,AtUri } from "@atproto/api"; 2import { TID } from "@atproto/common-web"; 3import type { QueryClient } from "@tanstack/react-query"; 4 5import { type linksRecordsResponse,useQueryConstellation } from "./useQuery"; 6 7export function useGetFollowState({ 8 target, 9 user, 10}: { 11 target: string; 12 user?: string; 13}): string[] | undefined { 14 const { data: followData } = useQueryConstellation( 15 user 16 ? { 17 method: "/links", 18 target: target, 19 // @ts-expect-error overloading sucks so much 20 collection: "app.bsky.graph.follow", 21 path: ".subject", 22 dids: [user], 23 } 24 : { method: "undefined", target: "whatever" } 25 // overloading sucks so much 26 ) as { data: linksRecordsResponse | undefined }; 27 const follows = followData?.linking_records.slice(0, 50) ?? []; 28 29 if (follows.length > 0) { 30 return follows.map((linksRecord) => { 31 return `at://${linksRecord.did}/${linksRecord.collection}/${linksRecord.rkey}`; 32 }); 33 } 34 35 return undefined; 36} 37 38export function toggleFollow({ 39 agent, 40 targetDid, 41 followRecords, 42 queryClient, 43}: { 44 agent?: Agent; 45 targetDid?: string; 46 followRecords: undefined | string[]; 47 queryClient: QueryClient; 48}) { 49 if (!agent?.did || !targetDid) return; 50 51 const queryKey = [ 52 "constellation", 53 "/links", 54 targetDid, 55 "app.bsky.graph.follow", 56 ".subject", 57 undefined, 58 [agent.did], 59 ] as const; 60 61 const updateCache = ( 62 updater: ( 63 oldData: linksRecordsResponse | undefined 64 ) => linksRecordsResponse | undefined 65 ) => { 66 queryClient.setQueryData( 67 queryKey, 68 (oldData: linksRecordsResponse | undefined) => updater(oldData) 69 ); 70 }; 71 72 if (typeof followRecords === "undefined") { 73 const newRecord = { 74 repo: agent.did, 75 collection: "app.bsky.graph.follow", 76 rkey: TID.next().toString(), 77 record: { 78 $type: "app.bsky.graph.follow", 79 subject: targetDid, 80 createdAt: new Date().toISOString(), 81 }, 82 }; 83 84 updateCache((old) => { 85 const newLinkingRecords = [newRecord, ...(old?.linking_records ?? [])]; 86 return { 87 ...old, 88 linking_records: newLinkingRecords, 89 } as linksRecordsResponse; 90 }); 91 92 agent.com.atproto.repo.createRecord(newRecord).catch((err) => { 93 console.error("Follow failed, reverting cache:", err); 94 // rollback cache 95 updateCache((old) => { 96 return { 97 ...old, 98 linking_records: 99 old?.linking_records.filter((r) => r.rkey !== newRecord.rkey) ?? [], 100 } as linksRecordsResponse; 101 }); 102 }); 103 104 return; 105 } 106 107 followRecords.forEach((followRecord) => { 108 const aturi = new AtUri(followRecord); 109 agent.com.atproto.repo 110 .deleteRecord({ 111 repo: agent.did!, 112 collection: "app.bsky.graph.follow", 113 rkey: aturi.rkey, 114 }) 115 .catch(console.error); 116 }); 117 118 updateCache((old) => { 119 if (!old?.linking_records) return old; 120 return { 121 ...old, 122 linking_records: old.linking_records.filter( 123 (rec) => 124 !followRecords.includes( 125 `at://${rec.did}/${rec.collection}/${rec.rkey}` 126 ) 127 ), 128 }; 129 }); 130} 131 132 133 134export function useGetOneToOneState(params?: { 135 target: string; 136 user: string; 137 collection: string; 138 path: string; 139}): string[] | undefined { 140 const { data: arbitrarydata } = useQueryConstellation( 141 params && params.user 142 ? { 143 method: "/links", 144 target: params.target, 145 // @ts-expect-error overloading sucks so much 146 collection: params.collection, 147 path: params.path, 148 dids: [params.user], 149 } 150 : { method: "undefined", target: "whatever" } 151 // overloading sucks so much 152 ) as { data: linksRecordsResponse | undefined }; 153 if (!params || !params.user) return undefined; 154 const data = arbitrarydata?.linking_records.slice(0, 50) ?? []; 155 156 if (data.length > 0) { 157 return data.map((linksRecord) => { 158 return `at://${linksRecord.did}/${linksRecord.collection}/${linksRecord.rkey}`; 159 }); 160 } 161 162 return undefined; 163}