mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React, {useRef, useState} from 'react' 2import {AppState, AppStateStatus} from 'react-native' 3import AsyncStorage from '@react-native-async-storage/async-storage' 4import {createAsyncStoragePersister} from '@tanstack/query-async-storage-persister' 5import {focusManager, QueryClient} from '@tanstack/react-query' 6import { 7 PersistQueryClientProvider, 8 PersistQueryClientProviderProps, 9} from '@tanstack/react-query-persist-client' 10 11import {isNative} from '#/platform/detection' 12 13// any query keys in this array will be persisted to AsyncStorage 14export const labelersDetailedInfoQueryKeyRoot = 'labelers-detailed-info' 15const STORED_CACHE_QUERY_KEY_ROOTS = [labelersDetailedInfoQueryKeyRoot] 16 17focusManager.setEventListener(onFocus => { 18 if (isNative) { 19 const subscription = AppState.addEventListener( 20 'change', 21 (status: AppStateStatus) => { 22 focusManager.setFocused(status === 'active') 23 }, 24 ) 25 26 return () => subscription.remove() 27 } else if (typeof window !== 'undefined' && window.addEventListener) { 28 // these handlers are a bit redundant but focus catches when the browser window 29 // is blurred/focused while visibilitychange seems to only handle when the 30 // window minimizes (both of them catch tab changes) 31 // there's no harm to redundant fires because refetchOnWindowFocus is only 32 // used with queries that employ stale data times 33 const handler = () => onFocus() 34 window.addEventListener('focus', handler, false) 35 window.addEventListener('visibilitychange', handler, false) 36 return () => { 37 window.removeEventListener('visibilitychange', handler) 38 window.removeEventListener('focus', handler) 39 } 40 } 41}) 42 43const createQueryClient = () => 44 new QueryClient({ 45 defaultOptions: { 46 queries: { 47 // NOTE 48 // refetchOnWindowFocus breaks some UIs (like feeds) 49 // so we only selectively want to enable this 50 // -prf 51 refetchOnWindowFocus: false, 52 // Structural sharing between responses makes it impossible to rely on 53 // "first seen" timestamps on objects to determine if they're fresh. 54 // Disable this optimization so that we can rely on "first seen" timestamps. 55 structuralSharing: false, 56 // We don't want to retry queries by default, because in most cases we 57 // want to fail early and show a response to the user. There are 58 // exceptions, and those can be made on a per-query basis. For others, we 59 // should give users controls to retry. 60 retry: false, 61 }, 62 }, 63 }) 64 65const dehydrateOptions: PersistQueryClientProviderProps['persistOptions']['dehydrateOptions'] = 66 { 67 shouldDehydrateMutation: (_: any) => false, 68 shouldDehydrateQuery: query => { 69 return STORED_CACHE_QUERY_KEY_ROOTS.includes(String(query.queryKey[0])) 70 }, 71 } 72 73export function QueryProvider({ 74 children, 75 currentDid, 76}: { 77 children: React.ReactNode 78 currentDid: string | undefined 79}) { 80 return ( 81 <QueryProviderInner 82 // Enforce we never reuse cache between users. 83 // These two props MUST stay in sync. 84 key={currentDid} 85 currentDid={currentDid}> 86 {children} 87 </QueryProviderInner> 88 ) 89} 90 91function QueryProviderInner({ 92 children, 93 currentDid, 94}: { 95 children: React.ReactNode 96 currentDid: string | undefined 97}) { 98 const initialDid = useRef(currentDid) 99 if (currentDid !== initialDid.current) { 100 throw Error( 101 'Something is very wrong. Expected did to be stable due to key above.', 102 ) 103 } 104 // We create the query client here so that it's scoped to a specific DID. 105 // Do not move the query client creation outside of this component. 106 const [queryClient, _setQueryClient] = useState(() => createQueryClient()) 107 const [persistOptions, _setPersistOptions] = useState(() => { 108 const asyncPersister = createAsyncStoragePersister({ 109 storage: AsyncStorage, 110 key: 'queryClient-' + (currentDid ?? 'logged-out'), 111 }) 112 return { 113 persister: asyncPersister, 114 dehydrateOptions, 115 } 116 }) 117 return ( 118 <PersistQueryClientProvider 119 client={queryClient} 120 persistOptions={persistOptions}> 121 {children} 122 </PersistQueryClientProvider> 123 ) 124}