Filter mute words from notifications

This commit is contained in:
Eric Bailey
2024-10-03 17:22:04 -05:00
parent 939b07155b
commit 37e5154830
+86 -17
View File
@@ -17,7 +17,12 @@
*/ */
import {useCallback, useEffect, useMemo, useRef} from 'react' import {useCallback, useEffect, useMemo, useRef} from 'react'
import {AppBskyActorDefs, AppBskyFeedDefs, AtUri} from '@atproto/api' import {
AppBskyActorDefs,
AppBskyFeedDefs,
AppBskyFeedPost,
AtUri,
} from '@atproto/api'
import { import {
InfiniteData, InfiniteData,
QueryClient, QueryClient,
@@ -26,6 +31,7 @@ import {
useQueryClient, useQueryClient,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped'
import {useAgent} from '#/state/session' import {useAgent} from '#/state/session'
import {useThreadgateHiddenReplyUris} from '#/state/threadgate-hidden-replies' import {useThreadgateHiddenReplyUris} from '#/state/threadgate-hidden-replies'
import {useModerationOpts} from '../../preferences/moderation-opts' import {useModerationOpts} from '../../preferences/moderation-opts'
@@ -67,9 +73,15 @@ export function useNotificationFeedQuery(opts?: {
const selectArgs = useMemo(() => { const selectArgs = useMemo(() => {
return { return {
moderationOpts,
hiddenReplyUris, hiddenReplyUris,
} }
}, [hiddenReplyUris]) }, [moderationOpts, hiddenReplyUris])
const lastRun = useRef<{
data: InfiniteData<FeedPage>
args: typeof selectArgs
result: InfiniteData<FeedPage>
} | null>(null)
const query = useInfiniteQuery< const query = useInfiniteQuery<
FeedPage, FeedPage,
@@ -111,7 +123,38 @@ export function useNotificationFeedQuery(opts?: {
enabled, enabled,
select: useCallback( select: useCallback(
(data: InfiniteData<FeedPage>) => { (data: InfiniteData<FeedPage>) => {
const {hiddenReplyUris} = selectArgs const {moderationOpts, hiddenReplyUris} = 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
}
}
}
// override 'isRead' using the first page's returned seenAt // override 'isRead' using the first page's returned seenAt
// we do this because the `markAllRead()` call above will // we do this because the `markAllRead()` call above will
@@ -124,23 +167,49 @@ export function useNotificationFeedQuery(opts?: {
} }
} }
data = { const result = {
...data, ...data,
pages: data.pages.map(page => { pages: [
return { ...reusedPages,
...page, ...data.pages.slice(reusedPages.length).map(page => {
items: page.items.filter(item => { return {
const isHiddenReply = ...page,
item.type === 'reply' && items: page.items
item.subjectUri && .filter(item => {
hiddenReplyUris.has(item.subjectUri) const isHiddenReply =
return !isHiddenReply item.type === 'reply' &&
}), item.subjectUri &&
} hiddenReplyUris.has(item.subjectUri)
}), return !isHiddenReply
})
.filter(item => {
if (
item.type === 'reply' ||
item.type === 'mention' ||
item.type === 'quote'
) {
/*
* The `isPostView` check will fail here bc we don't have
* a `$type` field on the `subject`. But if the nested
* `record` is a post, we know it's a post view.
*/
if (AppBskyFeedPost.isRecord(item.subject?.record)) {
const mod = moderatePost(item.subject, moderationOpts!)
if (mod.ui('contentList').filter) {
return false
}
}
}
return true
}),
}
}),
],
} }
return data lastRun.current = {data, result, args: selectArgs}
return result
}, },
[selectArgs], [selectArgs],
), ),