about summary refs log tree commit diff
path: root/src/state/queries/search-posts.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/state/queries/search-posts.ts')
-rw-r--r--src/state/queries/search-posts.ts85
1 files changed, 84 insertions, 1 deletions
diff --git a/src/state/queries/search-posts.ts b/src/state/queries/search-posts.ts
index 5c50ad267..8a8a3fa52 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,20 @@ export function useSearchPostsQuery({
   enabled?: boolean
 }) {
   const agent = useAgent()
+  const moderationOpts = useModerationOpts()
+  const selectArgs = React.useMemo(
+    () => ({
+      isSearchingSpecificUser: /from:(\w+)/.test(query),
+      moderationOpts,
+    }),
+    [query, moderationOpts],
+  )
+  const lastRun = React.useRef<{
+    data: InfiniteData<AppBskyFeedSearchPosts.OutputSchema>
+    args: typeof selectArgs
+    result: InfiniteData<AppBskyFeedSearchPosts.OutputSchema>
+  } | null>(null)
+
   return useInfiniteQuery<
     AppBskyFeedSearchPosts.OutputSchema,
     Error,
@@ -54,7 +71,73 @@ export function useSearchPostsQuery({
     },
     initialPageParam: undefined,
     getNextPageParam: lastPage => lastPage.cursor,
-    enabled,
+    enabled: enabled ?? !!moderationOpts,
+    select: React.useCallback(
+      (data: InfiniteData<AppBskyFeedSearchPosts.OutputSchema>) => {
+        const {moderationOpts, isSearchingSpecificUser} = selectArgs
+
+        /*
+         * If a user applies the `from:<user>` filter, don't apply any
+         * moderation. Note that if we add any more filtering logic below, we
+         * may need to adjust this.
+         */
+        if (isSearchingSpecificUser) {
+          return data
+        }
+
+        // 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],
+    ),
   })
 }