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}