feat: trust score filter

This commit is contained in:
codytseng 2025-12-31 18:22:23 +08:00
parent 43f4c34fb3
commit 5f785e5553
48 changed files with 974 additions and 480 deletions

View file

@ -33,42 +33,47 @@ export function NotificationProvider({ children }: { children: React.ReactNode }
const { current } = usePrimaryPage()
const active = useMemo(() => current === 'notifications', [current])
const { pubkey, notificationsSeenAt, updateNotificationsSeenAt } = useNostr()
const { hideUntrustedNotifications, isUserTrusted } = useUserTrust()
const { mutePubkeySet } = useMuteList()
const { meetsMinTrustScore } = useUserTrust()
const { hideContentMentioningMutedUsers } = useContentPolicy()
const [newNotifications, setNewNotifications] = useState<NostrEvent[]>([])
const [readNotificationIdSet, setReadNotificationIdSet] = useState<Set<string>>(new Set())
const filteredNewNotifications = useMemo(() => {
const [filteredNewNotifications, setFilteredNewNotifications] = useState<NostrEvent[]>([])
useEffect(() => {
if (active || notificationsSeenAt < 0) {
return []
setFilteredNewNotifications([])
return
}
const filtered: NostrEvent[] = []
for (const notification of newNotifications) {
if (notification.created_at <= notificationsSeenAt || filtered.length >= 10) {
break
}
if (
!notificationFilter(notification, {
pubkey,
mutePubkeySet,
hideContentMentioningMutedUsers,
hideUntrustedNotifications,
isUserTrusted
const filterNotifications = async () => {
const filtered: NostrEvent[] = []
await Promise.allSettled(
newNotifications.map(async (notification) => {
if (notification.created_at <= notificationsSeenAt || filtered.length >= 10) {
return
}
if (
!(await notificationFilter(notification, {
pubkey,
mutePubkeySet,
hideContentMentioningMutedUsers,
meetsMinTrustScore
}))
) {
return
}
filtered.push(notification)
})
) {
continue
}
filtered.push(notification)
)
setFilteredNewNotifications(filtered)
}
return filtered
filterNotifications()
}, [
newNotifications,
notificationsSeenAt,
mutePubkeySet,
hideContentMentioningMutedUsers,
hideUntrustedNotifications,
isUserTrusted,
active
meetsMinTrustScore
])
useEffect(() => {

View file

@ -5,14 +5,11 @@ import { createContext, useCallback, useContext, useEffect, useState } from 'rea
import { useNostr } from './NostrProvider'
type TUserTrustContext = {
hideUntrustedInteractions: boolean
hideUntrustedNotifications: boolean
hideUntrustedNotes: boolean
updateHideUntrustedInteractions: (hide: boolean) => void
updateHideUntrustedNotifications: (hide: boolean) => void
updateHideUntrustedNotes: (hide: boolean) => void
minTrustScore: number
updateMinTrustScore: (score: number) => void
isUserTrusted: (pubkey: string) => boolean
isSpammer: (pubkey: string) => Promise<boolean>
meetsMinTrustScore: (pubkey: string, minScore?: number) => Promise<boolean>
}
const UserTrustContext = createContext<TUserTrustContext | undefined>(undefined)
@ -29,15 +26,7 @@ const wotSet = new Set<string>()
export function UserTrustProvider({ children }: { children: React.ReactNode }) {
const { pubkey: currentPubkey } = useNostr()
const [hideUntrustedInteractions, setHideUntrustedInteractions] = useState(() =>
storage.getHideUntrustedInteractions()
)
const [hideUntrustedNotifications, setHideUntrustedNotifications] = useState(() =>
storage.getHideUntrustedNotifications()
)
const [hideUntrustedNotes, setHideUntrustedNotes] = useState(() =>
storage.getHideUntrustedNotes()
)
const [minTrustScore, setMinTrustScore] = useState(() => storage.getMinTrustScore())
useEffect(() => {
if (!currentPubkey) return
@ -81,32 +70,36 @@ export function UserTrustProvider({ children }: { children: React.ReactNode }) {
[isUserTrusted]
)
const updateHideUntrustedInteractions = (hide: boolean) => {
setHideUntrustedInteractions(hide)
storage.setHideUntrustedInteractions(hide)
const updateMinTrustScore = (score: number) => {
setMinTrustScore(score)
storage.setMinTrustScore(score)
}
const updateHideUntrustedNotifications = (hide: boolean) => {
setHideUntrustedNotifications(hide)
storage.setHideUntrustedNotifications(hide)
}
const meetsMinTrustScore = useCallback(
async (pubkey: string, minScore?: number) => {
const threshold = minScore !== undefined ? minScore : minTrustScore
if (threshold === 0) return true
if (pubkey === currentPubkey) return true
const updateHideUntrustedNotes = (hide: boolean) => {
setHideUntrustedNotes(hide)
storage.setHideUntrustedNotes(hide)
}
// WoT users always have 100% trust score
if (wotSet.has(pubkey)) return true
// Get percentile from reputation system
const percentile = await fayan.fetchUserPercentile(pubkey)
if (percentile === null) return true // If no data, indicate the trust server is down, so allow the user
return percentile >= threshold
},
[currentPubkey, minTrustScore]
)
return (
<UserTrustContext.Provider
value={{
hideUntrustedInteractions,
hideUntrustedNotifications,
hideUntrustedNotes,
updateHideUntrustedInteractions,
updateHideUntrustedNotifications,
updateHideUntrustedNotes,
minTrustScore,
updateMinTrustScore,
isUserTrusted,
isSpammer
isSpammer,
meetsMinTrustScore
}}
>
{children}