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 no-pointer-events 193 lines 5.6 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 {useSession, getAgent} from '#/state/session' 21import {RQKEY as LIST_MEMBERS_RQKEY} from '#/state/queries/list-members' 22import {STALE} from '#/state/queries' 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 29export const RQKEY = () => ['list-memberships'] 30 31export interface ListMembersip { 32 membershipUri: string 33 listUri: string 34 actorDid: string 35} 36 37/** 38 * This API is dangerous! Read the note above! 39 */ 40export function useDangerousListMembershipsQuery() { 41 const {currentAccount} = useSession() 42 return useQuery<ListMembersip[]>({ 43 staleTime: STALE.MINUTES.FIVE, 44 queryKey: RQKEY(), 45 async queryFn() { 46 if (!currentAccount) { 47 return [] 48 } 49 let cursor 50 let arr: ListMembersip[] = [] 51 for (let i = 0; i < SANITY_PAGE_LIMIT; i++) { 52 const res = await getAgent().app.bsky.graph.listitem.list({ 53 repo: currentAccount.did, 54 limit: PAGE_SIZE, 55 cursor, 56 }) 57 arr = arr.concat( 58 res.records.map(r => ({ 59 membershipUri: r.uri, 60 listUri: r.value.list, 61 actorDid: r.value.subject, 62 })), 63 ) 64 cursor = res.cursor 65 if (!cursor) { 66 break 67 } 68 } 69 return arr 70 }, 71 }) 72} 73 74/** 75 * Returns undefined for pending, false for not a member, and string for a member (the URI of the membership record) 76 */ 77export function getMembership( 78 memberships: ListMembersip[] | undefined, 79 list: string, 80 actor: string, 81): string | false | undefined { 82 if (!memberships) { 83 return undefined 84 } 85 const membership = memberships.find( 86 m => m.listUri === list && m.actorDid === actor, 87 ) 88 return membership ? membership.membershipUri : false 89} 90 91export function useListMembershipAddMutation() { 92 const {currentAccount} = useSession() 93 const queryClient = useQueryClient() 94 return useMutation< 95 {uri: string; cid: string}, 96 Error, 97 {listUri: string; actorDid: string} 98 >({ 99 mutationFn: async ({listUri, actorDid}) => { 100 if (!currentAccount) { 101 throw new Error('Not logged in') 102 } 103 const res = await getAgent().app.bsky.graph.listitem.create( 104 {repo: currentAccount.did}, 105 { 106 subject: actorDid, 107 list: listUri, 108 createdAt: new Date().toISOString(), 109 }, 110 ) 111 // TODO 112 // we need to wait for appview to update, but there's not an efficient 113 // query for that, so we use a timeout below 114 // -prf 115 return res 116 }, 117 onSuccess(data, variables) { 118 // manually update the cache; a refetch is too expensive 119 let memberships = queryClient.getQueryData<ListMembersip[]>(RQKEY()) 120 if (memberships) { 121 memberships = memberships 122 // avoid dups 123 .filter( 124 m => 125 !( 126 m.actorDid === variables.actorDid && 127 m.listUri === variables.listUri 128 ), 129 ) 130 .concat([ 131 { 132 ...variables, 133 membershipUri: data.uri, 134 }, 135 ]) 136 queryClient.setQueryData(RQKEY(), memberships) 137 } 138 // invalidate the members queries (used for rendering the listings) 139 // use a timeout to wait for the appview (see above) 140 setTimeout(() => { 141 queryClient.invalidateQueries({ 142 queryKey: LIST_MEMBERS_RQKEY(variables.listUri), 143 }) 144 }, 1e3) 145 }, 146 }) 147} 148 149export function useListMembershipRemoveMutation() { 150 const {currentAccount} = useSession() 151 const queryClient = useQueryClient() 152 return useMutation< 153 void, 154 Error, 155 {listUri: string; actorDid: string; membershipUri: string} 156 >({ 157 mutationFn: async ({membershipUri}) => { 158 if (!currentAccount) { 159 throw new Error('Not logged in') 160 } 161 const membershipUrip = new AtUri(membershipUri) 162 await getAgent().app.bsky.graph.listitem.delete({ 163 repo: currentAccount.did, 164 rkey: membershipUrip.rkey, 165 }) 166 // TODO 167 // we need to wait for appview to update, but there's not an efficient 168 // query for that, so we use a timeout below 169 // -prf 170 }, 171 onSuccess(data, variables) { 172 // manually update the cache; a refetch is too expensive 173 let memberships = queryClient.getQueryData<ListMembersip[]>(RQKEY()) 174 if (memberships) { 175 memberships = memberships.filter( 176 m => 177 !( 178 m.actorDid === variables.actorDid && 179 m.listUri === variables.listUri 180 ), 181 ) 182 queryClient.setQueryData(RQKEY(), memberships) 183 } 184 // invalidate the members queries (used for rendering the listings) 185 // use a timeout to wait for the appview (see above) 186 setTimeout(() => { 187 queryClient.invalidateQueries({ 188 queryKey: LIST_MEMBERS_RQKEY(variables.listUri), 189 }) 190 }, 1e3) 191 }, 192 }) 193}