From c17d1b8ab5571fb3c808cc1a58c9481b64e41d94 Mon Sep 17 00:00:00 2001 From: codytseng Date: Sun, 1 Jun 2025 23:00:01 +0800 Subject: [PATCH] feat: hide notifications from untrusted users --- src/components/NotificationList/index.tsx | 8 ++++++-- src/constants.ts | 2 +- src/i18n/locales/ar.ts | 5 ++++- src/i18n/locales/de.ts | 6 +++++- src/i18n/locales/en.ts | 5 ++++- src/i18n/locales/es.ts | 5 ++++- src/i18n/locales/fr.ts | 5 ++++- src/i18n/locales/it.ts | 5 ++++- src/i18n/locales/ja.ts | 5 ++++- src/i18n/locales/pl.ts | 5 ++++- src/i18n/locales/pt-BR.ts | 5 ++++- src/i18n/locales/pt-PT.ts | 5 ++++- src/i18n/locales/ru.ts | 5 ++++- src/i18n/locales/zh.ts | 5 ++++- .../secondary/GeneralSettingsPage/index.tsx | 14 +++++++------- src/providers/NotificationProvider.tsx | 8 +++++++- src/providers/UserTrustProvider.tsx | 4 ++-- src/services/local-storage.service.ts | 17 +++++++++-------- 18 files changed, 81 insertions(+), 33 deletions(-) diff --git a/src/components/NotificationList/index.tsx b/src/components/NotificationList/index.tsx index 605fe6ca..e7b5fd3c 100644 --- a/src/components/NotificationList/index.tsx +++ b/src/components/NotificationList/index.tsx @@ -5,6 +5,7 @@ import { usePrimaryPage } from '@/PageManager' import { useNostr } from '@/providers/NostrProvider' import { useNoteStats } from '@/providers/NoteStatsProvider' import { useNotification } from '@/providers/NotificationProvider' +import { useUserTrust } from '@/providers/UserTrustProvider' import client from '@/services/client.service' import { TNotificationType } from '@/types' import dayjs from 'dayjs' @@ -22,6 +23,7 @@ const NotificationList = forwardRef((_, ref) => { const { t } = useTranslation() const { current } = usePrimaryPage() const { pubkey } = useNostr() + const { enabled: hideUntrustedEvents, isUserTrusted } = useUserTrust() const { clearNewNotifications, getNotificationsSeenAt } = useNotification() const { updateNoteStatsByEvents } = useNoteStats() const [notificationType, setNotificationType] = useState('all') @@ -122,7 +124,9 @@ const NotificationList = forwardRef((_, ref) => { }, [pubkey, refreshCount, filterKinds, current]) useEffect(() => { - const visibleNotifications = notifications.slice(0, showCount) + const visibleNotifications = notifications + .slice(0, showCount) + .filter((event) => isUserTrusted(event.pubkey)) const index = visibleNotifications.findIndex((event) => event.created_at <= lastReadTime) if (index === -1) { setNewNotifications(visibleNotifications) @@ -131,7 +135,7 @@ const NotificationList = forwardRef((_, ref) => { setNewNotifications(visibleNotifications.slice(0, index)) setOldNotifications(visibleNotifications.slice(index)) } - }, [notifications, lastReadTime, showCount]) + }, [notifications, lastReadTime, showCount, hideUntrustedEvents]) useEffect(() => { const options = { diff --git a/src/constants.ts b/src/constants.ts index e13bcd85..9e7c530e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -14,7 +14,7 @@ export const StorageKey = { ACCOUNT_FEED_INFO_MAP: 'accountFeedInfoMap', MEDIA_UPLOAD_SERVICE: 'mediaUploadService', AUTOPLAY: 'autoplay', - HIDE_UNTRUSTED_REPLIES: 'hideUntrustedReplies', + HIDE_UNTRUSTED_EVENTS: 'hideUntrustedEvents', ACCOUNT_RELAY_LIST_EVENT_MAP: 'accountRelayListEventMap', // deprecated ACCOUNT_FOLLOW_LIST_EVENT_MAP: 'accountFollowListEventMap', // deprecated ACCOUNT_MUTE_LIST_EVENT_MAP: 'accountMuteListEventMap', // deprecated diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts index 12f37675..52080a4a 100644 --- a/src/i18n/locales/ar.ts +++ b/src/i18n/locales/ar.ts @@ -233,6 +233,9 @@ export default { 'Platinum Sponsors': 'الرعاة البلاتينيون', From: 'من', 'Comment on': 'تعليق على', - 'View on njump.me': 'عرض على njump.me' + 'View on njump.me': 'عرض على njump.me', + 'Hide content from untrusted users': 'إخفاء المحتوى من المستخدمين غير الموثوقين', + 'Only show content from your followed users and the users they follow': + 'فقط عرض المحتوى من المستخدمين الذين تتابعهم والمستخدمين الذين يتابعونهم' } } diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index 0665e062..9b1551b9 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -239,6 +239,10 @@ export default { 'Platinum Sponsors': 'Platin-Sponsoren', From: 'Von', 'Comment on': 'Kommentar zu', - 'View on njump.me': 'Auf njump.me ansehen' + 'View on njump.me': 'Auf njump.me ansehen', + 'Hide content from untrusted users': + 'Inhalte von nicht vertrauenswürdigen Benutzern ausblenden', + 'Only show content from your followed users and the users they follow': + 'Nur Inhalte von Benutzern anzeigen, denen du folgst und die sie folgen' } } diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 33d9a768..7a64af24 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -233,6 +233,9 @@ export default { 'Platinum Sponsors': 'Platinum Sponsors', From: 'From', 'Comment on': 'Comment on', - 'View on njump.me': 'View on njump.me' + 'View on njump.me': 'View on njump.me', + 'Hide content from untrusted users': 'Hide content from untrusted users', + 'Only show content from your followed users and the users they follow': + 'Only show content from your followed users and the users they follow' } } diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index 4a342e60..83ed1dbb 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -238,6 +238,9 @@ export default { 'Platinum Sponsors': 'Patrocinadores Platino', From: 'De', 'Comment on': 'Comentar en', - 'View on njump.me': 'Ver en njump.me' + 'View on njump.me': 'Ver en njump.me', + 'Hide content from untrusted users': 'Ocultar contenido de usuarios no confiables', + 'Only show content from your followed users and the users they follow': + 'Solo mostrar contenido de tus usuarios seguidos y los usuarios que ellos siguen' } } diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index fd7ad812..fd003746 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -238,6 +238,9 @@ export default { 'Platinum Sponsors': 'Sponsors Platine', From: 'De', 'Comment on': 'Commenter sur', - 'View on njump.me': 'Voir sur njump.me' + 'View on njump.me': 'Voir sur njump.me', + 'Hide content from untrusted users': 'Hider le contenu des utilisateurs non fiables', + 'Only show content from your followed users and the users they follow': + 'Afficher uniquement le contenu de vos utilisateurs suivis et des utilisateurs qu’ils suivent' } } diff --git a/src/i18n/locales/it.ts b/src/i18n/locales/it.ts index f6d041c3..7a8c8be3 100644 --- a/src/i18n/locales/it.ts +++ b/src/i18n/locales/it.ts @@ -237,6 +237,9 @@ export default { 'Platinum Sponsors': 'Sponsor Platino', From: 'Da', 'Comment on': 'Commenta su', - 'View on njump.me': 'Visualizza su njump.me' + 'View on njump.me': 'Visualizza su njump.me', + 'Hide content from untrusted users': 'Nascondi contenuti da utenti non fidati', + 'Only show content from your followed users and the users they follow': + 'Mostra solo contenuti dai tuoi utenti seguiti e dagli utenti che seguono' } } diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts index e78a5544..3f550e88 100644 --- a/src/i18n/locales/ja.ts +++ b/src/i18n/locales/ja.ts @@ -234,6 +234,9 @@ export default { 'Platinum Sponsors': 'プラチナスポンサー', From: 'から', 'Comment on': 'にコメント', - 'View on njump.me': 'njump.meで表示' + 'View on njump.me': 'njump.meで表示', + 'Hide content from untrusted users': '信頼できないユーザーのコンテンツを非表示', + 'Only show content from your followed users and the users they follow': + 'フォローしているユーザーとそのユーザーがフォローしているユーザーのコンテンツのみを表示' } } diff --git a/src/i18n/locales/pl.ts b/src/i18n/locales/pl.ts index a1c5d785..016c5835 100644 --- a/src/i18n/locales/pl.ts +++ b/src/i18n/locales/pl.ts @@ -236,6 +236,9 @@ export default { 'Platinum Sponsors': 'Sponsorzy Platynowi', From: 'Od', 'Comment on': 'Komentarz do', - 'View on njump.me': 'Zobacz na njump.me' + 'View on njump.me': 'Zobacz na njump.me', + 'Hide content from untrusted users': 'Ukryj treści od nieznanych użytkowników', + 'Only show content from your followed users and the users they follow': + 'Pokaż tylko treści od użytkowników, których obserwujesz i ich obserwowanych' } } diff --git a/src/i18n/locales/pt-BR.ts b/src/i18n/locales/pt-BR.ts index 80ea4a5e..b7a01641 100644 --- a/src/i18n/locales/pt-BR.ts +++ b/src/i18n/locales/pt-BR.ts @@ -236,6 +236,9 @@ export default { 'Platinum Sponsors': 'Patrocinadores Platinum', From: 'Fonte', 'Comment on': 'Comentando', - 'View on njump.me': 'Ver em njump.me' + 'View on njump.me': 'Ver em njump.me', + 'Hide content from untrusted users': 'Ocultar conteúdo de usuários não confiáveis', + 'Only show content from your followed users and the users they follow': + 'Mostrar apenas conteúdo dos usuários que você segue e dos usuários que eles seguem' } } diff --git a/src/i18n/locales/pt-PT.ts b/src/i18n/locales/pt-PT.ts index bd53019a..1ae93e94 100644 --- a/src/i18n/locales/pt-PT.ts +++ b/src/i18n/locales/pt-PT.ts @@ -237,6 +237,9 @@ export default { 'Platinum Sponsors': 'Patrocinadores Platinum', From: 'De', 'Comment on': 'Comentar em', - 'View on njump.me': 'Ver em njump.me' + 'View on njump.me': 'Ver em njump.me', + 'Hide content from untrusted users': 'Esconder conteúdo de usuários não confiáveis', + 'Only show content from your followed users and the users they follow': + 'Mostrar apenas conteúdo dos usuários que você segue e dos usuários que eles seguem' } } diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts index 20f35b84..370c111c 100644 --- a/src/i18n/locales/ru.ts +++ b/src/i18n/locales/ru.ts @@ -237,6 +237,9 @@ export default { 'Platinum Sponsors': 'Платиновые спонсоры', From: 'От', 'Comment on': 'Прокомментировать', - 'View on njump.me': 'Посмотреть на njump.me' + 'View on njump.me': 'Посмотреть на njump.me', + 'Hide content from untrusted users': 'Скрыть контент от недоверенных пользователей', + 'Only show content from your followed users and the users they follow': + 'Показывать только контент от пользователей, на которых вы подписаны, и от пользователей, на которых они подписаны' } } diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts index fd561243..3cd2f7d1 100644 --- a/src/i18n/locales/zh.ts +++ b/src/i18n/locales/zh.ts @@ -234,6 +234,9 @@ export default { 'Platinum Sponsors': '白金赞助商', From: '来自', 'Comment on': '评论于', - 'View on njump.me': '在 njump.me 上查看' + 'View on njump.me': '在 njump.me 上查看', + 'Hide content from untrusted users': '隐藏不受信任用户的内容', + 'Only show content from your followed users and the users they follow': + '仅显示您关注的用户及其关注的用户的内容' } } diff --git a/src/pages/secondary/GeneralSettingsPage/index.tsx b/src/pages/secondary/GeneralSettingsPage/index.tsx index 09cedc00..41bae265 100644 --- a/src/pages/secondary/GeneralSettingsPage/index.tsx +++ b/src/pages/secondary/GeneralSettingsPage/index.tsx @@ -16,7 +16,7 @@ const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => { const [language, setLanguage] = useState(i18n.language as TLanguage) const { themeSetting, setThemeSetting } = useTheme() const { autoplay, setAutoplay } = useAutoplay() - const { enabled: hideUntrustedRepliesEnabled, updateEnabled: updateHideUntrustedRepliesEnabled } = + const { enabled: hideUntrustedEventsEnabled, updateEnabled: updateHideUntrustedEventsEnabled } = useUserTrust() const handleLanguageChange = (value: TLanguage) => { @@ -67,16 +67,16 @@ const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => { - diff --git a/src/providers/NotificationProvider.tsx b/src/providers/NotificationProvider.tsx index ce6f9bba..0dce5e44 100644 --- a/src/providers/NotificationProvider.tsx +++ b/src/providers/NotificationProvider.tsx @@ -5,6 +5,7 @@ import { SubCloser } from 'nostr-tools/abstract-pool' import { createContext, useContext, useEffect, useRef, useState } from 'react' import { useMuteList } from './MuteListProvider' import { useNostr } from './NostrProvider' +import { useUserTrust } from './UserTrustProvider' type TNotificationContext = { hasNewNotification: boolean @@ -24,6 +25,7 @@ export const useNotification = () => { export function NotificationProvider({ children }: { children: React.ReactNode }) { const { pubkey, notificationsSeenAt, updateNotificationsSeenAt } = useNostr() + const { isUserTrusted } = useUserTrust() const { mutePubkeys } = useMuteList() const [newNotificationIds, setNewNotificationIds] = useState(new Set()) const subCloserRef = useRef(null) @@ -61,7 +63,11 @@ export function NotificationProvider({ children }: { children: React.ReactNode } { onevent: (evt) => { // Only show notification if not from self and not muted - if (evt.pubkey !== pubkey && !mutePubkeys.includes(evt.pubkey)) { + if ( + evt.pubkey !== pubkey && + !mutePubkeys.includes(evt.pubkey) && + isUserTrusted(evt.pubkey) + ) { setNewNotificationIds((prev) => new Set([...prev, evt.id])) } }, diff --git a/src/providers/UserTrustProvider.tsx b/src/providers/UserTrustProvider.tsx index 9e379d87..ed4b9677 100644 --- a/src/providers/UserTrustProvider.tsx +++ b/src/providers/UserTrustProvider.tsx @@ -23,7 +23,7 @@ const wotSet = new Set() export function UserTrustProvider({ children }: { children: React.ReactNode }) { const { pubkey: currentPubkey } = useNostr() - const [enabled, setEnabled] = useState(storage.getHideUntrustedReplies()) + const [enabled, setEnabled] = useState(storage.getHideUntrustedEvents()) useEffect(() => { if (!currentPubkey) return @@ -43,7 +43,7 @@ export function UserTrustProvider({ children }: { children: React.ReactNode }) { const updateEnabled = (enabled: boolean) => { setEnabled(enabled) - storage.setHideUntrustedReplies(enabled) + storage.setHideUntrustedEvents(enabled) } const isUserTrusted = (pubkey: string) => { diff --git a/src/services/local-storage.service.ts b/src/services/local-storage.service.ts index e5d392dd..b562ec78 100644 --- a/src/services/local-storage.service.ts +++ b/src/services/local-storage.service.ts @@ -25,7 +25,7 @@ class LocalStorageService { private accountFeedInfoMap: Record = {} private mediaUploadService: string = DEFAULT_NIP_96_SERVICE private autoplay: boolean = true - private hideUntrustedReplies: boolean = true + private hideUntrustedEvents: boolean = true constructor() { if (!LocalStorageService.instance) { @@ -93,8 +93,8 @@ class LocalStorageService { this.autoplay = window.localStorage.getItem(StorageKey.AUTOPLAY) !== 'false' - this.hideUntrustedReplies = - window.localStorage.getItem(StorageKey.HIDE_UNTRUSTED_REPLIES) !== 'false' + this.hideUntrustedEvents = + window.localStorage.getItem(StorageKey.HIDE_UNTRUSTED_EVENTS) !== 'false' // Clean up deprecated data window.localStorage.removeItem(StorageKey.ACCOUNT_PROFILE_EVENT_MAP) @@ -252,12 +252,13 @@ class LocalStorageService { window.localStorage.setItem(StorageKey.AUTOPLAY, autoplay.toString()) } - getHideUntrustedReplies() { - return this.hideUntrustedReplies + getHideUntrustedEvents() { + return this.hideUntrustedEvents } - setHideUntrustedReplies(hide: boolean) { - this.hideUntrustedReplies = hide - window.localStorage.setItem(StorageKey.HIDE_UNTRUSTED_REPLIES, hide.toString()) + + setHideUntrustedEvents(hide: boolean) { + this.hideUntrustedEvents = hide + window.localStorage.setItem(StorageKey.HIDE_UNTRUSTED_EVENTS, hide.toString()) } }