Traffic reduction and tuned caching strats (#2215)
* Update the feed to only check latest on focus after 30s, but to do a full reset on focus after 1 hour to avoid very stale data * Remove the isFeedPublic query * Fix: avoid double next-page fetches * Reduce some poll intervals to reduce server load * Guard against double-fires of fetchNextPage * Reduce polling on blurred screens
This commit is contained in:
parent
dd074371cf
commit
2a712630b4
8 changed files with 83 additions and 151 deletions
|
@ -161,51 +161,6 @@ export function useFeedSourceInfoQuery({uri}: {uri: string}) {
|
|||
})
|
||||
}
|
||||
|
||||
export const isFeedPublicQueryKey = ({uri}: {uri: string}) => [
|
||||
'isFeedPublic',
|
||||
uri,
|
||||
]
|
||||
|
||||
export function useIsFeedPublicQuery({uri}: {uri: string}) {
|
||||
return useQuery({
|
||||
queryKey: isFeedPublicQueryKey({uri}),
|
||||
queryFn: async ({queryKey}) => {
|
||||
const [, uri] = queryKey
|
||||
try {
|
||||
const res = await getAgent().app.bsky.feed.getFeed({
|
||||
feed: uri,
|
||||
limit: 1,
|
||||
})
|
||||
return {
|
||||
isPublic: Boolean(res.data.feed),
|
||||
error: undefined,
|
||||
}
|
||||
} catch (e: any) {
|
||||
/**
|
||||
* This should be an `XRPCError`, but I can't safely import from
|
||||
* `@atproto/xrpc` due to a depdency on node's `crypto` module.
|
||||
*
|
||||
* @see https://github.com/bluesky-social/atproto/blob/c17971a2d8e424cc7f10c071d97c07c08aa319cf/packages/xrpc/src/client.ts#L126
|
||||
*/
|
||||
if (e?.status === 401) {
|
||||
return {
|
||||
isPublic: false,
|
||||
error: e,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-401 response means something else went wrong on the server
|
||||
*/
|
||||
return {
|
||||
isPublic: true,
|
||||
error: e,
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const useGetPopularFeedsQueryKey = ['getPopularFeeds']
|
||||
|
||||
export function useGetPopularFeedsQuery() {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* 3. Don't call this query's `refetch()` if you're trying to sync latest; call `checkUnread()` instead.
|
||||
*/
|
||||
|
||||
import {useEffect} from 'react'
|
||||
import {useEffect, useRef} from 'react'
|
||||
import {AppBskyFeedDefs} from '@atproto/api'
|
||||
import {
|
||||
useInfiniteQuery,
|
||||
|
@ -49,6 +49,7 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
|
|||
const threadMutes = useMutedThreads()
|
||||
const unreads = useUnreadNotificationsApi()
|
||||
const enabled = opts?.enabled !== false
|
||||
const lastPageCountRef = useRef(0)
|
||||
|
||||
const query = useInfiniteQuery<
|
||||
FeedPage,
|
||||
|
@ -104,24 +105,26 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
|
|||
|
||||
useEffect(() => {
|
||||
const {isFetching, hasNextPage, data} = query
|
||||
|
||||
let count = 0
|
||||
let numEmpties = 0
|
||||
for (const page of data?.pages || []) {
|
||||
if (!page.items.length) {
|
||||
numEmpties++
|
||||
}
|
||||
count += page.items.length
|
||||
if (isFetching || !hasNextPage) {
|
||||
return
|
||||
}
|
||||
|
||||
// avoid double-fires of fetchNextPage()
|
||||
if (
|
||||
!isFetching &&
|
||||
hasNextPage &&
|
||||
count < PAGE_SIZE &&
|
||||
numEmpties < 3 &&
|
||||
(data?.pages.length || 0) < 6
|
||||
lastPageCountRef.current !== 0 &&
|
||||
lastPageCountRef.current === data?.pages?.length
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
// fetch next page if we haven't gotten a full page of content
|
||||
let count = 0
|
||||
for (const page of data?.pages || []) {
|
||||
count += page.items.length
|
||||
}
|
||||
if (count < PAGE_SIZE && (data?.pages.length || 0) < 6) {
|
||||
query.fetchNextPage()
|
||||
lastPageCountRef.current = data?.pages?.length || 0
|
||||
}
|
||||
}, [query])
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, {useCallback, useEffect} from 'react'
|
||||
import React, {useCallback, useEffect, useRef} from 'react'
|
||||
import {
|
||||
AppBskyFeedDefs,
|
||||
AppBskyFeedPost,
|
||||
|
@ -78,6 +78,7 @@ export interface FeedPageUnselected {
|
|||
api: FeedAPI
|
||||
cursor: string | undefined
|
||||
feed: AppBskyFeedDefs.FeedViewPost[]
|
||||
fetchedAt: number
|
||||
}
|
||||
|
||||
export interface FeedPage {
|
||||
|
@ -85,6 +86,7 @@ export interface FeedPage {
|
|||
tuner: FeedTuner | NoopFeedTuner
|
||||
cursor: string | undefined
|
||||
slices: FeedPostSlice[]
|
||||
fetchedAt: number
|
||||
}
|
||||
|
||||
const PAGE_SIZE = 30
|
||||
|
@ -98,11 +100,12 @@ export function usePostFeedQuery(
|
|||
const feedTuners = useFeedTuners(feedDesc)
|
||||
const moderationOpts = useModerationOpts()
|
||||
const enabled = opts?.enabled !== false && Boolean(moderationOpts)
|
||||
const lastRun = React.useRef<{
|
||||
const lastRun = useRef<{
|
||||
data: InfiniteData<FeedPageUnselected>
|
||||
args: typeof selectArgs
|
||||
result: InfiniteData<FeedPage>
|
||||
} | null>(null)
|
||||
const lastPageCountRef = useRef(0)
|
||||
|
||||
// Make sure this doesn't invalidate unless really needed.
|
||||
const selectArgs = React.useMemo(
|
||||
|
@ -152,6 +155,7 @@ export function usePostFeedQuery(
|
|||
api,
|
||||
cursor: res.cursor,
|
||||
feed: res.feed,
|
||||
fetchedAt: Date.now(),
|
||||
}
|
||||
},
|
||||
initialPageParam: undefined,
|
||||
|
@ -214,6 +218,7 @@ export function usePostFeedQuery(
|
|||
api: page.api,
|
||||
tuner,
|
||||
cursor: page.cursor,
|
||||
fetchedAt: page.fetchedAt,
|
||||
slices: tuner
|
||||
.tune(page.feed)
|
||||
.map(slice => {
|
||||
|
@ -279,26 +284,28 @@ export function usePostFeedQuery(
|
|||
|
||||
useEffect(() => {
|
||||
const {isFetching, hasNextPage, data} = query
|
||||
if (isFetching || !hasNextPage) {
|
||||
return
|
||||
}
|
||||
|
||||
// avoid double-fires of fetchNextPage()
|
||||
if (
|
||||
lastPageCountRef.current !== 0 &&
|
||||
lastPageCountRef.current === data?.pages?.length
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
// fetch next page if we haven't gotten a full page of content
|
||||
let count = 0
|
||||
let numEmpties = 0
|
||||
for (const page of data?.pages || []) {
|
||||
if (page.slices.length === 0) {
|
||||
numEmpties++
|
||||
}
|
||||
for (const slice of page.slices) {
|
||||
count += slice.items.length
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!isFetching &&
|
||||
hasNextPage &&
|
||||
count < PAGE_SIZE &&
|
||||
numEmpties < 3 &&
|
||||
(data?.pages.length || 0) < 6
|
||||
) {
|
||||
if (count < PAGE_SIZE && (data?.pages.length || 0) < 6) {
|
||||
query.fetchNextPage()
|
||||
lastPageCountRef.current = data?.pages?.length || 0
|
||||
}
|
||||
}, [query])
|
||||
|
||||
|
|
|
@ -35,9 +35,7 @@ export function useProfileQuery({did}: {did: string | undefined}) {
|
|||
// if you remove it, the UI infinite-loops
|
||||
// -prf
|
||||
staleTime: isCurrentAccount ? STALE.SECONDS.THIRTY : STALE.MINUTES.FIVE,
|
||||
refetchInterval: isCurrentAccount
|
||||
? STALE.SECONDS.THIRTY
|
||||
: STALE.MINUTES.FIVE,
|
||||
refetchInterval: STALE.MINUTES.FIVE,
|
||||
queryKey: RQKEY(did || ''),
|
||||
queryFn: async () => {
|
||||
const res = await getAgent().getProfile({actor: did || ''})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue