Bluesky app fork with some witchin' additions 馃挮
at linkat-integration 128 lines 3.5 kB view raw
1import {type AppBskyContactGetMatches} from '@atproto/api' 2import { 3 type InfiniteData, 4 type QueryClient, 5 useInfiniteQuery, 6 useQuery, 7} from '@tanstack/react-query' 8 9import {useAgent} from '#/state/session' 10import {type Match} from '#/components/contacts/state' 11import type * as bsky from '#/types/bsky' 12import {STALE} from '.' 13 14const RQ_KEY_ROOT = 'find-contacts' 15export const findContactsStatusQueryKey = [RQ_KEY_ROOT, 'sync-status'] 16 17export function useContactsSyncStatusQuery() { 18 const agent = useAgent() 19 20 return useQuery({ 21 queryKey: findContactsStatusQueryKey, 22 queryFn: async () => { 23 const status = await agent.app.bsky.contact.getSyncStatus() 24 return status.data 25 }, 26 staleTime: STALE.SECONDS.THIRTY, 27 }) 28} 29 30export const findContactsGetMatchesQueryKey = [RQ_KEY_ROOT, 'matches'] 31 32export function useContactsMatchesQuery() { 33 const agent = useAgent() 34 35 return useInfiniteQuery({ 36 queryKey: findContactsGetMatchesQueryKey, 37 queryFn: async ({pageParam}) => { 38 const matches = await agent.app.bsky.contact.getMatches({ 39 cursor: pageParam, 40 }) 41 return matches.data 42 }, 43 initialPageParam: undefined as string | undefined, 44 getNextPageParam: lastPage => lastPage.cursor, 45 staleTime: STALE.MINUTES.ONE, 46 }) 47} 48 49export function optimisticRemoveMatch(queryClient: QueryClient, did: string) { 50 queryClient.setQueryData<InfiniteData<AppBskyContactGetMatches.OutputSchema>>( 51 findContactsGetMatchesQueryKey, 52 old => { 53 if (!old) return old 54 55 return { 56 ...old, 57 pages: old.pages.map(page => ({ 58 ...page, 59 matches: page.matches.filter(match => match.did !== did), 60 })), 61 } 62 }, 63 ) 64} 65 66export const findContactsMatchesPassthroughQueryKey = (dids: string[]) => [ 67 RQ_KEY_ROOT, 68 'passthrough', 69 dids, 70] 71 72/** 73 * DIRTY HACK WARNING! 74 * 75 * The only way to get shadow state to work is to put it into React Query. 76 * However, when we get the matches it's via a POST, not a GET, so we use a mutation, 77 * which means we can't use shadowing! 78 * 79 * In lieu of any better ideas, I'm just going to take the contacts we have and 80 * "launder" them through a dummy query. This will then return "shadow-able" profiles. 81 */ 82export function useMatchesPassthroughQuery(matches: Match[]) { 83 const dids = matches.map(match => match.profile.did) 84 const {data} = useQuery({ 85 queryKey: findContactsMatchesPassthroughQueryKey(dids), 86 queryFn: () => { 87 return matches 88 }, 89 }) 90 return data ?? matches 91} 92 93export function* findAllProfilesInQueryData( 94 queryClient: QueryClient, 95 did: string, 96): Generator<bsky.profile.AnyProfileView, void> { 97 const queryDatas = queryClient.getQueriesData< 98 InfiniteData<AppBskyContactGetMatches.OutputSchema> 99 >({ 100 queryKey: findContactsGetMatchesQueryKey, 101 }) 102 for (const [_queryKey, queryData] of queryDatas) { 103 if (!queryData?.pages) { 104 continue 105 } 106 for (const page of queryData?.pages) { 107 for (const match of page.matches) { 108 if (match.did === did) { 109 yield match 110 } 111 } 112 } 113 } 114 115 const passthroughQueryDatas = queryClient.getQueriesData<Match[]>({ 116 queryKey: [RQ_KEY_ROOT, 'passthrough'], 117 }) 118 for (const [_queryKey, queryData] of passthroughQueryDatas) { 119 if (!queryData) { 120 continue 121 } 122 for (const match of queryData) { 123 if (match.profile.did === did) { 124 yield match.profile 125 } 126 } 127 } 128}