mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

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

at static-click 197 lines 5.7 kB view raw
1/** 2 * NOTE 3 * 4 * This query is a temporary solution to our lack of server API for 5 * querying user membership in an API. It is extremely inefficient. 6 * 7 * THIS SHOULD ONLY BE USED IN MODALS FOR MODIFYING A USER'S LIST MEMBERSHIP! 8 * Use the list-members query for rendering a list's members. 9 * 10 * It works by fetching *all* of the user's list item records and querying 11 * or manipulating that cache. For users with large lists, it will fall 12 * down completely, so be very conservative about how you use it. 13 * 14 * -prf 15 */ 16 17import {AtUri} from '@atproto/api' 18import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' 19 20import {STALE} from '#/state/queries' 21import {RQKEY as LIST_MEMBERS_RQKEY} from '#/state/queries/list-members' 22import {useAgent, useSession} from '#/state/session' 23 24// sanity limit is SANITY_PAGE_LIMIT*PAGE_SIZE total records 25const SANITY_PAGE_LIMIT = 1000 26const PAGE_SIZE = 100 27// ...which comes 100,000k list members 28 29const RQKEY_ROOT = 'list-memberships' 30export const RQKEY = () => [RQKEY_ROOT] 31 32export interface ListMembersip { 33 membershipUri: string 34 listUri: string 35 actorDid: string 36} 37 38/** 39 * This API is dangerous! Read the note above! 40 */ 41export function useDangerousListMembershipsQuery() { 42 const {currentAccount} = useSession() 43 const agent = useAgent() 44 return useQuery<ListMembersip[]>({ 45 staleTime: STALE.MINUTES.FIVE, 46 queryKey: RQKEY(), 47 async queryFn() { 48 if (!currentAccount) { 49 return [] 50 } 51 let cursor 52 let arr: ListMembersip[] = [] 53 for (let i = 0; i < SANITY_PAGE_LIMIT; i++) { 54 const res = await agent.app.bsky.graph.listitem.list({ 55 repo: currentAccount.did, 56 limit: PAGE_SIZE, 57 cursor, 58 }) 59 arr = arr.concat( 60 res.records.map(r => ({ 61 membershipUri: r.uri, 62 listUri: r.value.list, 63 actorDid: r.value.subject, 64 })), 65 ) 66 cursor = res.cursor 67 if (!cursor) { 68 break 69 } 70 } 71 return arr 72 }, 73 }) 74} 75 76/** 77 * Returns undefined for pending, false for not a member, and string for a member (the URI of the membership record) 78 */ 79export function getMembership( 80 memberships: ListMembersip[] | undefined, 81 list: string, 82 actor: string, 83): string | false | undefined { 84 if (!memberships) { 85 return undefined 86 } 87 const membership = memberships.find( 88 m => m.listUri === list && m.actorDid === actor, 89 ) 90 return membership ? membership.membershipUri : false 91} 92 93export function useListMembershipAddMutation() { 94 const {currentAccount} = useSession() 95 const agent = useAgent() 96 const queryClient = useQueryClient() 97 return useMutation< 98 {uri: string; cid: string}, 99 Error, 100 {listUri: string; actorDid: string} 101 >({ 102 mutationFn: async ({listUri, actorDid}) => { 103 if (!currentAccount) { 104 throw new Error('Not logged in') 105 } 106 const res = await agent.app.bsky.graph.listitem.create( 107 {repo: currentAccount.did}, 108 { 109 subject: actorDid, 110 list: listUri, 111 createdAt: new Date().toISOString(), 112 }, 113 ) 114 // TODO 115 // we need to wait for appview to update, but there's not an efficient 116 // query for that, so we use a timeout below 117 // -prf 118 return res 119 }, 120 onSuccess(data, variables) { 121 // manually update the cache; a refetch is too expensive 122 let memberships = queryClient.getQueryData<ListMembersip[]>(RQKEY()) 123 if (memberships) { 124 memberships = memberships 125 // avoid dups 126 .filter( 127 m => 128 !( 129 m.actorDid === variables.actorDid && 130 m.listUri === variables.listUri 131 ), 132 ) 133 .concat([ 134 { 135 ...variables, 136 membershipUri: data.uri, 137 }, 138 ]) 139 queryClient.setQueryData(RQKEY(), memberships) 140 } 141 // invalidate the members queries (used for rendering the listings) 142 // use a timeout to wait for the appview (see above) 143 setTimeout(() => { 144 queryClient.invalidateQueries({ 145 queryKey: LIST_MEMBERS_RQKEY(variables.listUri), 146 }) 147 }, 1e3) 148 }, 149 }) 150} 151 152export function useListMembershipRemoveMutation() { 153 const {currentAccount} = useSession() 154 const agent = useAgent() 155 const queryClient = useQueryClient() 156 return useMutation< 157 void, 158 Error, 159 {listUri: string; actorDid: string; membershipUri: string} 160 >({ 161 mutationFn: async ({membershipUri}) => { 162 if (!currentAccount) { 163 throw new Error('Not logged in') 164 } 165 const membershipUrip = new AtUri(membershipUri) 166 await agent.app.bsky.graph.listitem.delete({ 167 repo: currentAccount.did, 168 rkey: membershipUrip.rkey, 169 }) 170 // TODO 171 // we need to wait for appview to update, but there's not an efficient 172 // query for that, so we use a timeout below 173 // -prf 174 }, 175 onSuccess(data, variables) { 176 // manually update the cache; a refetch is too expensive 177 let memberships = queryClient.getQueryData<ListMembersip[]>(RQKEY()) 178 if (memberships) { 179 memberships = memberships.filter( 180 m => 181 !( 182 m.actorDid === variables.actorDid && 183 m.listUri === variables.listUri 184 ), 185 ) 186 queryClient.setQueryData(RQKEY(), memberships) 187 } 188 // invalidate the members queries (used for rendering the listings) 189 // use a timeout to wait for the appview (see above) 190 setTimeout(() => { 191 queryClient.invalidateQueries({ 192 queryKey: LIST_MEMBERS_RQKEY(variables.listUri), 193 }) 194 }, 1e3) 195 }, 196 }) 197}