Filter posts containing mute words from search and notifications (#5599)

* Filter mute words from search

* Filter mute words from notifications

* Do no filter search if using from filter
This commit is contained in:
Eric Bailey
2024-10-08 13:22:37 -05:00
committed by GitHub
parent fc82d2f6d5
commit 1db39ed1d1
2 changed files with 170 additions and 18 deletions
+84 -1
View File
@@ -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],
),
})
}