create abstraction for persisting queries, make gcTime infinite
This commit is contained in:
+22
-25
@@ -8,11 +8,11 @@ import {
|
|||||||
moderateFeedGenerator,
|
moderateFeedGenerator,
|
||||||
RichText,
|
RichText,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
|
import {t} from '@lingui/macro'
|
||||||
import {
|
import {
|
||||||
type InfiniteData,
|
type InfiniteData,
|
||||||
keepPreviousData,
|
keepPreviousData,
|
||||||
type QueryClient,
|
type QueryClient,
|
||||||
type QueryKey,
|
|
||||||
useInfiniteQuery,
|
useInfiniteQuery,
|
||||||
useMutation,
|
useMutation,
|
||||||
useQuery,
|
useQuery,
|
||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
import {DISCOVER_FEED_URI, DISCOVER_SAVED_FEED} from '#/lib/constants'
|
import {DISCOVER_FEED_URI, DISCOVER_SAVED_FEED} from '#/lib/constants'
|
||||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||||
import {PERSISTED_QUERY_ROOT, STALE} from '#/state/queries'
|
import {persist, STALE} from '#/state/queries'
|
||||||
import {RQKEY as listQueryKey} from '#/state/queries/list'
|
import {RQKEY as listQueryKey} from '#/state/queries/list'
|
||||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||||
import {useAgent, useSession} from '#/state/session'
|
import {useAgent, useSession} from '#/state/session'
|
||||||
@@ -114,7 +114,7 @@ export function hydrateFeedGenerator(
|
|||||||
avatar: view.avatar,
|
avatar: view.avatar,
|
||||||
displayName: view.displayName
|
displayName: view.displayName
|
||||||
? sanitizeDisplayName(view.displayName)
|
? sanitizeDisplayName(view.displayName)
|
||||||
: `Feed by ${sanitizeHandle(view.creator.handle, '@')}`,
|
: t`Feed by ${sanitizeHandle(view.creator.handle, '@')}`,
|
||||||
description: new RichText({
|
description: new RichText({
|
||||||
text: view.description || '',
|
text: view.description || '',
|
||||||
facets: (view.descriptionFacets || [])?.slice(),
|
facets: (view.descriptionFacets || [])?.slice(),
|
||||||
@@ -155,7 +155,7 @@ export function hydrateList(view: AppBskyGraphDefs.ListView): FeedSourceInfo {
|
|||||||
creatorHandle: view.creator.handle,
|
creatorHandle: view.creator.handle,
|
||||||
displayName: view.name
|
displayName: view.name
|
||||||
? sanitizeDisplayName(view.name)
|
? sanitizeDisplayName(view.name)
|
||||||
: `User List by ${sanitizeHandle(view.creator.handle, '@')}`,
|
: t`User List by ${sanitizeHandle(view.creator.handle, '@')}`,
|
||||||
contentMode: undefined,
|
contentMode: undefined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,13 +238,7 @@ export function useGetPopularFeedsQuery(options?: GetPopularFeedsOptions) {
|
|||||||
)
|
)
|
||||||
const lastPageCountRef = useRef(0)
|
const lastPageCountRef = useRef(0)
|
||||||
|
|
||||||
const query = useInfiniteQuery<
|
const query = useInfiniteQuery({
|
||||||
AppBskyUnspeccedGetPopularFeedGenerators.OutputSchema,
|
|
||||||
Error,
|
|
||||||
InfiniteData<AppBskyUnspeccedGetPopularFeedGenerators.OutputSchema>,
|
|
||||||
QueryKey,
|
|
||||||
string | undefined
|
|
||||||
>({
|
|
||||||
enabled: Boolean(moderationOpts) && options?.enabled !== false,
|
enabled: Boolean(moderationOpts) && options?.enabled !== false,
|
||||||
queryKey: createGetPopularFeedsQueryKey(options),
|
queryKey: createGetPopularFeedsQueryKey(options),
|
||||||
queryFn: async ({pageParam}) => {
|
queryFn: async ({pageParam}) => {
|
||||||
@@ -261,7 +255,7 @@ export function useGetPopularFeedsQuery(options?: GetPopularFeedsOptions) {
|
|||||||
|
|
||||||
return res.data
|
return res.data
|
||||||
},
|
},
|
||||||
initialPageParam: undefined,
|
initialPageParam: undefined as string | undefined,
|
||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
select: useCallback(
|
select: useCallback(
|
||||||
(
|
(
|
||||||
@@ -418,11 +412,10 @@ const PWI_DISCOVER_FEED_STUB: SavedFeedSourceInfo = {
|
|||||||
contentMode: undefined,
|
contentMode: undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
const createPinnedFeedInfosQueryKeyRoot = (...args: any[]) => [
|
const createPinnedFeedInfosQueryKeyRoot = (
|
||||||
PERSISTED_QUERY_ROOT,
|
kind: 'pinned' | 'saved',
|
||||||
'feed-info',
|
feedUris: string[],
|
||||||
...args,
|
) => ['feed-info', kind, feedUris]
|
||||||
]
|
|
||||||
|
|
||||||
export function usePinnedFeedsInfos() {
|
export function usePinnedFeedsInfos() {
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
@@ -431,12 +424,14 @@ export function usePinnedFeedsInfos() {
|
|||||||
const pinnedItems = preferences?.savedFeeds.filter(feed => feed.pinned) ?? []
|
const pinnedItems = preferences?.savedFeeds.filter(feed => feed.pinned) ?? []
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
|
...persist(
|
||||||
|
createPinnedFeedInfosQueryKeyRoot(
|
||||||
|
'pinned',
|
||||||
|
pinnedItems.map(f => f.value),
|
||||||
|
),
|
||||||
|
),
|
||||||
staleTime: STALE.INFINITY,
|
staleTime: STALE.INFINITY,
|
||||||
enabled: !isLoadingPrefs,
|
enabled: !isLoadingPrefs,
|
||||||
queryKey: createPinnedFeedInfosQueryKeyRoot(
|
|
||||||
'pinned',
|
|
||||||
...pinnedItems.map(f => f.value),
|
|
||||||
),
|
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (!hasSession) {
|
if (!hasSession) {
|
||||||
return [PWI_DISCOVER_FEED_STUB]
|
return [PWI_DISCOVER_FEED_STUB]
|
||||||
@@ -538,12 +533,14 @@ export function useSavedFeeds() {
|
|||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
|
...persist(
|
||||||
|
createPinnedFeedInfosQueryKeyRoot(
|
||||||
|
'saved',
|
||||||
|
savedItems.map(f => f.value),
|
||||||
|
),
|
||||||
|
),
|
||||||
staleTime: STALE.INFINITY,
|
staleTime: STALE.INFINITY,
|
||||||
enabled: !isLoadingPrefs,
|
enabled: !isLoadingPrefs,
|
||||||
queryKey: createPinnedFeedInfosQueryKeyRoot(
|
|
||||||
'saved',
|
|
||||||
...savedItems.map(f => f.value),
|
|
||||||
),
|
|
||||||
placeholderData: previousData => {
|
placeholderData: previousData => {
|
||||||
return (
|
return (
|
||||||
previousData || {
|
previousData || {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import {type QueryKey, queryOptions} from '@tanstack/react-query'
|
||||||
|
|
||||||
const SECOND = 1e3
|
const SECOND = 1e3
|
||||||
const MINUTE = SECOND * 60
|
const MINUTE = SECOND * 60
|
||||||
const HOUR = MINUTE * 60
|
const HOUR = MINUTE * 60
|
||||||
@@ -32,5 +34,34 @@ export const STALE = {
|
|||||||
*
|
*
|
||||||
* Also, only use this for queries that are safe to persist between
|
* Also, only use this for queries that are safe to persist between
|
||||||
* app launches (like user preferences).
|
* app launches (like user preferences).
|
||||||
|
*
|
||||||
|
* Note that for queries that are persisted, it is recommended to extend
|
||||||
|
* the `gcTime` to a longer duration, otherwise it'll get busted
|
||||||
*/
|
*/
|
||||||
export const PERSISTED_QUERY_ROOT = 'PERSISTED'
|
export const PERSISTED_QUERY_ROOT = 'PERSISTED'
|
||||||
|
export const PERSISTED_QUERY_GCTIME = Infinity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up a query to be persisted automatically by the `PersistQueryClientProvider`.
|
||||||
|
* This automatically adjusts the query key, and sets the `gcTime` to be infinite.
|
||||||
|
*
|
||||||
|
* Be careful when using this, since it will change the query key and may
|
||||||
|
* break any cases where we call `invalidateQueries` or `refetchQueries`
|
||||||
|
* with the old key.
|
||||||
|
*
|
||||||
|
* Also, only use this for queries that are safe to persist between
|
||||||
|
* app launches (like user preferences).
|
||||||
|
*
|
||||||
|
* Use like this:
|
||||||
|
* ```ts
|
||||||
|
* useQuery({
|
||||||
|
* ...persist(['user', userId]),
|
||||||
|
* queryFn: () => fetchUser(userId),
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
export function persist<T extends QueryKey>(key: T) {
|
||||||
|
return queryOptions({
|
||||||
|
queryKey: [PERSISTED_QUERY_ROOT, ...key],
|
||||||
|
gcTime: PERSISTED_QUERY_GCTIME,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
|
|||||||
import {z} from 'zod'
|
import {z} from 'zod'
|
||||||
|
|
||||||
import {MAX_LABELERS} from '#/lib/constants'
|
import {MAX_LABELERS} from '#/lib/constants'
|
||||||
import {PERSISTED_QUERY_ROOT, STALE} from '#/state/queries'
|
import {persist, STALE} from '#/state/queries'
|
||||||
import {
|
import {
|
||||||
preferencesQueryKey,
|
preferencesQueryKey,
|
||||||
usePreferencesQuery,
|
usePreferencesQuery,
|
||||||
@@ -22,8 +22,7 @@ export const labelersInfoQueryKey = (dids: string[]) => [
|
|||||||
dids.slice().sort(),
|
dids.slice().sort(),
|
||||||
]
|
]
|
||||||
|
|
||||||
export const persistedLabelersDetailedInfoQueryKey = (dids: string[]) => [
|
const persistedLabelersDetailedInfoQueryKey = (dids: string[]) => [
|
||||||
PERSISTED_QUERY_ROOT,
|
|
||||||
'labelers-detailed-info',
|
'labelers-detailed-info',
|
||||||
dids,
|
dids,
|
||||||
]
|
]
|
||||||
@@ -64,8 +63,8 @@ export function useLabelersInfoQuery({dids}: {dids: string[]}) {
|
|||||||
export function useLabelersDetailedInfoQuery({dids}: {dids: string[]}) {
|
export function useLabelersDetailedInfoQuery({dids}: {dids: string[]}) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
return useQuery({
|
return useQuery({
|
||||||
|
...persist(persistedLabelersDetailedInfoQueryKey(dids)),
|
||||||
enabled: !!dids.length,
|
enabled: !!dids.length,
|
||||||
queryKey: persistedLabelersDetailedInfoQueryKey(dids),
|
|
||||||
gcTime: 1000 * 60 * 60 * 6, // 6 hours
|
gcTime: 1000 * 60 * 60 * 6, // 6 hours
|
||||||
staleTime: STALE.MINUTES.ONE,
|
staleTime: STALE.MINUTES.ONE,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
|
|||||||
import {PROD_DEFAULT_FEED} from '#/lib/constants'
|
import {PROD_DEFAULT_FEED} from '#/lib/constants'
|
||||||
import {replaceEqualDeep} from '#/lib/functions'
|
import {replaceEqualDeep} from '#/lib/functions'
|
||||||
import {getAge} from '#/lib/strings/time'
|
import {getAge} from '#/lib/strings/time'
|
||||||
import {PERSISTED_QUERY_ROOT, STALE} from '#/state/queries'
|
import {
|
||||||
|
PERSISTED_QUERY_GCTIME,
|
||||||
|
PERSISTED_QUERY_ROOT,
|
||||||
|
STALE,
|
||||||
|
} from '#/state/queries'
|
||||||
import {
|
import {
|
||||||
DEFAULT_HOME_FEED_PREFS,
|
DEFAULT_HOME_FEED_PREFS,
|
||||||
DEFAULT_LOGGED_OUT_PREFERENCES,
|
DEFAULT_LOGGED_OUT_PREFERENCES,
|
||||||
@@ -40,6 +44,7 @@ export function usePreferencesQuery() {
|
|||||||
structuralSharing: replaceEqualDeep,
|
structuralSharing: replaceEqualDeep,
|
||||||
refetchOnWindowFocus: true,
|
refetchOnWindowFocus: true,
|
||||||
queryKey: preferencesQueryKey,
|
queryKey: preferencesQueryKey,
|
||||||
|
gcTime: PERSISTED_QUERY_GCTIME,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (!agent.did) {
|
if (!agent.did) {
|
||||||
return DEFAULT_LOGGED_OUT_PREFERENCES
|
return DEFAULT_LOGGED_OUT_PREFERENCES
|
||||||
|
|||||||
Reference in New Issue
Block a user