diff --git a/src/components/ContentPreview/index.tsx b/src/components/ContentPreview/index.tsx index f0e28e91..662cbdbc 100644 --- a/src/components/ContentPreview/index.tsx +++ b/src/components/ContentPreview/index.tsx @@ -1,5 +1,7 @@ import { ExtendedKind } from '@/constants' +import { isMentioningMutedUsers } from '@/lib/event' import { cn } from '@/lib/utils' +import { useContentPolicy } from '@/providers/ContentPolicyProvider' import { useMuteList } from '@/providers/MuteListProvider' import { Event, kinds } from 'nostr-tools' import { useMemo } from 'react' @@ -22,10 +24,18 @@ export default function ContentPreview({ className?: string }) { const { t } = useTranslation() - const { mutePubkeys } = useMuteList() + const { mutePubkeySet } = useMuteList() + const { hideContentMentioningMutedUsers } = useContentPolicy() const isMuted = useMemo( - () => (event ? mutePubkeys.includes(event.pubkey) : false), - [mutePubkeys, event] + () => (event ? mutePubkeySet.has(event.pubkey) : false), + [mutePubkeySet, event] + ) + const isMentioningMuted = useMemo( + () => + hideContentMentioningMutedUsers && event + ? isMentioningMutedUsers(event, mutePubkeySet) + : false, + [event, mutePubkeySet] ) if (!event) { @@ -38,6 +48,14 @@ export default function ContentPreview({ ) } + if (isMentioningMuted) { + return ( +
+ [{t('This note mentions a user you muted')}] +
+ ) + } + if ( [ kinds.ShortTextNote, diff --git a/src/components/MuteButton/index.tsx b/src/components/MuteButton/index.tsx index 3a8ae4dc..651c3a9d 100644 --- a/src/components/MuteButton/index.tsx +++ b/src/components/MuteButton/index.tsx @@ -18,10 +18,10 @@ export default function MuteButton({ pubkey }: { pubkey: string }) { const { t } = useTranslation() const { isSmallScreen } = useScreenSize() const { pubkey: accountPubkey, checkLogin } = useNostr() - const { mutePubkeys, changing, mutePubkeyPrivately, mutePubkeyPublicly, unmutePubkey } = + const { mutePubkeySet, changing, mutePubkeyPrivately, mutePubkeyPublicly, unmutePubkey } = useMuteList() const [updating, setUpdating] = useState(false) - const isMuted = useMemo(() => mutePubkeys.includes(pubkey), [mutePubkeys, pubkey]) + const isMuted = useMemo(() => mutePubkeySet.has(pubkey), [mutePubkeySet, pubkey]) if (!accountPubkey || (pubkey && pubkey === accountPubkey)) return null diff --git a/src/components/Note/index.tsx b/src/components/Note/index.tsx index 1d4b70e0..211ca611 100644 --- a/src/components/Note/index.tsx +++ b/src/components/Note/index.tsx @@ -54,7 +54,7 @@ export default function Note({ const usingClient = useMemo(() => getUsingClient(event), [event]) const { defaultShowNsfw } = useContentPolicy() const [showNsfw, setShowNsfw] = useState(false) - const { mutePubkeys } = useMuteList() + const { mutePubkeySet } = useMuteList() const [showMuted, setShowMuted] = useState(false) let content: React.ReactNode @@ -67,7 +67,7 @@ export default function Note({ ].includes(event.kind) ) { content = - } else if (mutePubkeys.includes(event.pubkey) && !showMuted) { + } else if (mutePubkeySet.has(event.pubkey) && !showMuted) { content = setShowMuted(true)} /> } else if (!defaultShowNsfw && isNsfwEvent(event) && !showNsfw) { content = setShowNsfw(true)} /> diff --git a/src/components/NoteCard/RepostNoteCard.tsx b/src/components/NoteCard/RepostNoteCard.tsx index ccf5582d..ed8ed401 100644 --- a/src/components/NoteCard/RepostNoteCard.tsx +++ b/src/components/NoteCard/RepostNoteCard.tsx @@ -1,8 +1,10 @@ +import { isMentioningMutedUsers } from '@/lib/event' import { tagNameEquals } from '@/lib/tag' +import { useContentPolicy } from '@/providers/ContentPolicyProvider' import { useMuteList } from '@/providers/MuteListProvider' import client from '@/services/client.service' import { Event, kinds, nip19, verifyEvent } from 'nostr-tools' -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import MainNoteCard from './MainNoteCard' export default function RepostNoteCard({ @@ -14,8 +16,19 @@ export default function RepostNoteCard({ className?: string filterMutedNotes?: boolean }) { - const { mutePubkeys } = useMuteList() + const { mutePubkeySet } = useMuteList() + const { hideContentMentioningMutedUsers } = useContentPolicy() const [targetEvent, setTargetEvent] = useState(null) + const shouldHide = useMemo(() => { + if (!targetEvent) return true + if (filterMutedNotes && mutePubkeySet.has(targetEvent.pubkey)) { + return true + } + if (hideContentMentioningMutedUsers && isMentioningMutedUsers(targetEvent, mutePubkeySet)) { + return true + } + return false + }, [targetEvent, filterMutedNotes, hideContentMentioningMutedUsers, mutePubkeySet]) useEffect(() => { const fetch = async () => { try { @@ -56,10 +69,7 @@ export default function RepostNoteCard({ fetch() }, [event]) - if (!targetEvent) return null - if (filterMutedNotes && mutePubkeys.includes(targetEvent.pubkey)) { - return null - } + if (!targetEvent || shouldHide) return null return } diff --git a/src/components/NoteCard/index.tsx b/src/components/NoteCard/index.tsx index b8bde9d3..9d4c70fc 100644 --- a/src/components/NoteCard/index.tsx +++ b/src/components/NoteCard/index.tsx @@ -1,6 +1,9 @@ import { Skeleton } from '@/components/ui/skeleton' +import { isMentioningMutedUsers } from '@/lib/event' +import { useContentPolicy } from '@/providers/ContentPolicyProvider' import { useMuteList } from '@/providers/MuteListProvider' import { Event, kinds } from 'nostr-tools' +import { useMemo } from 'react' import MainNoteCard from './MainNoteCard' import RepostNoteCard from './RepostNoteCard' @@ -13,10 +16,18 @@ export default function NoteCard({ className?: string filterMutedNotes?: boolean }) { - const { mutePubkeys } = useMuteList() - if (filterMutedNotes && mutePubkeys.includes(event.pubkey)) { - return null - } + const { mutePubkeySet } = useMuteList() + const { hideContentMentioningMutedUsers } = useContentPolicy() + const shouldHide = useMemo(() => { + if (filterMutedNotes && mutePubkeySet.has(event.pubkey)) { + return true + } + if (hideContentMentioningMutedUsers && isMentioningMutedUsers(event, mutePubkeySet)) { + return true + } + return false + }, [event, filterMutedNotes, mutePubkeySet]) + if (shouldHide) return null if (event.kind === kinds.Repost) { return ( diff --git a/src/components/NoteList/index.tsx b/src/components/NoteList/index.tsx index a4448fba..cf0f6e61 100644 --- a/src/components/NoteList/index.tsx +++ b/src/components/NoteList/index.tsx @@ -2,9 +2,11 @@ import NewNotesButton from '@/components/NewNotesButton' import { Button } from '@/components/ui/button' import { getReplaceableCoordinateFromEvent, + isMentioningMutedUsers, isReplaceableEvent, isReplyNoteEvent } from '@/lib/event' +import { useContentPolicy } from '@/providers/ContentPolicyProvider' import { useDeletedEvent } from '@/providers/DeletedEventProvider' import { useMuteList } from '@/providers/MuteListProvider' import { useNostr } from '@/providers/NostrProvider' @@ -13,7 +15,15 @@ import client from '@/services/client.service' import { TFeedSubRequest } from '@/types' import dayjs from 'dayjs' import { Event } from 'nostr-tools' -import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react' +import { + forwardRef, + useCallback, + useEffect, + useImperativeHandle, + useMemo, + useRef, + useState +} from 'react' import { useTranslation } from 'react-i18next' import PullToRefresh from 'react-simple-pull-to-refresh' import NoteCard, { NoteCardLoadingSkeleton } from '../NoteCard' @@ -44,7 +54,8 @@ const NoteList = forwardRef( const { t } = useTranslation() const { startLogin } = useNostr() const { isUserTrusted } = useUserTrust() - const { mutePubkeys } = useMuteList() + const { mutePubkeySet } = useMuteList() + const { hideContentMentioningMutedUsers } = useContentPolicy() const { isEventDeleted } = useDeletedEvent() const [events, setEvents] = useState([]) const [newEvents, setNewEvents] = useState([]) @@ -56,13 +67,30 @@ const NoteList = forwardRef( const bottomRef = useRef(null) const topRef = useRef(null) + const shouldHideEvent = useCallback( + (evt: Event) => { + if (isEventDeleted(evt)) return true + if (hideReplies && isReplyNoteEvent(evt)) return true + if (hideUntrustedNotes && !isUserTrusted(evt.pubkey)) return true + if (filterMutedNotes && mutePubkeySet.has(evt.pubkey)) return true + if ( + filterMutedNotes && + hideContentMentioningMutedUsers && + isMentioningMutedUsers(evt, mutePubkeySet) + ) { + return true + } + + return false + }, + [hideReplies, hideUntrustedNotes, mutePubkeySet, isEventDeleted] + ) + const filteredEvents = useMemo(() => { const idSet = new Set() return events.slice(0, showCount).filter((evt) => { - if (isEventDeleted(evt)) return false - if (hideReplies && isReplyNoteEvent(evt)) return false - if (hideUntrustedNotes && !isUserTrusted(evt.pubkey)) return false + if (shouldHideEvent(evt)) return false const id = isReplaceableEvent(evt.kind) ? getReplaceableCoordinateFromEvent(evt) : evt.id if (idSet.has(id)) { @@ -71,16 +99,13 @@ const NoteList = forwardRef( idSet.add(id) return true }) - }, [events, hideReplies, hideUntrustedNotes, showCount, isEventDeleted]) + }, [events, showCount, shouldHideEvent]) const filteredNewEvents = useMemo(() => { const idSet = new Set() return newEvents.filter((event: Event) => { - if (isEventDeleted(event)) return false - if (hideReplies && isReplyNoteEvent(event)) return false - if (hideUntrustedNotes && !isUserTrusted(event.pubkey)) return false - if (filterMutedNotes && mutePubkeys.includes(event.pubkey)) return false + if (shouldHideEvent(event)) return false const id = isReplaceableEvent(event.kind) ? getReplaceableCoordinateFromEvent(event) @@ -91,7 +116,7 @@ const NoteList = forwardRef( idSet.add(id) return true }) - }, [newEvents, hideReplies, hideUntrustedNotes, filterMutedNotes, mutePubkeys, isEventDeleted]) + }, [events, showCount, shouldHideEvent]) const scrollToTop = (behavior: ScrollBehavior = 'instant') => { setTimeout(() => { diff --git a/src/components/NoteOptions/useMenuActions.tsx b/src/components/NoteOptions/useMenuActions.tsx index e3792fb9..c4b69573 100644 --- a/src/components/NoteOptions/useMenuActions.tsx +++ b/src/components/NoteOptions/useMenuActions.tsx @@ -47,8 +47,8 @@ export function useMenuActions({ const { t } = useTranslation() const { pubkey, attemptDelete } = useNostr() const { relaySets, favoriteRelays } = useFavoriteRelays() - const { mutePubkeyPublicly, mutePubkeyPrivately, unmutePubkey, mutePubkeys } = useMuteList() - const isMuted = useMemo(() => mutePubkeys.includes(event.pubkey), [mutePubkeys, event]) + const { mutePubkeyPublicly, mutePubkeyPrivately, unmutePubkey, mutePubkeySet } = useMuteList() + const isMuted = useMemo(() => mutePubkeySet.has(event.pubkey), [mutePubkeySet, event]) const broadcastSubMenu: SubMenuAction[] = useMemo(() => { const items = [] diff --git a/src/components/NoteStats/ReplyButton.tsx b/src/components/NoteStats/ReplyButton.tsx index 0b449c19..de8406fa 100644 --- a/src/components/NoteStats/ReplyButton.tsx +++ b/src/components/NoteStats/ReplyButton.tsx @@ -1,4 +1,7 @@ +import { isMentioningMutedUsers } from '@/lib/event' import { cn } from '@/lib/utils' +import { useContentPolicy } from '@/providers/ContentPolicyProvider' +import { useMuteList } from '@/providers/MuteListProvider' import { useNostr } from '@/providers/NostrProvider' import { useReply } from '@/providers/ReplyProvider' import { useUserTrust } from '@/providers/UserTrustProvider' @@ -14,18 +17,29 @@ export default function ReplyButton({ event }: { event: Event }) { const { pubkey, checkLogin } = useNostr() const { repliesMap } = useReply() const { hideUntrustedInteractions, isUserTrusted } = useUserTrust() + const { mutePubkeySet } = useMuteList() + const { hideContentMentioningMutedUsers } = useContentPolicy() const { replyCount, hasReplied } = useMemo(() => { const hasReplied = pubkey ? repliesMap.get(event.id)?.events.some((evt) => evt.pubkey === pubkey) : false - if (hideUntrustedInteractions) { - return { - replyCount: - repliesMap.get(event.id)?.events.filter((evt) => isUserTrusted(evt.pubkey)).length ?? 0, - hasReplied - } + + return { + replyCount: + repliesMap.get(event.id)?.events.filter((evt) => { + if (hideUntrustedInteractions && !isUserTrusted(evt.pubkey)) { + return false + } + if (mutePubkeySet.has(evt.pubkey)) { + return false + } + if (hideContentMentioningMutedUsers && isMentioningMutedUsers(evt, mutePubkeySet)) { + return false + } + return true + }).length ?? 0, + hasReplied } - return { replyCount: repliesMap.get(event.id)?.events.length ?? 0, hasReplied } }, [repliesMap, event.id, hideUntrustedInteractions]) const [open, setOpen] = useState(false) diff --git a/src/components/NotificationList/NotificationItem/index.tsx b/src/components/NotificationList/NotificationItem/index.tsx index b86c8e86..d0caf42c 100644 --- a/src/components/NotificationList/NotificationItem/index.tsx +++ b/src/components/NotificationList/NotificationItem/index.tsx @@ -1,6 +1,9 @@ import { ExtendedKind } from '@/constants' +import { isMentioningMutedUsers } from '@/lib/event' +import { useContentPolicy } from '@/providers/ContentPolicyProvider' import { useMuteList } from '@/providers/MuteListProvider' import { Event, kinds } from 'nostr-tools' +import { useMemo } from 'react' import { MentionNotification } from './MentionNotification' import { PollResponseNotification } from './PollResponseNotification' import { ReactionNotification } from './ReactionNotification' @@ -14,10 +17,19 @@ export function NotificationItem({ notification: Event isNew?: boolean }) { - const { mutePubkeys } = useMuteList() - if (mutePubkeys.includes(notification.pubkey)) { - return null - } + const { mutePubkeySet } = useMuteList() + const { hideContentMentioningMutedUsers } = useContentPolicy() + const shouldHide = useMemo(() => { + if (mutePubkeySet.has(notification.pubkey)) { + return true + } + if (hideContentMentioningMutedUsers && isMentioningMutedUsers(notification, mutePubkeySet)) { + return true + } + return false + }, []) + if (shouldHide) return null + if (notification.kind === kinds.Reaction) { return } diff --git a/src/components/PostEditor/Mentions.tsx b/src/components/PostEditor/Mentions.tsx index 4a6baa15..5463003e 100644 --- a/src/components/PostEditor/Mentions.tsx +++ b/src/components/PostEditor/Mentions.tsx @@ -23,7 +23,7 @@ export default function Mentions({ }) { const { t } = useTranslation() const { pubkey } = useNostr() - const { mutePubkeys } = useMuteList() + const { mutePubkeySet } = useMuteList() const [potentialMentions, setPotentialMentions] = useState([]) const [parentEventPubkey, setParentEventPubkey] = useState() const [removedPubkeys, setRemovedPubkeys] = useState([]) @@ -43,13 +43,13 @@ export default function Mentions({ pubkeys .filter((p) => potentialMentions.includes(p)) .concat( - potentialMentions.filter((p) => mutePubkeys.includes(p) && p !== _parentEventPubkey) + potentialMentions.filter((p) => mutePubkeySet.has(p) && p !== _parentEventPubkey) ) ) ) }) }) - }, [content, parentEvent, pubkey]) + }, [content, parentEvent, pubkey, mutePubkeySet]) useEffect(() => { const newMentions = potentialMentions.filter((pubkey) => !removedPubkeys.includes(pubkey)) diff --git a/src/components/Profile/ProfileFeed.tsx b/src/components/Profile/ProfileFeed.tsx index 8e5e4db4..0162493a 100644 --- a/src/components/Profile/ProfileFeed.tsx +++ b/src/components/Profile/ProfileFeed.tsx @@ -108,6 +108,7 @@ export default function ProfileFeed({ subRequests={subRequests} showKinds={temporaryShowKinds} hideReplies={listMode === 'posts'} + filterMutedNotes={false} /> ) diff --git a/src/components/Profile/index.tsx b/src/components/Profile/index.tsx index 66ae035f..fbab966d 100644 --- a/src/components/Profile/index.tsx +++ b/src/components/Profile/index.tsx @@ -31,7 +31,7 @@ export default function Profile({ id }: { id?: string }) { const { push } = useSecondaryPage() const { profile, isFetching } = useFetchProfile(id) const { pubkey: accountPubkey } = useNostr() - const { mutePubkeys } = useMuteList() + const { mutePubkeySet } = useMuteList() const { followings } = useFetchFollowings(profile?.pubkey) const isFollowingYou = useMemo(() => { return ( @@ -176,7 +176,7 @@ export default function Profile({ id }: { id?: string }) { {isSelf && ( - {mutePubkeys.length} + {mutePubkeySet.size}
{t('Muted')}
)} diff --git a/src/components/ProfileOptions/index.tsx b/src/components/ProfileOptions/index.tsx index 95462dc5..9004d4d2 100644 --- a/src/components/ProfileOptions/index.tsx +++ b/src/components/ProfileOptions/index.tsx @@ -9,17 +9,17 @@ import { pubkeyToNpub } from '@/lib/pubkey' import { useMuteList } from '@/providers/MuteListProvider' import { useNostr } from '@/providers/NostrProvider' import { Bell, BellOff, Copy, Ellipsis } from 'lucide-react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' export default function ProfileOptions({ pubkey }: { pubkey: string }) { const { t } = useTranslation() const { pubkey: accountPubkey } = useNostr() - const { mutePubkeys, mutePubkeyPrivately, mutePubkeyPublicly, unmutePubkey } = useMuteList() + const { mutePubkeySet, mutePubkeyPrivately, mutePubkeyPublicly, unmutePubkey } = useMuteList() + const isMuted = useMemo(() => mutePubkeySet.has(pubkey), [mutePubkeySet, pubkey]) if (pubkey === accountPubkey) return null - const isMuted = mutePubkeys.includes(pubkey) - return ( diff --git a/src/components/ReplyNote/index.tsx b/src/components/ReplyNote/index.tsx index eaa563ed..ac7c3352 100644 --- a/src/components/ReplyNote/index.tsx +++ b/src/components/ReplyNote/index.tsx @@ -1,8 +1,9 @@ import { useSecondaryPage } from '@/PageManager' import { Button } from '@/components/ui/button' import { Skeleton } from '@/components/ui/skeleton' -import { getUsingClient } from '@/lib/event' +import { getUsingClient, isMentioningMutedUsers } from '@/lib/event' import { toNote } from '@/lib/link' +import { useContentPolicy } from '@/providers/ContentPolicyProvider' import { useMuteList } from '@/providers/MuteListProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider' import { Event } from 'nostr-tools' @@ -33,12 +34,21 @@ export default function ReplyNote({ const { t } = useTranslation() const { isSmallScreen } = useScreenSize() const { push } = useSecondaryPage() - const { mutePubkeys } = useMuteList() + const { mutePubkeySet } = useMuteList() + const { hideContentMentioningMutedUsers } = useContentPolicy() const [showMuted, setShowMuted] = useState(false) - const show = useMemo( - () => showMuted || !mutePubkeys.includes(event.pubkey), - [showMuted, mutePubkeys, event] - ) + const show = useMemo(() => { + if (showMuted) { + return true + } + if (mutePubkeySet.has(event.pubkey)) { + return false + } + if (hideContentMentioningMutedUsers && isMentioningMutedUsers(event, mutePubkeySet)) { + return false + } + return true + }, [showMuted, mutePubkeySet, event, hideContentMentioningMutedUsers]) const usingClient = useMemo(() => getUsingClient(event), [event]) return ( diff --git a/src/components/ReplyNoteList/index.tsx b/src/components/ReplyNoteList/index.tsx index 6eabf3e0..ce1cba17 100644 --- a/src/components/ReplyNoteList/index.tsx +++ b/src/components/ReplyNoteList/index.tsx @@ -5,11 +5,15 @@ import { getRootATag, getRootETag, getRootEventHexId, + isMentioningMutedUsers, isReplaceableEvent, isReplyNoteEvent } from '@/lib/event' +import { toNote } from '@/lib/link' import { generateBech32IdFromETag, tagNameEquals } from '@/lib/tag' import { useSecondaryPage } from '@/PageManager' +import { useContentPolicy } from '@/providers/ContentPolicyProvider' +import { useMuteList } from '@/providers/MuteListProvider' import { useReply } from '@/providers/ReplyProvider' import { useUserTrust } from '@/providers/UserTrustProvider' import client from '@/services/client.service' @@ -29,8 +33,10 @@ const SHOW_COUNT = 10 export default function ReplyNoteList({ index, event }: { index?: number; event: NEvent }) { const { t } = useTranslation() - const { currentIndex } = useSecondaryPage() + const { push, currentIndex } = useSecondaryPage() const { hideUntrustedInteractions, isUserTrusted } = useUserTrust() + const { mutePubkeySet } = useMuteList() + const { hideContentMentioningMutedUsers } = useContentPolicy() const [rootInfo, setRootInfo] = useState(undefined) const { repliesMap, addReplies } = useReply() const replies = useMemo(() => { @@ -44,6 +50,9 @@ export default function ReplyNoteList({ index, event }: { index?: number; event: const events = parentEventKeys.flatMap((id) => repliesMap.get(id)?.events || []) events.forEach((evt) => { if (replyIdSet.has(evt.id)) return + if (mutePubkeySet.has(evt.pubkey)) return + if (hideContentMentioningMutedUsers && isMentioningMutedUsers(evt, mutePubkeySet)) return + replyIdSet.add(evt.id) replyEvents.push(evt) }) @@ -309,7 +318,14 @@ export default function ReplyNoteList({ index, event }: { index?: number; event: parentEventHexId && highlightReply(parentEventHexId)} + onClickParent={() => { + if (!parentEventHexId) return + if (replies.every((r) => r.id !== parentEventHexId)) { + push(toNote(parentEventId ?? parentEventHexId)) + return + } + highlightReply(parentEventHexId) + }} highlight={highlightReplyId === reply.id} /> diff --git a/src/constants.ts b/src/constants.ts index 32a5f692..768f7eb9 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -40,6 +40,7 @@ export const StorageKey = { DISMISSED_TOO_MANY_RELAYS_ALERT: 'dismissedTooManyRelaysAlert', SHOW_KINDS: 'showKinds', SHOW_KINDS_VERSION: 'showKindsVersion', + HIDE_CONTENT_MENTIONING_MUTED_USERS: 'hideContentMentioningMutedUsers', MEDIA_UPLOAD_SERVICE: 'mediaUploadService', // deprecated HIDE_UNTRUSTED_EVENTS: 'hideUntrustedEvents', // deprecated ACCOUNT_RELAY_LIST_EVENT_MAP: 'accountRelayListEventMap', // deprecated diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts index 9ebea88c..cd8cbd72 100644 --- a/src/i18n/locales/ar.ts +++ b/src/i18n/locales/ar.ts @@ -372,6 +372,8 @@ export default { 'Deletion request sent to {{count}} relays': 'تم إرسال طلب الحذف إلى {{count}} ريلايات', 'Suitable Relays': 'الريلايات المناسبة', 'Type searching for people, keywords, or relays': - 'اكتب للبحث عن أشخاص، كلمات مفتاحية، أو ريلايات' + 'اكتب للبحث عن أشخاص، كلمات مفتاحية، أو ريلايات', + 'Hide content mentioning muted users': 'إخفاء المحتوى الذي يذكر المستخدمين المكتومين', + 'This note mentions a user you muted': 'هذه الملاحظة تذكر مستخدماً قمت بكتمه' } } diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index 08cf39a3..5161610d 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -380,6 +380,9 @@ export default { 'Deletion request sent to {{count}} relays': 'Löschanfrage an {{count}} Relays gesendet', 'Suitable Relays': 'Geeignete Relays', 'Type searching for people, keywords, or relays': - 'Gib ein, um nach Personen, Schlüsselwörtern oder Relays zu suchen' + 'Gib ein, um nach Personen, Schlüsselwörtern oder Relays zu suchen', + 'Hide content mentioning muted users': 'Inhalte ausblenden, die stumme Benutzer erwähnen', + 'This note mentions a user you muted': + 'Diese Notiz erwähnt einen Benutzer, den Sie stumm geschaltet haben' } } diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index cfc47f70..83ef1974 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -371,6 +371,8 @@ export default { 'Deletion request sent to {{count}} relays': 'Deletion request sent to {{count}} relays', 'Suitable Relays': 'Suitable Relays', 'Type searching for people, keywords, or relays': - 'Type searching for people, keywords, or relays' + 'Type searching for people, keywords, or relays', + 'Hide content mentioning muted users': 'Hide content mentioning muted users', + 'This note mentions a user you muted': 'This note mentions a user you muted' } } diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index 97d13882..0dc535be 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -377,6 +377,8 @@ export default { 'Solicitud de eliminación enviada a {{count}} relés', 'Suitable Relays': 'Relés adecuados', 'Type searching for people, keywords, or relays': - 'Escribe para buscar personas, palabras clave o relés' + 'Escribe para buscar personas, palabras clave o relés', + 'Hide content mentioning muted users': 'Ocultar contenido que mencione usuarios silenciados', + 'This note mentions a user you muted': 'Esta nota menciona a un usuario que silenciaste' } } diff --git a/src/i18n/locales/fa.ts b/src/i18n/locales/fa.ts index 159e7776..c668b3cc 100644 --- a/src/i18n/locales/fa.ts +++ b/src/i18n/locales/fa.ts @@ -373,6 +373,8 @@ export default { 'Deletion request sent to {{count}} relays': 'درخواست حذف به {{count}} رله ارسال شد', 'Suitable Relays': 'رله‌های مناسب', 'Type searching for people, keywords, or relays': - 'برای جستجو افراد، کلمات کلیدی یا رله‌ها تایپ کنید' + 'برای جستجو افراد، کلمات کلیدی یا رله‌ها تایپ کنید', + 'Hide content mentioning muted users': 'مخفی کردن محتوای اشاره کننده به کاربران بی‌صدا شده', + 'This note mentions a user you muted': 'این یادداشت به کاربری که بی‌صدا کرده‌اید اشاره می‌کند' } } diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index 27b368c3..0cd4840e 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -379,6 +379,10 @@ export default { 'Demande de suppression envoyée à {{count}} relais', 'Suitable Relays': 'Relais adaptés', 'Type searching for people, keywords, or relays': - 'Tapez pour rechercher des personnes, des mots-clés ou des relais' + 'Tapez pour rechercher des personnes, des mots-clés ou des relais', + 'Hide content mentioning muted users': + 'Masquer le contenu mentionnant des utilisateurs masqués', + 'This note mentions a user you muted': + 'Cette note mentionne un utilisateur que vous avez masqué' } } diff --git a/src/i18n/locales/it.ts b/src/i18n/locales/it.ts index 76fe4e0e..156813b4 100644 --- a/src/i18n/locales/it.ts +++ b/src/i18n/locales/it.ts @@ -377,6 +377,8 @@ export default { 'Richiesta di eliminazione inviata a {{count}} relays', 'Suitable Relays': 'Relays adatti', 'Type searching for people, keywords, or relays': - 'Digita per cercare persone, parole chiave o relays' + 'Digita per cercare persone, parole chiave o relays', + 'Hide content mentioning muted users': 'Nascondi contenuto che menziona utenti silenziati', + 'This note mentions a user you muted': 'Questa nota menziona un utente che hai silenziato' } } diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts index 7d44d334..ee21f7a2 100644 --- a/src/i18n/locales/ja.ts +++ b/src/i18n/locales/ja.ts @@ -374,6 +374,8 @@ export default { '削除リクエストが{{count}}個のリレーに送信されました', 'Suitable Relays': '適切なリレー', 'Type searching for people, keywords, or relays': - '人、キーワード、またはリレーを検索するために入力してください' + '人、キーワード、またはリレーを検索するために入力してください', + 'Hide content mentioning muted users': 'ミュートしたユーザーを言及するコンテンツを非表示', + 'This note mentions a user you muted': 'このノートはミュートしたユーザーを言及しています' } } diff --git a/src/i18n/locales/ko.ts b/src/i18n/locales/ko.ts index 76625ea8..4ec7539e 100644 --- a/src/i18n/locales/ko.ts +++ b/src/i18n/locales/ko.ts @@ -374,6 +374,8 @@ export default { '삭제 요청이 {{count}}개의 릴레이로 전송되었습니다', 'Suitable Relays': '적합한 릴레이', 'Type searching for people, keywords, or relays': - '사람, 키워드 또는 릴레이를 검색하려면 입력하세요' + '사람, 키워드 또는 릴레이를 검색하려면 입력하세요', + 'Hide content mentioning muted users': '뮤트된 사용자를 언급하는 콘텐츠 숨기기', + 'This note mentions a user you muted': '이 노트는 뮤트한 사용자를 언급합니다' } } diff --git a/src/i18n/locales/pl.ts b/src/i18n/locales/pl.ts index 51e7d5e5..d2ccb5e6 100644 --- a/src/i18n/locales/pl.ts +++ b/src/i18n/locales/pl.ts @@ -378,6 +378,8 @@ export default { 'Żądanie usunięcia wysłane do {{count}} przekaźników', 'Suitable Relays': 'Odpowiednie przekaźniki', 'Type searching for people, keywords, or relays': - 'Wpisz, aby wyszukać osoby, słowa kluczowe lub przekaźniki' + 'Wpisz, aby wyszukać osoby, słowa kluczowe lub przekaźniki', + 'Hide content mentioning muted users': 'Ukryj treści wspominające wyciszonych użytkowników', + 'This note mentions a user you muted': 'Ten wpis wspomina użytkownika, którego wyciszyłeś' } } diff --git a/src/i18n/locales/pt-BR.ts b/src/i18n/locales/pt-BR.ts index ea3147f2..67534904 100644 --- a/src/i18n/locales/pt-BR.ts +++ b/src/i18n/locales/pt-BR.ts @@ -374,6 +374,8 @@ export default { 'Deletion request sent to {{count}} relays': 'Pedido de exclusão enviado para {{count}} relays', 'Suitable Relays': 'Relays adequados', 'Type searching for people, keywords, or relays': - 'Digite para buscar pessoas, palavras-chave ou relays' + 'Digite para buscar pessoas, palavras-chave ou relays', + 'Hide content mentioning muted users': 'Ocultar conteúdo que menciona usuários silenciados', + 'This note mentions a user you muted': 'Esta nota menciona um usuário que você silenciou' } } diff --git a/src/i18n/locales/pt-PT.ts b/src/i18n/locales/pt-PT.ts index 7aa6bc21..a2743567 100644 --- a/src/i18n/locales/pt-PT.ts +++ b/src/i18n/locales/pt-PT.ts @@ -377,6 +377,8 @@ export default { 'Pedido de eliminação enviado para {{count}} relays', 'Suitable Relays': 'Relays adequados', 'Type searching for people, keywords, or relays': - 'Digite para buscar pessoas, palavras-chave ou relays' + 'Digite para buscar pessoas, palavras-chave ou relays', + 'Hide content mentioning muted users': 'Ocultar conteúdo que menciona utilizadores silenciados', + 'This note mentions a user you muted': 'Esta nota menciona um utilizador que silenciou' } } diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts index fc41f89f..462e7dc6 100644 --- a/src/i18n/locales/ru.ts +++ b/src/i18n/locales/ru.ts @@ -377,6 +377,9 @@ export default { 'Deletion request sent to {{count}} relays': 'Запрос на удаление отправлен на {{count}} релеев', 'Suitable Relays': 'Подходящие релея', 'Type searching for people, keywords, or relays': - 'Начните ввод для поиска людей, ключевых слов или релеев' + 'Начните ввод для поиска людей, ключевых слов или релеев', + 'Hide content mentioning muted users': 'Скрыть контент, упоминающий заглушённых пользователей', + 'This note mentions a user you muted': + 'Эта заметка упоминает пользователя, которого вы заглушили' } } diff --git a/src/i18n/locales/th.ts b/src/i18n/locales/th.ts index b1f0897b..5de44407 100644 --- a/src/i18n/locales/th.ts +++ b/src/i18n/locales/th.ts @@ -369,6 +369,8 @@ export default { 'Try deleting this note': 'ลองลบโน้ตนี้ดู', 'Deletion request sent to {{count}} relays': 'คำขอลบถูกส่งไปยังรีเลย์ {{count}} รายการ', 'Suitable Relays': 'รีเลย์ที่เหมาะสม', - 'Type searching for people, keywords, or relays': 'พิมพ์เพื่อค้นหาผู้คน คีย์เวิร์ด หรือรีเลย์' + 'Type searching for people, keywords, or relays': 'พิมพ์เพื่อค้นหาผู้คน คีย์เวิร์ด หรือรีเลย์', + 'Hide content mentioning muted users': 'ซ่อนเนื้อหาที่กล่าวถึงผู้ใช้ที่ปิดเสียง', + 'This note mentions a user you muted': 'โน้ตนี้กล่าวถึงผู้ใช้ที่คุณปิดเสียง' } } diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts index c2802e0c..d42dce96 100644 --- a/src/i18n/locales/zh.ts +++ b/src/i18n/locales/zh.ts @@ -367,6 +367,8 @@ export default { 'Try deleting this note': '尝试删除此笔记', 'Deletion request sent to {{count}} relays': '删除请求已发送到 {{count}} 个服务器', 'Suitable Relays': '适合的服务器', - 'Type searching for people, keywords, or relays': '输入以搜索用户、关键词或服务器' + 'Type searching for people, keywords, or relays': '输入以搜索用户、关键词或服务器', + 'Hide content mentioning muted users': '隐藏提及已屏蔽用户的内容', + 'This note mentions a user you muted': '此笔记提及了您已屏蔽的用户' } } diff --git a/src/lib/event.ts b/src/lib/event.ts index 729ea78e..73b68088 100644 --- a/src/lib/event.ts +++ b/src/lib/event.ts @@ -47,6 +47,15 @@ export function isProtectedEvent(event: Event) { return event.tags.some(([tagName]) => tagName === '-') } +export function isMentioningMutedUsers(event: Event, mutePubkeySet: Set) { + for (const [tagName, pubkey] of event.tags) { + if (tagName === 'p' && mutePubkeySet.has(pubkey)) { + return true + } + } + return false +} + export function getParentETag(event?: Event) { if (!event) return undefined diff --git a/src/pages/secondary/GeneralSettingsPage/index.tsx b/src/pages/secondary/GeneralSettingsPage/index.tsx index 6da326e8..e4a8215b 100644 --- a/src/pages/secondary/GeneralSettingsPage/index.tsx +++ b/src/pages/secondary/GeneralSettingsPage/index.tsx @@ -16,7 +16,14 @@ const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => { const { t, i18n } = useTranslation() const [language, setLanguage] = useState(i18n.language as TLanguage) const { themeSetting, setThemeSetting } = useTheme() - const { autoplay, setAutoplay, defaultShowNsfw, setDefaultShowNsfw } = useContentPolicy() + const { + autoplay, + setAutoplay, + defaultShowNsfw, + setDefaultShowNsfw, + hideContentMentioningMutedUsers, + setHideContentMentioningMutedUsers + } = useContentPolicy() const { hideUntrustedNotes, updateHideUntrustedNotes } = useUserTrust() const handleLanguageChange = (value: TLanguage) => { @@ -76,6 +83,16 @@ const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => { onCheckedChange={updateHideUntrustedNotes} /> + + + +