request count of library items only when needed
This commit is contained in:
@ -37,6 +37,7 @@ import {
|
||||
MutationSaveArticleReadingProgressArgs,
|
||||
MutationSetBookmarkArticleArgs,
|
||||
MutationSetFavoriteArticleArgs,
|
||||
PageInfo,
|
||||
PageType,
|
||||
QueryArticleArgs,
|
||||
QuerySearchArgs,
|
||||
@ -79,7 +80,8 @@ import {
|
||||
countLibraryItems,
|
||||
createOrUpdateLibraryItem,
|
||||
findLibraryItemsByPrefix,
|
||||
searchAndCountLibraryItems,
|
||||
SearchArgs,
|
||||
searchLibraryItems,
|
||||
softDeleteLibraryItem,
|
||||
sortParamsToSort,
|
||||
updateLibraryItem,
|
||||
@ -582,8 +584,15 @@ export const saveArticleReadingProgressResolver = authorized<
|
||||
|
||||
export type PartialLibraryItem = Merge<LibraryItem, { format?: string }>
|
||||
type PartialSearchItemEdge = Merge<SearchItemEdge, { node: PartialLibraryItem }>
|
||||
export type PartialPageInfo = Merge<
|
||||
PageInfo,
|
||||
{ searchLibraryItemArgs?: SearchArgs }
|
||||
>
|
||||
export const searchResolver = authorized<
|
||||
Merge<SearchSuccess, { edges: Array<PartialSearchItemEdge> }>,
|
||||
Merge<
|
||||
SearchSuccess,
|
||||
{ edges: Array<PartialSearchItemEdge>; pageInfo: PartialPageInfo }
|
||||
>,
|
||||
SearchError,
|
||||
QuerySearchArgs
|
||||
>(async (_obj, params, { uid }) => {
|
||||
@ -595,18 +604,17 @@ export const searchResolver = authorized<
|
||||
return { errorCodes: [SearchErrorCode.QueryTooLong] }
|
||||
}
|
||||
|
||||
const { libraryItems, count } = await searchAndCountLibraryItems(
|
||||
{
|
||||
from: Number(startCursor),
|
||||
size: first + 1, // fetch one more item to get next cursor
|
||||
includePending: true,
|
||||
includeContent: params.includeContent ?? true, // by default include content for offline use for now
|
||||
includeDeleted: params.query?.includes('in:trash'),
|
||||
query: params.query,
|
||||
useFolders: params.query?.includes('use:folders'),
|
||||
},
|
||||
uid
|
||||
)
|
||||
const searchLibraryItemArgs = {
|
||||
from: Number(startCursor),
|
||||
size: first + 1, // fetch one more item to get next cursor
|
||||
includePending: true,
|
||||
includeContent: params.includeContent ?? true, // by default include content for offline use for now
|
||||
includeDeleted: params.query?.includes('in:trash'),
|
||||
query: params.query,
|
||||
useFolders: params.query?.includes('use:folders'),
|
||||
}
|
||||
|
||||
const libraryItems = await searchLibraryItems(searchLibraryItemArgs, uid)
|
||||
|
||||
const start =
|
||||
startCursor && !isNaN(Number(startCursor)) ? Number(startCursor) : 0
|
||||
@ -631,7 +639,7 @@ export const searchResolver = authorized<
|
||||
startCursor,
|
||||
hasNextPage,
|
||||
endCursor,
|
||||
totalCount: count,
|
||||
searchLibraryItemArgs,
|
||||
},
|
||||
}
|
||||
})
|
||||
@ -675,7 +683,7 @@ export const updatesSinceResolver = authorized<
|
||||
folder ? ' in:' + folder : ''
|
||||
} sort:${sort.by}-${sort.order}`
|
||||
|
||||
const { libraryItems, count } = await searchAndCountLibraryItems(
|
||||
const libraryItems = await searchLibraryItems(
|
||||
{
|
||||
from: Number(startCursor),
|
||||
size: size + 1, // fetch one more item to get next cursor
|
||||
@ -714,7 +722,6 @@ export const updatesSinceResolver = authorized<
|
||||
startCursor,
|
||||
hasNextPage,
|
||||
endCursor,
|
||||
totalCount: count,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
@ -30,6 +30,7 @@ import {
|
||||
} from '../generated/graphql'
|
||||
import { getAISummary } from '../services/ai-summaries'
|
||||
import { findUserFeatures } from '../services/features'
|
||||
import { countLibraryItems } from '../services/library_item'
|
||||
import { Merge } from '../util'
|
||||
import { isBase64Image, validatedDate, wordsCount } from '../utils/helpers'
|
||||
import { createImageProxyUrl } from '../utils/imageproxy'
|
||||
@ -43,6 +44,7 @@ import {
|
||||
emptyTrashResolver,
|
||||
fetchContentResolver,
|
||||
PartialLibraryItem,
|
||||
PartialPageInfo,
|
||||
} from './article'
|
||||
import {
|
||||
addDiscoverFeedResolver,
|
||||
@ -588,6 +590,21 @@ export const functionResolvers = {
|
||||
highlightsCount: (item: LibraryItem) => item.highlightAnnotations?.length,
|
||||
...readingProgressHandlers,
|
||||
},
|
||||
PageInfo: {
|
||||
async totalCount(
|
||||
pageInfo: PartialPageInfo,
|
||||
_: unknown,
|
||||
ctx: ResolverContext
|
||||
) {
|
||||
if (pageInfo.totalCount) return pageInfo.totalCount
|
||||
|
||||
if (pageInfo.searchLibraryItemArgs && ctx.claims) {
|
||||
return countLibraryItems(pageInfo.searchLibraryItemArgs, ctx.claims.uid)
|
||||
}
|
||||
|
||||
return 0
|
||||
},
|
||||
},
|
||||
Subscription: {
|
||||
newsletterEmail(subscription: Subscription) {
|
||||
return subscription.newsletterEmail?.address
|
||||
|
||||
@ -18,7 +18,7 @@ export const recommendationFragment = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const GQL_SEARCH_QUERY = gql`
|
||||
export const gqlSearchQuery = (includeTotalCount = false) => gql`
|
||||
query Search(
|
||||
$after: String
|
||||
$first: Int
|
||||
@ -77,7 +77,7 @@ export const GQL_SEARCH_QUERY = gql`
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
totalCount
|
||||
totalCount @include(if: ${includeTotalCount})
|
||||
}
|
||||
}
|
||||
... on SearchError {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import {
|
||||
QueryClient,
|
||||
useInfiniteQuery,
|
||||
@ -6,25 +5,24 @@ import {
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import { gqlEndpoint } from '../../appConfig'
|
||||
import { ContentReader, PageType, State } from '../fragments/articleFragment'
|
||||
import { Highlight } from '../fragments/highlightFragment'
|
||||
import { requestHeaders } from '../networkHelpers'
|
||||
import { Label } from '../fragments/labelFragment'
|
||||
import { requestHeaders } from '../networkHelpers'
|
||||
import {
|
||||
gqlSearchQuery,
|
||||
GQL_BULK_ACTION,
|
||||
GQL_DELETE_LIBRARY_ITEM,
|
||||
GQL_GET_LIBRARY_ITEM,
|
||||
GQL_GET_LIBRARY_ITEM_CONTENT,
|
||||
GQL_MOVE_ITEM_TO_FOLDER,
|
||||
GQL_SAVE_ARTICLE_READING_PROGRESS,
|
||||
GQL_SAVE_URL,
|
||||
GQL_SEARCH_QUERY,
|
||||
GQL_SET_LABELS,
|
||||
GQL_SET_LINK_ARCHIVED,
|
||||
GQL_UPDATE_LIBRARY_ITEM,
|
||||
} from './gql'
|
||||
import { gqlEndpoint } from '../../appConfig'
|
||||
import { useState } from 'react'
|
||||
|
||||
function gqlFetcher(
|
||||
query: string,
|
||||
@ -213,7 +211,7 @@ export const insertItemInCache = (
|
||||
|
||||
export function useGetLibraryItems(
|
||||
folder: string | undefined,
|
||||
{ limit, searchQuery }: LibraryItemsQueryInput,
|
||||
{ limit, searchQuery, includeCount }: LibraryItemsQueryInput,
|
||||
enabled = true
|
||||
) {
|
||||
const fullQuery = folder
|
||||
@ -223,7 +221,7 @@ export function useGetLibraryItems(
|
||||
return useInfiniteQuery({
|
||||
queryKey: ['libraryItems', fullQuery],
|
||||
queryFn: async ({ pageParam }) => {
|
||||
const response = (await gqlFetcher(GQL_SEARCH_QUERY, {
|
||||
const response = (await gqlFetcher(gqlSearchQuery(includeCount), {
|
||||
after: pageParam,
|
||||
first: limit,
|
||||
query: fullQuery,
|
||||
@ -531,7 +529,7 @@ export function useRefreshProcessingItems() {
|
||||
itemIds: string[]
|
||||
}) => {
|
||||
const fullQuery = `in:all includes:${variables.itemIds.join(',')}`
|
||||
const result = (await gqlFetcher(GQL_SEARCH_QUERY, {
|
||||
const result = (await gqlFetcher(gqlSearchQuery(), {
|
||||
first: 10,
|
||||
query: fullQuery,
|
||||
includeContent: false,
|
||||
@ -945,6 +943,7 @@ export type LibraryItemsQueryInput = {
|
||||
searchQuery?: string
|
||||
cursor?: string
|
||||
includeContent?: boolean
|
||||
includeCount?: boolean
|
||||
}
|
||||
|
||||
type LibraryItemsData = {
|
||||
|
||||
@ -1,11 +1,4 @@
|
||||
import {
|
||||
ChangeEvent,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Toaster } from 'react-hot-toast'
|
||||
import { Button } from '../../components/elements/Button'
|
||||
import {
|
||||
@ -19,22 +12,22 @@ import { ConfirmationModal } from '../../components/patterns/ConfirmationModal'
|
||||
import { SettingsLayout } from '../../components/templates/SettingsLayout'
|
||||
import { styled, theme } from '../../components/tokens/stitches.config'
|
||||
import { userHasFeature } from '../../lib/featureFlag'
|
||||
import { useGetLibraryItems } from '../../lib/networking/library_items/useLibraryItems'
|
||||
import { emptyTrashMutation } from '../../lib/networking/mutations/emptyTrashMutation'
|
||||
import { optInFeature } from '../../lib/networking/mutations/optIntoFeatureMutation'
|
||||
import { scheduleDigest } from '../../lib/networking/mutations/scheduleDigest'
|
||||
import { updateDigestConfigMutation } from '../../lib/networking/mutations/updateDigestConfigMutation'
|
||||
import { updateEmailMutation } from '../../lib/networking/mutations/updateEmailMutation'
|
||||
import { updateUserMutation } from '../../lib/networking/mutations/updateUserMutation'
|
||||
import { updateUserProfileMutation } from '../../lib/networking/mutations/updateUserProfileMutation'
|
||||
import { useGetLibraryItems } from '../../lib/networking/library_items/useLibraryItems'
|
||||
import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery'
|
||||
import { useValidateUsernameQuery } from '../../lib/networking/queries/useValidateUsernameQuery'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
|
||||
import {
|
||||
DigestChannel,
|
||||
useGetUserPersonalization,
|
||||
} from '../../lib/networking/queries/useGetUserPersonalization'
|
||||
import { updateDigestConfigMutation } from '../../lib/networking/mutations/updateDigestConfigMutation'
|
||||
import { scheduleDigest } from '../../lib/networking/mutations/scheduleDigest'
|
||||
import { optInFeature } from '../../lib/networking/mutations/optIntoFeatureMutation'
|
||||
import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery'
|
||||
import { useValidateUsernameQuery } from '../../lib/networking/queries/useValidateUsernameQuery'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
|
||||
|
||||
const ACCOUNT_LIMIT = 50_000
|
||||
|
||||
@ -103,6 +96,7 @@ export default function Account(): JSX.Element {
|
||||
limit: 0,
|
||||
searchQuery: '',
|
||||
sortDescending: false,
|
||||
includeCount: true,
|
||||
})
|
||||
|
||||
const libraryCount = useMemo(() => {
|
||||
|
||||
@ -1,25 +1,23 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
|
||||
import { VStack } from '../../components/elements/LayoutPrimitives'
|
||||
|
||||
import { StyledText } from '../../components/elements/StyledText'
|
||||
import { ProfileLayout } from '../../components/templates/ProfileLayout'
|
||||
import { BulkAction } from '../../lib/networking/library_items/useLibraryItems'
|
||||
import { Button } from '../../components/elements/Button'
|
||||
import { theme } from '../../components/tokens/stitches.config'
|
||||
import { ConfirmationModal } from '../../components/patterns/ConfirmationModal'
|
||||
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
|
||||
import { useRouter } from 'next/router'
|
||||
import {
|
||||
useBulkActions,
|
||||
useGetLibraryItems,
|
||||
} from '../../lib/networking/library_items/useLibraryItems'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Button } from '../../components/elements/Button'
|
||||
import {
|
||||
BorderedFormInput,
|
||||
FormLabel,
|
||||
} from '../../components/elements/FormElements'
|
||||
import { VStack } from '../../components/elements/LayoutPrimitives'
|
||||
import { StyledText } from '../../components/elements/StyledText'
|
||||
import { ConfirmationModal } from '../../components/patterns/ConfirmationModal'
|
||||
import { ProfileLayout } from '../../components/templates/ProfileLayout'
|
||||
import { theme } from '../../components/tokens/stitches.config'
|
||||
import { DEFAULT_HOME_PATH } from '../../lib/navigations'
|
||||
import {
|
||||
BulkAction,
|
||||
useBulkActions,
|
||||
useGetLibraryItems,
|
||||
} from '../../lib/networking/library_items/useLibraryItems'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
|
||||
|
||||
type RunningState = 'none' | 'confirming' | 'running' | 'completed'
|
||||
|
||||
@ -39,6 +37,7 @@ export default function BulkPerformer(): JSX.Element {
|
||||
searchQuery: query,
|
||||
limit: 1,
|
||||
sortDescending: false,
|
||||
includeCount: true,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user