Fix pagination through empty pages
This commit is contained in:
@@ -1363,7 +1363,7 @@
|
|||||||
"count": 2
|
"count": 2
|
||||||
},
|
},
|
||||||
"typescript/no-floating-promises": {
|
"typescript/no-floating-promises": {
|
||||||
"count": 2
|
"count": 1
|
||||||
},
|
},
|
||||||
"typescript/no-unsafe-member-access": {
|
"typescript/no-unsafe-member-access": {
|
||||||
"count": 2
|
"count": 2
|
||||||
@@ -1398,7 +1398,7 @@
|
|||||||
"count": 3
|
"count": 3
|
||||||
},
|
},
|
||||||
"typescript/no-floating-promises": {
|
"typescript/no-floating-promises": {
|
||||||
"count": 3
|
"count": 2
|
||||||
},
|
},
|
||||||
"typescript/no-unsafe-member-access": {
|
"typescript/no-unsafe-member-access": {
|
||||||
"count": 3
|
"count": 3
|
||||||
@@ -1802,4 +1802,4 @@
|
|||||||
"count": 3
|
"count": 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export class CustomFeedAPI implements FeedAPI {
|
|||||||
res.data.feed = res.data.feed.slice(0, limit)
|
res.data.feed = res.data.feed.slice(0, limit)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
cursor: res.data.feed.length ? res.data.cursor : undefined,
|
cursor: res.data.cursor,
|
||||||
feed: res.data.feed,
|
feed: res.data.feed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,8 @@ export class LikesFeedAPI implements FeedAPI {
|
|||||||
limit,
|
limit,
|
||||||
})
|
})
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
// HACKFIX: the API incorrectly returns a cursor when there are no items -sfn
|
|
||||||
const isEmptyPage = res.data.feed.length === 0
|
|
||||||
return {
|
return {
|
||||||
cursor: isEmptyPage ? undefined : res.data.cursor,
|
cursor: res.data.cursor,
|
||||||
feed: res.data.feed,
|
feed: res.data.feed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,10 +213,9 @@ class MergeFeedSource {
|
|||||||
const res = await this._getFeed(this.cursor, n)
|
const res = await this._getFeed(this.cursor, n)
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
this.cursor = res.data.cursor
|
this.cursor = res.data.cursor
|
||||||
|
this.hasMore = Boolean(this.cursor)
|
||||||
if (res.data.feed.length) {
|
if (res.data.feed.length) {
|
||||||
this.queue = this.queue.concat(res.data.feed)
|
this.queue = this.queue.concat(res.data.feed)
|
||||||
} else {
|
|
||||||
this.hasMore = false
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.hasMore = false
|
this.hasMore = false
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
useQueryClient,
|
useQueryClient,
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {useAutoPagination} from '#/state/queries/util'
|
||||||
import {useAgent, useSession} from '#/state/session'
|
import {useAgent, useSession} from '#/state/session'
|
||||||
import * as Toast from '#/components/Toast'
|
import * as Toast from '#/components/Toast'
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ export const RQKEY_getNotificationDeclaration = ['notification-declaration']
|
|||||||
export function useActivitySubscriptionsQuery() {
|
export function useActivitySubscriptionsQuery() {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
|
|
||||||
return useInfiniteQuery({
|
const query = useInfiniteQuery({
|
||||||
queryKey: RQKEY_getActivitySubscriptions,
|
queryKey: RQKEY_getActivitySubscriptions,
|
||||||
queryFn: async ({pageParam}) => {
|
queryFn: async ({pageParam}) => {
|
||||||
const response =
|
const response =
|
||||||
@@ -34,6 +35,13 @@ export function useActivitySubscriptionsQuery() {
|
|||||||
initialPageParam: undefined as string | undefined,
|
initialPageParam: undefined as string | undefined,
|
||||||
getNextPageParam: prev => prev.cursor,
|
getNextPageParam: prev => prev.cursor,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce(
|
||||||
|
(count, page) => count + page.subscriptions.length,
|
||||||
|
0,
|
||||||
|
) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, 50)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useNotificationDeclarationQuery() {
|
export function useNotificationDeclarationQuery() {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
|
import {useAutoPagination} from '#/state/queries/util'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
|
|
||||||
export const RQKEY_ROOT = 'actor-search'
|
export const RQKEY_ROOT = 'actor-search'
|
||||||
@@ -29,7 +30,7 @@ export function useActorSearch({
|
|||||||
limit?: number
|
limit?: number
|
||||||
}) {
|
}) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
return useInfiniteQuery<
|
const result = useInfiniteQuery<
|
||||||
AppBskyActorSearchActors.OutputSchema,
|
AppBskyActorSearchActors.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyActorSearchActors.OutputSchema>,
|
InfiniteData<AppBskyActorSearchActors.OutputSchema>,
|
||||||
@@ -52,6 +53,11 @@ export function useActorSearch({
|
|||||||
placeholderData: maintainData ? keepPreviousData : undefined,
|
placeholderData: maintainData ? keepPreviousData : undefined,
|
||||||
select,
|
select,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
result.data?.pages.reduce((count, page) => count + page.actors.length, 0) ??
|
||||||
|
0
|
||||||
|
useAutoPagination(result, itemCount, limit)
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function select(data: InfiniteData<AppBskyActorSearchActors.OutputSchema>) {
|
function select(data: InfiniteData<AppBskyActorSearchActors.OutputSchema>) {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import {type QueryClient, useInfiniteQuery} from '@tanstack/react-query'
|
import {type QueryClient, useInfiniteQuery} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {useAutoPagination} from '#/state/queries/util'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
|
|
||||||
|
const PAGE_SIZE = 10
|
||||||
|
|
||||||
export const RQKEY_ROOT = 'actor-starter-packs'
|
export const RQKEY_ROOT = 'actor-starter-packs'
|
||||||
export const RQKEY_WITH_MEMBERSHIP_ROOT = 'actor-starter-packs-with-membership'
|
export const RQKEY_WITH_MEMBERSHIP_ROOT = 'actor-starter-packs-with-membership'
|
||||||
export const RQKEY = (did?: string) => [RQKEY_ROOT, did]
|
export const RQKEY = (did?: string) => [RQKEY_ROOT, did]
|
||||||
@@ -19,12 +22,12 @@ export function useActorStarterPacksQuery({
|
|||||||
}) {
|
}) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
|
|
||||||
return useInfiniteQuery({
|
const query = useInfiniteQuery({
|
||||||
queryKey: RQKEY(did),
|
queryKey: RQKEY(did),
|
||||||
queryFn: async ({pageParam}: {pageParam?: string}) => {
|
queryFn: async ({pageParam}: {pageParam?: string}) => {
|
||||||
const res = await agent.app.bsky.graph.getActorStarterPacks({
|
const res = await agent.app.bsky.graph.getActorStarterPacks({
|
||||||
actor: did!,
|
actor: did!,
|
||||||
limit: 10,
|
limit: PAGE_SIZE,
|
||||||
cursor: pageParam,
|
cursor: pageParam,
|
||||||
})
|
})
|
||||||
return res.data
|
return res.data
|
||||||
@@ -33,6 +36,13 @@ export function useActorStarterPacksQuery({
|
|||||||
initialPageParam: undefined,
|
initialPageParam: undefined,
|
||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce(
|
||||||
|
(count, page) => count + page.starterPacks.length,
|
||||||
|
0,
|
||||||
|
) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, PAGE_SIZE)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useActorStarterPacksWithMembershipsQuery({
|
export function useActorStarterPacksWithMembershipsQuery({
|
||||||
@@ -44,12 +54,12 @@ export function useActorStarterPacksWithMembershipsQuery({
|
|||||||
}) {
|
}) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
|
|
||||||
return useInfiniteQuery({
|
const query = useInfiniteQuery({
|
||||||
queryKey: RQKEY_WITH_MEMBERSHIP(did),
|
queryKey: RQKEY_WITH_MEMBERSHIP(did),
|
||||||
queryFn: async ({pageParam}: {pageParam?: string}) => {
|
queryFn: async ({pageParam}: {pageParam?: string}) => {
|
||||||
const res = await agent.app.bsky.graph.getStarterPacksWithMembership({
|
const res = await agent.app.bsky.graph.getStarterPacksWithMembership({
|
||||||
actor: did!,
|
actor: did!,
|
||||||
limit: 10,
|
limit: PAGE_SIZE,
|
||||||
cursor: pageParam,
|
cursor: pageParam,
|
||||||
})
|
})
|
||||||
return res.data
|
return res.data
|
||||||
@@ -58,6 +68,13 @@ export function useActorStarterPacksWithMembershipsQuery({
|
|||||||
initialPageParam: undefined,
|
initialPageParam: undefined,
|
||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce(
|
||||||
|
(count, page) => count + page.starterPacksWithMembership.length,
|
||||||
|
0,
|
||||||
|
) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, PAGE_SIZE)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function invalidateActorStarterPacksQuery({
|
export async function invalidateActorStarterPacksQuery({
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
didOrHandleUriMatches,
|
didOrHandleUriMatches,
|
||||||
embedViewRecordToPostView,
|
embedViewRecordToPostView,
|
||||||
getEmbeddedPost,
|
getEmbeddedPost,
|
||||||
|
useAutoPagination,
|
||||||
} from '#/state/queries/util'
|
} from '#/state/queries/util'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
import * as bsky from '#/types/bsky'
|
import * as bsky from '#/types/bsky'
|
||||||
@@ -25,7 +26,7 @@ export const createBookmarksQueryKey = () => [bookmarksQueryKeyRoot]
|
|||||||
export function useBookmarksQuery() {
|
export function useBookmarksQuery() {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
|
|
||||||
return useInfiniteQuery<
|
const query = useInfiniteQuery<
|
||||||
AppBskyBookmarkGetBookmarks.OutputSchema,
|
AppBskyBookmarkGetBookmarks.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>,
|
InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>,
|
||||||
@@ -42,6 +43,13 @@ export function useBookmarksQuery() {
|
|||||||
initialPageParam: undefined,
|
initialPageParam: undefined,
|
||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce(
|
||||||
|
(count, page) => count + page.bookmarks.length,
|
||||||
|
0,
|
||||||
|
) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, 50)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function truncateAndInvalidate(qc: QueryClient) {
|
export async function truncateAndInvalidate(qc: QueryClient) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
useQuery,
|
useQuery,
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {useAutoPagination} from '#/state/queries/util'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
import {type Match} from '#/components/contacts/state'
|
import {type Match} from '#/components/contacts/state'
|
||||||
import type * as bsky from '#/types/bsky'
|
import type * as bsky from '#/types/bsky'
|
||||||
@@ -32,7 +33,7 @@ export const findContactsGetMatchesQueryKey = [RQ_KEY_ROOT, 'matches']
|
|||||||
export function useContactsMatchesQuery() {
|
export function useContactsMatchesQuery() {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
|
|
||||||
return useInfiniteQuery({
|
const query = useInfiniteQuery({
|
||||||
queryKey: findContactsGetMatchesQueryKey,
|
queryKey: findContactsGetMatchesQueryKey,
|
||||||
queryFn: async ({pageParam}) => {
|
queryFn: async ({pageParam}) => {
|
||||||
const matches = await agent.app.bsky.contact.getMatches({
|
const matches = await agent.app.bsky.contact.getMatches({
|
||||||
@@ -44,6 +45,11 @@ export function useContactsMatchesQuery() {
|
|||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
staleTime: STALE.MINUTES.ONE,
|
staleTime: STALE.MINUTES.ONE,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce((count, page) => count + page.matches.length, 0) ??
|
||||||
|
0
|
||||||
|
useAutoPagination(query, itemCount, 50)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export function optimisticRemoveMatch(queryClient: QueryClient, did: string) {
|
export function optimisticRemoveMatch(queryClient: QueryClient, did: string) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
|
import {useAutoPagination} from '#/state/queries/util'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
|
|
||||||
const PAGE_SIZE = 30
|
const PAGE_SIZE = 30
|
||||||
@@ -25,7 +26,7 @@ export const RQKEY_ALL = (uri: string) => [RQKEY_ROOT_ALL, uri]
|
|||||||
|
|
||||||
export function useListMembersQuery(uri?: string, limit: number = PAGE_SIZE) {
|
export function useListMembersQuery(uri?: string, limit: number = PAGE_SIZE) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
return useInfiniteQuery<
|
const query = useInfiniteQuery<
|
||||||
AppBskyGraphGetList.OutputSchema,
|
AppBskyGraphGetList.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyGraphGetList.OutputSchema>,
|
InfiniteData<AppBskyGraphGetList.OutputSchema>,
|
||||||
@@ -46,6 +47,10 @@ export function useListMembersQuery(uri?: string, limit: number = PAGE_SIZE) {
|
|||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
enabled: Boolean(uri),
|
enabled: Boolean(uri),
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce((count, page) => count + page.items.length, 0) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, limit)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAllListMembersQuery(uri?: string) {
|
export function useAllListMembersQuery(uri?: string) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
useInfiniteQuery,
|
useInfiniteQuery,
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {createQueryKey} from '#/state/queries/util'
|
import {createQueryKey, useAutoPagination} from '#/state/queries/util'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
|
|
||||||
export type ListWithMembership =
|
export type ListWithMembership =
|
||||||
@@ -28,7 +28,7 @@ export function useListsWithMembershipQuery({
|
|||||||
}) {
|
}) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
|
|
||||||
return useInfiniteQuery<
|
const query = useInfiniteQuery<
|
||||||
AppBskyGraphGetListsWithMembership.OutputSchema,
|
AppBskyGraphGetListsWithMembership.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyGraphGetListsWithMembership.OutputSchema>,
|
InfiniteData<AppBskyGraphGetListsWithMembership.OutputSchema>,
|
||||||
@@ -48,6 +48,13 @@ export function useListsWithMembershipQuery({
|
|||||||
initialPageParam: undefined,
|
initialPageParam: undefined,
|
||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce(
|
||||||
|
(count, page) => count + page.listsWithMembership.length,
|
||||||
|
0,
|
||||||
|
) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, 50)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateListMembershipOptimistically({
|
export function updateListMembershipOptimistically({
|
||||||
|
|||||||
@@ -7,14 +7,16 @@ import {
|
|||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
|
import {useAutoPagination} from './util'
|
||||||
|
|
||||||
const RQKEY_ROOT = 'my-blocked-accounts'
|
const RQKEY_ROOT = 'my-blocked-accounts'
|
||||||
|
const PAGE_SIZE = 30
|
||||||
export const RQKEY = () => [RQKEY_ROOT]
|
export const RQKEY = () => [RQKEY_ROOT]
|
||||||
type RQPageParam = string | undefined
|
type RQPageParam = string | undefined
|
||||||
|
|
||||||
export function useMyBlockedAccountsQuery() {
|
export function useMyBlockedAccountsQuery() {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
return useInfiniteQuery<
|
const query = useInfiniteQuery<
|
||||||
AppBskyGraphGetBlocks.OutputSchema,
|
AppBskyGraphGetBlocks.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyGraphGetBlocks.OutputSchema>,
|
InfiniteData<AppBskyGraphGetBlocks.OutputSchema>,
|
||||||
@@ -24,7 +26,7 @@ export function useMyBlockedAccountsQuery() {
|
|||||||
queryKey: RQKEY(),
|
queryKey: RQKEY(),
|
||||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||||
const res = await agent.app.bsky.graph.getBlocks({
|
const res = await agent.app.bsky.graph.getBlocks({
|
||||||
limit: 30,
|
limit: PAGE_SIZE,
|
||||||
cursor: pageParam,
|
cursor: pageParam,
|
||||||
})
|
})
|
||||||
return res.data
|
return res.data
|
||||||
@@ -32,6 +34,11 @@ export function useMyBlockedAccountsQuery() {
|
|||||||
initialPageParam: undefined,
|
initialPageParam: undefined,
|
||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce((count, page) => count + page.blocks.length, 0) ??
|
||||||
|
0
|
||||||
|
useAutoPagination(query, itemCount, PAGE_SIZE)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* findAllProfilesInQueryData(
|
export function* findAllProfilesInQueryData(
|
||||||
|
|||||||
@@ -7,14 +7,16 @@ import {
|
|||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
|
import {useAutoPagination} from './util'
|
||||||
|
|
||||||
const RQKEY_ROOT = 'my-muted-accounts'
|
const RQKEY_ROOT = 'my-muted-accounts'
|
||||||
|
const PAGE_SIZE = 30
|
||||||
export const RQKEY = () => [RQKEY_ROOT]
|
export const RQKEY = () => [RQKEY_ROOT]
|
||||||
type RQPageParam = string | undefined
|
type RQPageParam = string | undefined
|
||||||
|
|
||||||
export function useMyMutedAccountsQuery() {
|
export function useMyMutedAccountsQuery() {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
return useInfiniteQuery<
|
const query = useInfiniteQuery<
|
||||||
AppBskyGraphGetMutes.OutputSchema,
|
AppBskyGraphGetMutes.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyGraphGetMutes.OutputSchema>,
|
InfiniteData<AppBskyGraphGetMutes.OutputSchema>,
|
||||||
@@ -24,7 +26,7 @@ export function useMyMutedAccountsQuery() {
|
|||||||
queryKey: RQKEY(),
|
queryKey: RQKEY(),
|
||||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||||
const res = await agent.app.bsky.graph.getMutes({
|
const res = await agent.app.bsky.graph.getMutes({
|
||||||
limit: 30,
|
limit: PAGE_SIZE,
|
||||||
cursor: pageParam,
|
cursor: pageParam,
|
||||||
})
|
})
|
||||||
return res.data
|
return res.data
|
||||||
@@ -32,6 +34,10 @@ export function useMyMutedAccountsQuery() {
|
|||||||
initialPageParam: undefined,
|
initialPageParam: undefined,
|
||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce((count, page) => count + page.mutes.length, 0) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, PAGE_SIZE)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* findAllProfilesInQueryData(
|
export function* findAllProfilesInQueryData(
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
* 3. Don't call this query's `refetch()` if you're trying to sync latest; call `checkUnread()` instead.
|
* 3. Don't call this query's `refetch()` if you're trying to sync latest; call `checkUnread()` instead.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {useCallback, useEffect, useMemo, useRef} from 'react'
|
import {useCallback, useMemo, useRef} from 'react'
|
||||||
import {
|
import {
|
||||||
AppBskyFeedDefs,
|
AppBskyFeedDefs,
|
||||||
AppBskyFeedPost,
|
AppBskyFeedPost,
|
||||||
@@ -40,6 +40,7 @@ import {
|
|||||||
didOrHandleUriMatches,
|
didOrHandleUriMatches,
|
||||||
embedViewRecordToPostView,
|
embedViewRecordToPostView,
|
||||||
getEmbeddedPost,
|
getEmbeddedPost,
|
||||||
|
useAutoPagination,
|
||||||
} from '../util'
|
} from '../util'
|
||||||
import {type FeedPage} from './types'
|
import {type FeedPage} from './types'
|
||||||
import {useUnreadNotificationsApi} from './unread'
|
import {useUnreadNotificationsApi} from './unread'
|
||||||
@@ -221,54 +222,9 @@ export function useNotificationFeedQuery(opts: {
|
|||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
// The server may end up returning an empty page, a page with too few items,
|
const itemCount =
|
||||||
// or a page with items that end up getting filtered out. When we fetch pages,
|
query.data?.pages.reduce((count, page) => count + page.items.length, 0) ?? 0
|
||||||
// we'll keep track of how many items we actually hope to see. If the server
|
useAutoPagination(query, itemCount, PAGE_SIZE)
|
||||||
// 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(() => {
|
|
||||||
const {data, isLoading, isRefetching, isFetchingNextPage, hasNextPage} =
|
|
||||||
query
|
|
||||||
// Count the items that we already have.
|
|
||||||
let itemCount = 0
|
|
||||||
for (const page of data?.pages || []) {
|
|
||||||
itemCount += page.items.length
|
|
||||||
}
|
|
||||||
|
|
||||||
// If items got truncated, reset the state we're tracking below.
|
|
||||||
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])
|
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {useCallback, useEffect, useMemo, useRef} from 'react'
|
import {useCallback, useMemo, useRef} from 'react'
|
||||||
import {AppState} from 'react-native'
|
import {AppState} from 'react-native'
|
||||||
import {
|
import {
|
||||||
type AppBskyActorDefs,
|
type AppBskyActorDefs,
|
||||||
@@ -43,6 +43,7 @@ import {
|
|||||||
didOrHandleUriMatches,
|
didOrHandleUriMatches,
|
||||||
embedViewRecordToPostView,
|
embedViewRecordToPostView,
|
||||||
getEmbeddedPost,
|
getEmbeddedPost,
|
||||||
|
useAutoPagination,
|
||||||
} from './util'
|
} from './util'
|
||||||
|
|
||||||
type ActorDid = string
|
type ActorDid = string
|
||||||
@@ -362,56 +363,13 @@ export function usePostFeedQuery(
|
|||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
// The server may end up returning an empty page, a page with too few items,
|
let itemCount = 0
|
||||||
// or a page with items that end up getting filtered out. When we fetch pages,
|
for (const page of query.data?.pages || []) {
|
||||||
// we'll keep track of how many items we actually hope to see. If the server
|
for (const slice of page.slices) {
|
||||||
// doesn't return enough items, we're going to continue asking for more items.
|
itemCount += slice.items.length
|
||||||
const lastItemCount = useRef(0)
|
|
||||||
const wantedItemCount = useRef(0)
|
|
||||||
const autoPaginationAttemptCount = useRef(0)
|
|
||||||
useEffect(() => {
|
|
||||||
const {data, isLoading, isRefetching, isFetchingNextPage, hasNextPage} =
|
|
||||||
query
|
|
||||||
// Count the items that we already have.
|
|
||||||
let itemCount = 0
|
|
||||||
for (const page of data?.pages || []) {
|
|
||||||
for (const slice of page.slices) {
|
|
||||||
itemCount += slice.items.length
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// If items got truncated, reset the state we're tracking below.
|
useAutoPagination(query, itemCount, MIN_POSTS)
|
||||||
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 = MIN_POSTS
|
|
||||||
} 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 + MIN_POSTS
|
|
||||||
}
|
|
||||||
} 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])
|
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
import {createQueryKey} from '#/state/queries/util'
|
import {createQueryKey, useAutoPagination} from '#/state/queries/util'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
|
|
||||||
const PAGE_SIZE = 30
|
const PAGE_SIZE = 30
|
||||||
@@ -20,7 +20,7 @@ export const RQKEY = (resolvedUri: string) => [RQKEY_ROOT, resolvedUri]
|
|||||||
|
|
||||||
export function useLikedByQuery(resolvedUri: string | undefined) {
|
export function useLikedByQuery(resolvedUri: string | undefined) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
return useInfiniteQuery<
|
const query = useInfiniteQuery<
|
||||||
AppBskyFeedGetLikes.OutputSchema,
|
AppBskyFeedGetLikes.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyFeedGetLikes.OutputSchema>,
|
InfiniteData<AppBskyFeedGetLikes.OutputSchema>,
|
||||||
@@ -40,6 +40,10 @@ export function useLikedByQuery(resolvedUri: string | undefined) {
|
|||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
enabled: !!resolvedUri,
|
enabled: !!resolvedUri,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce((count, page) => count + page.likes.length, 0) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, PAGE_SIZE)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
didOrHandleUriMatches,
|
didOrHandleUriMatches,
|
||||||
embedViewRecordToPostView,
|
embedViewRecordToPostView,
|
||||||
getEmbeddedPost,
|
getEmbeddedPost,
|
||||||
|
useAutoPagination,
|
||||||
} from './util'
|
} from './util'
|
||||||
|
|
||||||
const PAGE_SIZE = 30
|
const PAGE_SIZE = 30
|
||||||
@@ -27,7 +28,7 @@ export const RQKEY = (resolvedUri: string) => [RQKEY_ROOT, resolvedUri]
|
|||||||
|
|
||||||
export function usePostQuotesQuery(resolvedUri: string | undefined) {
|
export function usePostQuotesQuery(resolvedUri: string | undefined) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
return useInfiniteQuery<
|
const query = useInfiniteQuery<
|
||||||
AppBskyFeedGetQuotes.OutputSchema,
|
AppBskyFeedGetQuotes.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyFeedGetQuotes.OutputSchema>,
|
InfiniteData<AppBskyFeedGetQuotes.OutputSchema>,
|
||||||
@@ -65,6 +66,10 @@ export function usePostQuotesQuery(resolvedUri: string | undefined) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce((count, page) => count + page.posts.length, 0) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, PAGE_SIZE)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* findAllProfilesInQueryData(
|
export function* findAllProfilesInQueryData(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
|
import {useAutoPagination} from './util'
|
||||||
|
|
||||||
const PAGE_SIZE = 30
|
const PAGE_SIZE = 30
|
||||||
type RQPageParam = string | undefined
|
type RQPageParam = string | undefined
|
||||||
@@ -20,7 +21,7 @@ export const RQKEY = (resolvedUri: string) => [RQKEY_ROOT, resolvedUri]
|
|||||||
|
|
||||||
export function usePostRepostedByQuery(resolvedUri: string | undefined) {
|
export function usePostRepostedByQuery(resolvedUri: string | undefined) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
return useInfiniteQuery<
|
const query = useInfiniteQuery<
|
||||||
AppBskyFeedGetRepostedBy.OutputSchema,
|
AppBskyFeedGetRepostedBy.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyFeedGetRepostedBy.OutputSchema>,
|
InfiniteData<AppBskyFeedGetRepostedBy.OutputSchema>,
|
||||||
@@ -40,6 +41,13 @@ export function usePostRepostedByQuery(resolvedUri: string | undefined) {
|
|||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
enabled: !!resolvedUri,
|
enabled: !!resolvedUri,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce(
|
||||||
|
(count, page) => count + page.repostedBy.length,
|
||||||
|
0,
|
||||||
|
) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, PAGE_SIZE)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* findAllProfilesInQueryData(
|
export function* findAllProfilesInQueryData(
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
useInfiniteQuery,
|
useInfiniteQuery,
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {useAutoPagination} from '#/state/queries/util'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
import {useModerationOpts} from '../preferences/moderation-opts'
|
import {useModerationOpts} from '../preferences/moderation-opts'
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ export function useProfileFeedgensQuery(
|
|||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const enabled = opts?.enabled !== false && Boolean(moderationOpts)
|
const enabled = opts?.enabled !== false && Boolean(moderationOpts)
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
return useInfiniteQuery<
|
const query = useInfiniteQuery<
|
||||||
AppBskyFeedGetActorFeeds.OutputSchema,
|
AppBskyFeedGetActorFeeds.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyFeedGetActorFeeds.OutputSchema>,
|
InfiniteData<AppBskyFeedGetActorFeeds.OutputSchema>,
|
||||||
@@ -66,4 +67,8 @@ export function useProfileFeedgensQuery(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce((count, page) => count + page.feeds.length, 0) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, PAGE_SIZE)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
|
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
|
import {useAutoPagination} from './util'
|
||||||
|
|
||||||
const DEFAULT_SORT = 'latest'
|
const DEFAULT_SORT = 'latest'
|
||||||
const PAGE_SIZE = 30
|
const PAGE_SIZE = 30
|
||||||
@@ -37,7 +38,7 @@ export function useProfileFollowersQuery(
|
|||||||
|
|
||||||
const sortParam = isSortEnabled ? sort || DEFAULT_SORT : undefined
|
const sortParam = isSortEnabled ? sort || DEFAULT_SORT : undefined
|
||||||
|
|
||||||
return useInfiniteQuery<
|
const query = useInfiniteQuery<
|
||||||
AppBskyGraphGetFollowers.OutputSchema,
|
AppBskyGraphGetFollowers.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyGraphGetFollowers.OutputSchema>,
|
InfiniteData<AppBskyGraphGetFollowers.OutputSchema>,
|
||||||
@@ -58,6 +59,13 @@ export function useProfileFollowersQuery(
|
|||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
enabled: !!did,
|
enabled: !!did,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce(
|
||||||
|
(count, page) => count + page.followers.length,
|
||||||
|
0,
|
||||||
|
) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, PAGE_SIZE)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* findAllProfilesInQueryData(
|
export function* findAllProfilesInQueryData(
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
|
import {useAutoPagination} from './util'
|
||||||
|
|
||||||
const DEFAULT_SORT = 'latest'
|
const DEFAULT_SORT = 'latest'
|
||||||
const PAGE_SIZE = 30
|
const PAGE_SIZE = 30
|
||||||
@@ -38,7 +39,7 @@ export function useProfileFollowsQuery(
|
|||||||
|
|
||||||
const sortParam = isSortEnabled ? sort || DEFAULT_SORT : undefined
|
const sortParam = isSortEnabled ? sort || DEFAULT_SORT : undefined
|
||||||
|
|
||||||
return useInfiniteQuery<
|
const query = useInfiniteQuery<
|
||||||
AppBskyGraphGetFollows.OutputSchema,
|
AppBskyGraphGetFollows.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyGraphGetFollows.OutputSchema>,
|
InfiniteData<AppBskyGraphGetFollows.OutputSchema>,
|
||||||
@@ -60,6 +61,11 @@ export function useProfileFollowsQuery(
|
|||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
enabled: !!did,
|
enabled: !!did,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce((count, page) => count + page.follows.length, 0) ??
|
||||||
|
0
|
||||||
|
useAutoPagination(query, itemCount, limit || PAGE_SIZE)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* findAllProfilesInQueryData(
|
export function* findAllProfilesInQueryData(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
useInfiniteQuery,
|
useInfiniteQuery,
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {useAutoPagination} from '#/state/queries/util'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
import {useModerationOpts} from '../preferences/moderation-opts'
|
import {useModerationOpts} from '../preferences/moderation-opts'
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ export function useProfileListsQuery(did: string, opts?: {enabled?: boolean}) {
|
|||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const enabled = opts?.enabled !== false && Boolean(moderationOpts)
|
const enabled = opts?.enabled !== false && Boolean(moderationOpts)
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
return useInfiniteQuery<
|
const query = useInfiniteQuery<
|
||||||
AppBskyGraphGetLists.OutputSchema,
|
AppBskyGraphGetLists.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyGraphGetLists.OutputSchema>,
|
InfiniteData<AppBskyGraphGetLists.OutputSchema>,
|
||||||
@@ -55,4 +56,8 @@ export function useProfileListsQuery(did: string, opts?: {enabled?: boolean}) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
query.data?.pages.reduce((count, page) => count + page.lists.length, 0) ?? 0
|
||||||
|
useAutoPagination(query, itemCount, PAGE_SIZE)
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
didOrHandleUriMatches,
|
didOrHandleUriMatches,
|
||||||
embedViewRecordToPostView,
|
embedViewRecordToPostView,
|
||||||
getEmbeddedPost,
|
getEmbeddedPost,
|
||||||
|
useAutoPagination,
|
||||||
} from './util'
|
} from './util'
|
||||||
|
|
||||||
const searchPostsQueryKeyRoot = 'search-posts'
|
const searchPostsQueryKeyRoot = 'search-posts'
|
||||||
@@ -64,7 +65,7 @@ export function useSearchPostsV2Query({
|
|||||||
result: InfiniteData<AppBskyFeedSearchPostsV2.OutputSchema>
|
result: InfiniteData<AppBskyFeedSearchPostsV2.OutputSchema>
|
||||||
} | null>(null)
|
} | null>(null)
|
||||||
|
|
||||||
return useInfiniteQuery<
|
const result = useInfiniteQuery<
|
||||||
AppBskyFeedSearchPostsV2.OutputSchema,
|
AppBskyFeedSearchPostsV2.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyFeedSearchPostsV2.OutputSchema>,
|
InfiniteData<AppBskyFeedSearchPostsV2.OutputSchema>,
|
||||||
@@ -170,6 +171,11 @@ export function useSearchPostsV2Query({
|
|||||||
[selectArgs],
|
[selectArgs],
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
const uris = new Set(
|
||||||
|
result.data?.pages.flatMap(page => page.posts.map(post => post.uri)),
|
||||||
|
)
|
||||||
|
useAutoPagination(result, uris.size, 25)
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* findAllPostsInQueryData(
|
export function* findAllPostsInQueryData(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
|
import {useAutoPagination} from '#/state/queries/util'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
|
|
||||||
export const RQKEY_ROOT = 'starter-pack-search'
|
export const RQKEY_ROOT = 'starter-pack-search'
|
||||||
@@ -28,7 +29,7 @@ export function useStarterPackSearch({
|
|||||||
limit?: number
|
limit?: number
|
||||||
}) {
|
}) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
return useInfiniteQuery<
|
const result = useInfiniteQuery<
|
||||||
AppBskyGraphSearchStarterPacksV2.OutputSchema,
|
AppBskyGraphSearchStarterPacksV2.OutputSchema,
|
||||||
Error,
|
Error,
|
||||||
InfiniteData<AppBskyGraphSearchStarterPacksV2.OutputSchema>,
|
InfiniteData<AppBskyGraphSearchStarterPacksV2.OutputSchema>,
|
||||||
@@ -51,6 +52,13 @@ export function useStarterPackSearch({
|
|||||||
placeholderData: maintainData ? keepPreviousData : undefined,
|
placeholderData: maintainData ? keepPreviousData : undefined,
|
||||||
select,
|
select,
|
||||||
})
|
})
|
||||||
|
const itemCount =
|
||||||
|
result.data?.pages.reduce(
|
||||||
|
(count, page) => count + page.starterPacks.length,
|
||||||
|
0,
|
||||||
|
) ?? 0
|
||||||
|
useAutoPagination(result, itemCount, limit)
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function select(
|
function select(
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import {renderHook} from '@testing-library/react-native'
|
||||||
|
|
||||||
|
import {useAutoPagination} from './util'
|
||||||
|
|
||||||
|
function query(overrides: Record<string, unknown> = {}) {
|
||||||
|
return {
|
||||||
|
data: {pageParams: [undefined]},
|
||||||
|
isLoading: false,
|
||||||
|
isRefetching: false,
|
||||||
|
isFetchingNextPage: false,
|
||||||
|
hasNextPage: true,
|
||||||
|
fetchNextPage: jest.fn().mockResolvedValue(undefined),
|
||||||
|
...overrides,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('useAutoPagination', () => {
|
||||||
|
it('fetches another page when visible items are missing', () => {
|
||||||
|
const value = query()
|
||||||
|
|
||||||
|
renderHook(() => useAutoPagination(value, 0, 10))
|
||||||
|
|
||||||
|
expect(value.fetchNextPage).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('stops when the requested number of items is visible', () => {
|
||||||
|
const value = query()
|
||||||
|
|
||||||
|
renderHook(() => useAutoPagination(value, 10, 10))
|
||||||
|
|
||||||
|
expect(value.fetchNextPage).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('stops when the server repeats a cursor', () => {
|
||||||
|
const value = query({data: {pageParams: [undefined, 'a', 'a']}})
|
||||||
|
|
||||||
|
renderHook(() => useAutoPagination(value, 0, 10))
|
||||||
|
|
||||||
|
expect(value.fetchNextPage).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import {useEffect, useRef} from 'react'
|
||||||
import {
|
import {
|
||||||
type AppBskyActorDefs,
|
type AppBskyActorDefs,
|
||||||
AppBskyEmbedRecord,
|
AppBskyEmbedRecord,
|
||||||
@@ -14,6 +15,57 @@ import {
|
|||||||
|
|
||||||
import * as bsky from '#/types/bsky'
|
import * as bsky from '#/types/bsky'
|
||||||
|
|
||||||
|
type AutoPaginationQuery = {
|
||||||
|
data?: {pageParams: unknown[]}
|
||||||
|
isLoading: boolean
|
||||||
|
isRefetching: boolean
|
||||||
|
isFetchingNextPage: boolean
|
||||||
|
hasNextPage: boolean
|
||||||
|
fetchNextPage: () => Promise<unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAutoPagination(
|
||||||
|
query: AutoPaginationQuery,
|
||||||
|
itemCount: number,
|
||||||
|
pageSize: number,
|
||||||
|
) {
|
||||||
|
const lastItemCount = useRef(0)
|
||||||
|
const wantedItemCount = useRef(pageSize)
|
||||||
|
const attemptCount = useRef(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (itemCount !== lastItemCount.current) {
|
||||||
|
if (itemCount < lastItemCount.current) {
|
||||||
|
wantedItemCount.current = itemCount
|
||||||
|
}
|
||||||
|
lastItemCount.current = itemCount
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.isLoading || query.isRefetching) {
|
||||||
|
wantedItemCount.current = pageSize
|
||||||
|
} else if (query.isFetchingNextPage) {
|
||||||
|
if (itemCount > wantedItemCount.current) {
|
||||||
|
wantedItemCount.current = itemCount + pageSize
|
||||||
|
}
|
||||||
|
} else if (query.hasNextPage) {
|
||||||
|
if (itemCount < wantedItemCount.current) {
|
||||||
|
const pageParams = query.data?.pageParams
|
||||||
|
const repeatedCursor =
|
||||||
|
pageParams &&
|
||||||
|
pageParams.length > 1 &&
|
||||||
|
Object.is(pageParams.at(-1), pageParams.at(-2))
|
||||||
|
if (repeatedCursor) return
|
||||||
|
attemptCount.current++
|
||||||
|
if (attemptCount.current < 50) {
|
||||||
|
void query.fetchNextPage()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attemptCount.current = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [itemCount, pageSize, query])
|
||||||
|
}
|
||||||
|
|
||||||
export type StructuredQueryKey<T extends Record<string, unknown>> = readonly [
|
export type StructuredQueryKey<T extends Record<string, unknown>> = readonly [
|
||||||
string,
|
string,
|
||||||
T,
|
T,
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export function ProfileFeedgens({
|
|||||||
error,
|
error,
|
||||||
refetch,
|
refetch,
|
||||||
} = useProfileFeedgensQuery(did, opts)
|
} = useProfileFeedgensQuery(did, opts)
|
||||||
const isEmpty = !isPending && !data?.pages[0]?.feeds.length
|
const isEmpty = !isPending && !data?.pages.some(page => page.feeds.length)
|
||||||
const {data: preferences} = usePreferencesQuery()
|
const {data: preferences} = usePreferencesQuery()
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export function ListMembers({
|
|||||||
hasNextPage,
|
hasNextPage,
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
} = useListMembersQuery(list)
|
} = useListMembersQuery(list)
|
||||||
const isEmpty = !isFetching && !data?.pages[0].items.length
|
const isEmpty = !isFetching && !data?.pages.some(page => page.items.length)
|
||||||
const isOwner =
|
const isOwner =
|
||||||
currentAccount && data?.pages[0].list.creator.did === currentAccount.did
|
currentAccount && data?.pages[0].list.creator.did === currentAccount.did
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export function ProfileLists({
|
|||||||
error,
|
error,
|
||||||
refetch,
|
refetch,
|
||||||
} = useProfileListsQuery(did, opts)
|
} = useProfileListsQuery(did, opts)
|
||||||
const isEmpty = !isPending && !data?.pages[0]?.lists.length
|
const isEmpty = !isPending && !data?.pages.some(page => page.lists.length)
|
||||||
const {data: preferences} = usePreferencesQuery()
|
const {data: preferences} = usePreferencesQuery()
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export function ModerationBlockedAccounts({}: Props) {
|
|||||||
fetchNextPage,
|
fetchNextPage,
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
} = useMyBlockedAccountsQuery()
|
} = useMyBlockedAccountsQuery()
|
||||||
const isEmpty = !isFetching && !data?.pages[0]?.blocks.length
|
const isEmpty = !isFetching && !data?.pages.some(page => page.blocks.length)
|
||||||
const profiles = useMemo(() => {
|
const profiles = useMemo(() => {
|
||||||
if (data?.pages) {
|
if (data?.pages) {
|
||||||
return data.pages.flatMap(page => page.blocks)
|
return data.pages.flatMap(page => page.blocks)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export function ModerationMutedAccounts({}: Props) {
|
|||||||
fetchNextPage,
|
fetchNextPage,
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
} = useMyMutedAccountsQuery()
|
} = useMyMutedAccountsQuery()
|
||||||
const isEmpty = !isFetching && !data?.pages[0]?.mutes.length
|
const isEmpty = !isFetching && !data?.pages.some(page => page.mutes.length)
|
||||||
const profiles = useMemo(() => {
|
const profiles = useMemo(() => {
|
||||||
if (data?.pages) {
|
if (data?.pages) {
|
||||||
return data.pages.flatMap(page => page.mutes)
|
return data.pages.flatMap(page => page.mutes)
|
||||||
|
|||||||
Reference in New Issue
Block a user