Fetch enough pages to fill a page's worth of items (#4863)
* Fetch enough pages to fill a page's worth of items * Add failsafe in case of appview bugzio/stable
parent
70ffd387e3
commit
d2e88cc623
|
@ -59,7 +59,6 @@ export function useNotificationFeedQuery(opts?: {
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const unreads = useUnreadNotificationsApi()
|
const unreads = useUnreadNotificationsApi()
|
||||||
const enabled = opts?.enabled !== false
|
const enabled = opts?.enabled !== false
|
||||||
const lastPageCountRef = useRef(0)
|
|
||||||
const gate = useGate()
|
const gate = useGate()
|
||||||
|
|
||||||
// false: force showing all notifications
|
// false: force showing all notifications
|
||||||
|
@ -121,28 +120,52 @@ export function useNotificationFeedQuery(opts?: {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// The server may end up returning an empty page, a page with too few items,
|
||||||
|
// or a page with items that end up getting filtered out. When we fetch pages,
|
||||||
|
// we'll keep track of how many items we actually hope to see. If the server
|
||||||
|
// doesn't return enough items, we're going to continue asking for more items.
|
||||||
|
const lastItemCount = useRef(0)
|
||||||
|
const wantedItemCount = useRef(0)
|
||||||
|
const autoPaginationAttemptCount = useRef(0)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const {isFetching, hasNextPage, data} = query
|
const {data, isLoading, isRefetching, isFetchingNextPage, hasNextPage} =
|
||||||
if (isFetching || !hasNextPage) {
|
query
|
||||||
return
|
// Count the items that we already have.
|
||||||
}
|
let itemCount = 0
|
||||||
|
|
||||||
// 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
|
|
||||||
for (const page of data?.pages || []) {
|
for (const page of data?.pages || []) {
|
||||||
count += page.items.length
|
itemCount += page.items.length
|
||||||
}
|
}
|
||||||
if (count < PAGE_SIZE && (data?.pages.length || 0) < 6) {
|
|
||||||
query.fetchNextPage()
|
// If items got truncated, reset the state we're tracking below.
|
||||||
lastPageCountRef.current = data?.pages?.length || 0
|
if (itemCount !== lastItemCount.current) {
|
||||||
|
if (itemCount < lastItemCount.current) {
|
||||||
|
wantedItemCount.current = itemCount
|
||||||
|
}
|
||||||
|
lastItemCount.current = itemCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now track how many items we really want, and fetch more if needed.
|
||||||
|
if (isLoading || isRefetching) {
|
||||||
|
// During the initial fetch, we want to get an entire page's worth of items.
|
||||||
|
wantedItemCount.current = PAGE_SIZE
|
||||||
|
} else if (isFetchingNextPage) {
|
||||||
|
if (itemCount > wantedItemCount.current) {
|
||||||
|
// We have more items than wantedItemCount, so wantedItemCount must be out of date.
|
||||||
|
// Some other code must have called fetchNextPage(), for example, from onEndReached.
|
||||||
|
// Adjust the wantedItemCount to reflect that we want one more full page of items.
|
||||||
|
wantedItemCount.current = itemCount + PAGE_SIZE
|
||||||
|
}
|
||||||
|
} else if (hasNextPage) {
|
||||||
|
// At this point we're not fetching anymore, so it's time to make a decision.
|
||||||
|
// If we didn't receive enough items from the server, paginate again until we do.
|
||||||
|
if (itemCount < wantedItemCount.current) {
|
||||||
|
autoPaginationAttemptCount.current++
|
||||||
|
if (autoPaginationAttemptCount.current < 50 /* failsafe */) {
|
||||||
|
query.fetchNextPage()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
autoPaginationAttemptCount.current = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [query])
|
}, [query])
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,6 @@ export function usePostFeedQuery(
|
||||||
args: typeof selectArgs
|
args: typeof selectArgs
|
||||||
result: InfiniteData<FeedPage>
|
result: InfiniteData<FeedPage>
|
||||||
} | null>(null)
|
} | null>(null)
|
||||||
const lastPageCountRef = useRef(0)
|
|
||||||
const isDiscover = feedDesc.includes(DISCOVER_FEED_URI)
|
const isDiscover = feedDesc.includes(DISCOVER_FEED_URI)
|
||||||
|
|
||||||
// Make sure this doesn't invalidate unless really needed.
|
// Make sure this doesn't invalidate unless really needed.
|
||||||
|
@ -376,30 +375,54 @@ export function usePostFeedQuery(
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// The server may end up returning an empty page, a page with too few items,
|
||||||
|
// or a page with items that end up getting filtered out. When we fetch pages,
|
||||||
|
// we'll keep track of how many items we actually hope to see. If the server
|
||||||
|
// doesn't return enough items, we're going to continue asking for more items.
|
||||||
|
const lastItemCount = useRef(0)
|
||||||
|
const wantedItemCount = useRef(0)
|
||||||
|
const autoPaginationAttemptCount = useRef(0)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const {isFetching, hasNextPage, data} = query
|
const {data, isLoading, isRefetching, isFetchingNextPage, hasNextPage} =
|
||||||
if (isFetching || !hasNextPage) {
|
query
|
||||||
return
|
// Count the items that we already have.
|
||||||
}
|
let itemCount = 0
|
||||||
|
|
||||||
// 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
|
|
||||||
for (const page of data?.pages || []) {
|
for (const page of data?.pages || []) {
|
||||||
for (const slice of page.slices) {
|
for (const slice of page.slices) {
|
||||||
count += slice.items.length
|
itemCount += slice.items.length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (count < PAGE_SIZE && (data?.pages.length || 0) < 6) {
|
|
||||||
query.fetchNextPage()
|
// If items got truncated, reset the state we're tracking below.
|
||||||
lastPageCountRef.current = data?.pages?.length || 0
|
if (itemCount !== lastItemCount.current) {
|
||||||
|
if (itemCount < lastItemCount.current) {
|
||||||
|
wantedItemCount.current = itemCount
|
||||||
|
}
|
||||||
|
lastItemCount.current = itemCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now track how many items we really want, and fetch more if needed.
|
||||||
|
if (isLoading || isRefetching) {
|
||||||
|
// During the initial fetch, we want to get an entire page's worth of items.
|
||||||
|
wantedItemCount.current = PAGE_SIZE
|
||||||
|
} else if (isFetchingNextPage) {
|
||||||
|
if (itemCount > wantedItemCount.current) {
|
||||||
|
// We have more items than wantedItemCount, so wantedItemCount must be out of date.
|
||||||
|
// Some other code must have called fetchNextPage(), for example, from onEndReached.
|
||||||
|
// Adjust the wantedItemCount to reflect that we want one more full page of items.
|
||||||
|
wantedItemCount.current = itemCount + PAGE_SIZE
|
||||||
|
}
|
||||||
|
} else if (hasNextPage) {
|
||||||
|
// At this point we're not fetching anymore, so it's time to make a decision.
|
||||||
|
// If we didn't receive enough items from the server, paginate again until we do.
|
||||||
|
if (itemCount < wantedItemCount.current) {
|
||||||
|
autoPaginationAttemptCount.current++
|
||||||
|
if (autoPaginationAttemptCount.current < 50 /* failsafe */) {
|
||||||
|
query.fetchNextPage()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
autoPaginationAttemptCount.current = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [query])
|
}, [query])
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue