diff --git a/src/state/queries/search-posts.ts b/src/state/queries/search-posts.ts index 5c50ad2671..b3bc721063 100644 --- a/src/state/queries/search-posts.ts +++ b/src/state/queries/search-posts.ts @@ -1,3 +1,4 @@ +import React from 'react' import { AppBskyActorDefs, AppBskyFeedDefs, @@ -11,6 +12,8 @@ import { useInfiniteQuery, } from '@tanstack/react-query' +import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped' +import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useAgent} from '#/state/session' import { didOrHandleUriMatches, @@ -35,6 +38,19 @@ export function useSearchPostsQuery({ enabled?: boolean }) { const agent = useAgent() + const moderationOpts = useModerationOpts() + const selectArgs = React.useMemo( + () => ({ + moderationOpts, + }), + [moderationOpts], + ) + const lastRun = React.useRef<{ + data: InfiniteData + args: typeof selectArgs + result: InfiniteData + } | null>(null) + return useInfiniteQuery< AppBskyFeedSearchPosts.OutputSchema, Error, @@ -54,7 +70,64 @@ export function useSearchPostsQuery({ }, initialPageParam: undefined, getNextPageParam: lastPage => lastPage.cursor, - enabled, + enabled: enabled ?? !!moderationOpts, + select: React.useCallback( + (data: InfiniteData) => { + const {moderationOpts} = selectArgs + + // Keep track of the last run and whether we can reuse + // some already selected pages from there. + let reusedPages = [] + if (lastRun.current) { + const { + data: lastData, + args: lastArgs, + result: lastResult, + } = lastRun.current + let canReuse = true + for (let key in selectArgs) { + if (selectArgs.hasOwnProperty(key)) { + if ((selectArgs as any)[key] !== (lastArgs as any)[key]) { + // Can't do reuse anything if any input has changed. + canReuse = false + break + } + } + } + if (canReuse) { + for (let i = 0; i < data.pages.length; i++) { + if (data.pages[i] && lastData.pages[i] === data.pages[i]) { + reusedPages.push(lastResult.pages[i]) + continue + } + // Stop as soon as pages stop matching up. + break + } + } + } + + const result = { + ...data, + pages: [ + ...reusedPages, + ...data.pages.slice(reusedPages.length).map(page => { + return { + ...page, + posts: page.posts.filter(post => { + const mod = moderatePost(post, moderationOpts!) + return !mod.ui('contentList').filter + }), + } + }), + ], + } + + lastRun.current = {data, result, args: selectArgs} + + return result + }, + [selectArgs], + ), }) }