feat: support hiding indirect notifications
This commit is contained in:
parent
331811f683
commit
2cd1ae481b
27 changed files with 196 additions and 38 deletions
|
|
@ -1,13 +1,16 @@
|
|||
import ParentNotePreview from '@/components/ParentNotePreview'
|
||||
import { NOTIFICATION_LIST_STYLE } from '@/constants'
|
||||
import { getEmbeddedPubkeys, getParentStuff } from '@/lib/event'
|
||||
import { ExtendedKind, NOTIFICATION_LIST_STYLE } from '@/constants'
|
||||
import { getEmbeddedPubkeys, getParentStuff, getParentTag } from '@/lib/event'
|
||||
import { toExternalContent, toNote } from '@/lib/link'
|
||||
import { generateBech32IdFromETag, tagNameEquals } from '@/lib/tag'
|
||||
import { useSecondaryPage } from '@/PageManager'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useNotificationUserPreference } from '@/providers/NotificationUserPreferenceProvider'
|
||||
import { useUserPreferences } from '@/providers/UserPreferencesProvider'
|
||||
import client from '@/services/client.service'
|
||||
import { AtSign, MessageCircle, Quote } from 'lucide-react'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Notification from './Notification'
|
||||
|
||||
|
|
@ -22,6 +25,7 @@ export function MentionNotification({
|
|||
const { push } = useSecondaryPage()
|
||||
const { pubkey } = useNostr()
|
||||
const { notificationListStyle } = useUserPreferences()
|
||||
const { hideIndirect } = useNotificationUserPreference()
|
||||
const isMention = useMemo(() => {
|
||||
if (!pubkey) return false
|
||||
const mentions = getEmbeddedPubkeys(notification)
|
||||
|
|
@ -30,6 +34,49 @@ export function MentionNotification({
|
|||
const { parentEventId, parentExternalContent } = useMemo(() => {
|
||||
return getParentStuff(notification)
|
||||
}, [notification])
|
||||
const [isDirectMention, setIsDirectMention] = useState(false)
|
||||
useEffect(() => {
|
||||
const checkIsDirectMention = async () => {
|
||||
if (!pubkey) return false
|
||||
if (isMention) return true
|
||||
if (notification.kind === ExtendedKind.POLL) return true
|
||||
|
||||
if (
|
||||
notification.kind === ExtendedKind.VOICE_COMMENT ||
|
||||
notification.kind === ExtendedKind.COMMENT
|
||||
) {
|
||||
const parentPTag = notification.tags.findLast(tagNameEquals('p'))
|
||||
const parentPubkey = parentPTag?.[1]
|
||||
return parentPubkey === pubkey
|
||||
}
|
||||
|
||||
const parentTag = getParentTag(notification)
|
||||
if (parentTag?.type === 'e') {
|
||||
const [, , , , parentPubkey] = parentTag.tag
|
||||
if (parentPubkey) {
|
||||
return parentPubkey === pubkey
|
||||
}
|
||||
const parentEventId = generateBech32IdFromETag(parentTag.tag)
|
||||
if (!parentEventId) return false
|
||||
const parentEvent = await client.fetchEvent(parentEventId)
|
||||
if (parentEvent) {
|
||||
return parentEvent.pubkey === pubkey
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (parentTag?.type === 'a') {
|
||||
const coordinate = parentTag.tag[1]
|
||||
const [, parentPubkey] = coordinate.split(':')
|
||||
return parentPubkey === pubkey
|
||||
}
|
||||
return false
|
||||
}
|
||||
checkIsDirectMention().then(setIsDirectMention)
|
||||
}, [pubkey, notification, isMention])
|
||||
|
||||
if (hideIndirect && !isDirectMention) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Notification
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import Image from '@/components/Image'
|
|||
import { useFetchEvent } from '@/hooks'
|
||||
import { generateBech32IdFromATag, generateBech32IdFromETag, tagNameEquals } from '@/lib/tag'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useNotificationUserPreference } from '@/providers/NotificationUserPreferenceProvider'
|
||||
import { Heart } from 'lucide-react'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
|
|
@ -17,6 +18,7 @@ export function ReactionNotification({
|
|||
}) {
|
||||
const { t } = useTranslation()
|
||||
const { pubkey } = useNostr()
|
||||
const { hideIndirect } = useNotificationUserPreference()
|
||||
const eventId = useMemo(() => {
|
||||
const aTag = notification.tags.findLast(tagNameEquals('a'))
|
||||
if (aTag) {
|
||||
|
|
@ -56,6 +58,9 @@ export function ReactionNotification({
|
|||
if (!event || !eventId || !reaction) {
|
||||
return null
|
||||
}
|
||||
if (hideIndirect && event.pubkey !== pubkey) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Notification
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useNotificationUserPreference } from '@/providers/NotificationUserPreferenceProvider'
|
||||
import client from '@/services/client.service'
|
||||
import { Repeat } from 'lucide-react'
|
||||
import { Event, validateEvent } from 'nostr-tools'
|
||||
|
|
@ -13,6 +15,8 @@ export function RepostNotification({
|
|||
isNew?: boolean
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const { pubkey } = useNostr()
|
||||
const { hideIndirect } = useNotificationUserPreference()
|
||||
const event = useMemo(() => {
|
||||
try {
|
||||
const event = JSON.parse(notification.content) as Event
|
||||
|
|
@ -25,6 +29,9 @@ export function RepostNotification({
|
|||
}
|
||||
}, [notification.content])
|
||||
if (!event) return null
|
||||
if (hideIndirect && event.pubkey !== pubkey) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Notification
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ExtendedKind, NOTIFICATION_LIST_STYLE } from '@/constants'
|
||||
import { ExtendedKind, NOTIFICATION_LIST_STYLE, SPECIAL_TRUST_SCORE_FILTER_ID } from '@/constants'
|
||||
import { useInfiniteScroll } from '@/hooks'
|
||||
import { compareEvents } from '@/lib/event'
|
||||
import { getDefaultRelayUrls } from '@/lib/relay'
|
||||
|
|
@ -28,6 +28,7 @@ import PullToRefresh from 'react-simple-pull-to-refresh'
|
|||
import { LoadingBar } from '../LoadingBar'
|
||||
import { RefreshButton } from '../RefreshButton'
|
||||
import Tabs from '../Tabs'
|
||||
import TrustScoreFilter from '../TrustScoreFilter'
|
||||
import { NotificationItem } from './NotificationItem'
|
||||
import { NotificationSkeleton } from './NotificationItem/Notification'
|
||||
|
||||
|
|
@ -280,7 +281,12 @@ const NotificationList = forwardRef((_, ref) => {
|
|||
setShowCount(SHOW_COUNT)
|
||||
setNotificationType(type as TNotificationType)
|
||||
}}
|
||||
options={!supportTouch ? <RefreshButton onClick={() => refresh()} /> : null}
|
||||
options={
|
||||
<>
|
||||
{!supportTouch ? <RefreshButton onClick={() => refresh()} /> : null}
|
||||
<TrustScoreFilter filterId={SPECIAL_TRUST_SCORE_FILTER_ID.NOTIFICATIONS} />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<div ref={topRef} className="scroll-mt-[calc(6rem+1px)]" />
|
||||
{supportTouch ? (
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ export const StorageKey = {
|
|||
MUTED_WORDS: 'mutedWords',
|
||||
MIN_TRUST_SCORE: 'minTrustScore',
|
||||
MIN_TRUST_SCORE_MAP: 'minTrustScoreMap',
|
||||
HIDE_INDIRECT_NOTIFICATIONS: 'hideIndirectNotifications',
|
||||
ENABLE_LIVE_FEED: 'enableLiveFeed', // deprecated
|
||||
HIDE_UNTRUSTED_NOTES: 'hideUntrustedNotes', // deprecated
|
||||
HIDE_UNTRUSTED_INTERACTIONS: 'hideUntrustedInteractions', // deprecated
|
||||
|
|
|
|||
|
|
@ -665,6 +665,7 @@ export default {
|
|||
'Zap Details': 'تفاصيل Zap',
|
||||
'Default trust score filter threshold ({{n}}%)': 'عتبة مرشح درجة الثقة الافتراضية ({{n}}%)',
|
||||
'No notes found': 'لم يتم العثور على ملاحظات',
|
||||
'Try again later or check your connection': 'حاول مرة أخرى لاحقًا أو تحقق من اتصالك'
|
||||
'Try again later or check your connection': 'حاول مرة أخرى لاحقًا أو تحقق من اتصالك',
|
||||
'Hide indirect': 'إخفاء غير المباشرة'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -688,6 +688,8 @@ export default {
|
|||
'Default trust score filter threshold ({{n}}%)':
|
||||
'Standard-Vertrauenswert-Filter-Schwelle ({{n}}%)',
|
||||
'No notes found': 'Keine Notizen gefunden',
|
||||
'Try again later or check your connection': 'Versuchen Sie es später erneut oder überprüfen Sie Ihre Verbindung'
|
||||
'Try again later or check your connection':
|
||||
'Versuchen Sie es später erneut oder überprüfen Sie Ihre Verbindung',
|
||||
'Hide indirect': 'Indirekte ausblenden'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -668,8 +668,10 @@ export default {
|
|||
'Muted words': 'Muted words',
|
||||
'Add muted word': 'Add muted word',
|
||||
'Zap Details': 'Zap Details',
|
||||
'Default trust score filter threshold ({{n}}%)': 'Default trust score filter threshold ({{n}}%)',
|
||||
'Default trust score filter threshold ({{n}}%)':
|
||||
'Default trust score filter threshold ({{n}}%)',
|
||||
'No notes found': 'No notes found',
|
||||
'Try again later or check your connection': 'Try again later or check your connection'
|
||||
'Try again later or check your connection': 'Try again later or check your connection',
|
||||
'Hide indirect': 'Hide indirect'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -682,6 +682,7 @@ export default {
|
|||
'Default trust score filter threshold ({{n}}%)':
|
||||
'Umbral predeterminado del filtro de puntuación de confianza ({{n}}%)',
|
||||
'No notes found': 'No se encontraron notas',
|
||||
'Try again later or check your connection': 'Inténtalo más tarde o verifica tu conexión'
|
||||
'Try again later or check your connection': 'Inténtalo más tarde o verifica tu conexión',
|
||||
'Hide indirect': 'Ocultar indirectas'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -676,6 +676,8 @@ export default {
|
|||
'Zap Details': 'جزئیات زپ',
|
||||
'Default trust score filter threshold ({{n}}%)': 'آستانه فیلتر امتیاز اعتماد پیشفرض ({{n}}%)',
|
||||
'No notes found': 'یادداشتی یافت نشد',
|
||||
'Try again later or check your connection': 'بعداً دوباره امتحان کنید یا اتصال خود را بررسی کنید'
|
||||
'Try again later or check your connection':
|
||||
'بعداً دوباره امتحان کنید یا اتصال خود را بررسی کنید',
|
||||
'Hide indirect': 'پنهان کردن غیرمستقیم'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -686,6 +686,7 @@ export default {
|
|||
'Default trust score filter threshold ({{n}}%)':
|
||||
'Seuil par défaut du filtre de score de confiance ({{n}}%)',
|
||||
'No notes found': 'Aucune note trouvée',
|
||||
'Try again later or check your connection': 'Réessayez plus tard ou vérifiez votre connexion'
|
||||
'Try again later or check your connection': 'Réessayez plus tard ou vérifiez votre connexion',
|
||||
'Hide indirect': 'Masquer indirects'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -677,6 +677,7 @@ export default {
|
|||
'Zap Details': 'जैप विवरण',
|
||||
'Default trust score filter threshold ({{n}}%)': 'डिफ़ॉल्ट विश्वास स्कोर फ़िल्टर सीमा ({{n}}%)',
|
||||
'No notes found': 'कोई नोट्स नहीं मिले',
|
||||
'Try again later or check your connection': 'बाद में पुनः प्रयास करें या अपना कनेक्शन जाँचें'
|
||||
'Try again later or check your connection': 'बाद में पुनः प्रयास करें या अपना कनेक्शन जाँचें',
|
||||
'Hide indirect': 'अप्रत्यक्ष छुपाएं'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -671,6 +671,7 @@ export default {
|
|||
'Default trust score filter threshold ({{n}}%)':
|
||||
'Alapértelmezett bizalmi pontszám szűrő küszöbérték ({{n}}%)',
|
||||
'No notes found': 'Nem található jegyzet',
|
||||
'Try again later or check your connection': 'Próbáld újra később vagy ellenőrizd a kapcsolatot'
|
||||
'Try again later or check your connection': 'Próbáld újra később vagy ellenőrizd a kapcsolatot',
|
||||
'Hide indirect': 'Közvetettek elrejtése'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -682,6 +682,7 @@ export default {
|
|||
'Default trust score filter threshold ({{n}}%)':
|
||||
'Soglia predefinita del filtro del punteggio di fiducia ({{n}}%)',
|
||||
'No notes found': 'Nessuna nota trovata',
|
||||
'Try again later or check your connection': 'Riprova più tardi o controlla la connessione'
|
||||
'Try again later or check your connection': 'Riprova più tardi o controlla la connessione',
|
||||
'Hide indirect': 'Nascondi indirette'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -673,8 +673,11 @@ export default {
|
|||
'Muted words': 'ミュートワード',
|
||||
'Add muted word': 'ミュートワードを追加',
|
||||
'Zap Details': 'Zapの詳細',
|
||||
'Default trust score filter threshold ({{n}}%)': 'デフォルトの信頼スコアフィルター閾値 ({{n}}%)',
|
||||
'Default trust score filter threshold ({{n}}%)':
|
||||
'デフォルトの信頼スコアフィルター閾値 ({{n}}%)',
|
||||
'No notes found': 'ノートが見つかりません',
|
||||
'Try again later or check your connection': '後でもう一度お試しいただくか、接続を確認してください'
|
||||
'Try again later or check your connection':
|
||||
'後でもう一度お試しいただくか、接続を確認してください',
|
||||
'Hide indirect': '間接通知を非表示'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -671,6 +671,7 @@ export default {
|
|||
'Zap Details': '잽 세부 정보',
|
||||
'Default trust score filter threshold ({{n}}%)': '기본 신뢰 점수 필터 임계값 ({{n}}%)',
|
||||
'No notes found': '노트를 찾을 수 없습니다',
|
||||
'Try again later or check your connection': '나중에 다시 시도하거나 연결을 확인하세요'
|
||||
'Try again later or check your connection': '나중에 다시 시도하거나 연결을 확인하세요',
|
||||
'Hide indirect': '간접 숨기기'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -680,8 +680,10 @@ export default {
|
|||
'Muted words': 'Wyciszone słowa',
|
||||
'Add muted word': 'Dodaj wyciszone słowo',
|
||||
'Zap Details': 'Szczegóły zapu',
|
||||
'Default trust score filter threshold ({{n}}%)': 'Domyślny próg filtra wyniku zaufania ({{n}}%)',
|
||||
'Default trust score filter threshold ({{n}}%)':
|
||||
'Domyślny próg filtra wyniku zaufania ({{n}}%)',
|
||||
'No notes found': 'Nie znaleziono notatek',
|
||||
'Try again later or check your connection': 'Spróbuj ponownie później lub sprawdź połączenie'
|
||||
'Try again later or check your connection': 'Spróbuj ponownie później lub sprawdź połączenie',
|
||||
'Hide indirect': 'Ukryj pośrednie'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -679,6 +679,8 @@ export default {
|
|||
'Default trust score filter threshold ({{n}}%)':
|
||||
'Limite padrão do filtro de pontuação de confiança ({{n}}%)',
|
||||
'No notes found': 'Nenhuma nota encontrada',
|
||||
'Try again later or check your connection': 'Tente novamente mais tarde ou verifique sua conexão'
|
||||
'Try again later or check your connection':
|
||||
'Tente novamente mais tarde ou verifique sua conexão',
|
||||
'Hide indirect': 'Ocultar indiretas'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -682,6 +682,8 @@ export default {
|
|||
'Default trust score filter threshold ({{n}}%)':
|
||||
'Limite predefinido do filtro de pontuação de confiança ({{n}}%)',
|
||||
'No notes found': 'Nenhuma nota encontrada',
|
||||
'Try again later or check your connection': 'Tente novamente mais tarde ou verifique a sua ligação'
|
||||
'Try again later or check your connection':
|
||||
'Tente novamente mais tarde ou verifique a sua ligação',
|
||||
'Hide indirect': 'Ocultar indiretas'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -682,6 +682,7 @@ export default {
|
|||
'Default trust score filter threshold ({{n}}%)':
|
||||
'Порог фильтра рейтинга доверия по умолчанию ({{n}}%)',
|
||||
'No notes found': 'Заметки не найдены',
|
||||
'Try again later or check your connection': 'Попробуйте позже или проверьте подключение'
|
||||
'Try again later or check your connection': 'Попробуйте позже или проверьте подключение',
|
||||
'Hide indirect': 'Скрыть косвенные'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -667,6 +667,7 @@ export default {
|
|||
'Default trust score filter threshold ({{n}}%)':
|
||||
'เกณฑ์ตัวกรองคะแนนความไว้วางใจเริ่มต้น ({{n}}%)',
|
||||
'No notes found': 'ไม่พบโน้ต',
|
||||
'Try again later or check your connection': 'ลองใหม่ภายหลังหรือตรวจสอบการเชื่อมต่อของคุณ'
|
||||
'Try again later or check your connection': 'ลองใหม่ภายหลังหรือตรวจสอบการเชื่อมต่อของคุณ',
|
||||
'Hide indirect': 'ซ่อนทางอ้อม'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -649,6 +649,7 @@ export default {
|
|||
'Zap Details': '打閃詳情',
|
||||
'Default trust score filter threshold ({{n}}%)': '預設信任分數過濾閾值 ({{n}}%)',
|
||||
'No notes found': '沒有找到筆記',
|
||||
'Try again later or check your connection': '請稍後重試或檢查網路連接'
|
||||
'Try again later or check your connection': '請稍後重試或檢查網路連接',
|
||||
'Hide indirect': '隱藏間接通知'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -654,6 +654,7 @@ export default {
|
|||
'Zap Details': '打闪详情',
|
||||
'Default trust score filter threshold ({{n}}%)': '默认信任分数过滤阈值 ({{n}}%)',
|
||||
'No notes found': '没有找到笔记',
|
||||
'Try again later or check your connection': '请稍后重试或检查网络连接'
|
||||
'Try again later or check your connection': '请稍后重试或检查网络连接',
|
||||
'Hide indirect': '隐藏间接通知'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ function NoteListPageTitlebar({
|
|||
layoutRef?.current?.scrollToTop('smooth')
|
||||
}
|
||||
}}
|
||||
className={showRelayDetails ? 'bg-accent/50' : ''}
|
||||
className={showRelayDetails ? 'bg-muted/40' : ''}
|
||||
>
|
||||
<Info />
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,21 @@
|
|||
import NotificationList from '@/components/NotificationList'
|
||||
import TrustScoreFilter from '@/components/TrustScoreFilter'
|
||||
import { SPECIAL_TRUST_SCORE_FILTER_ID } from '@/constants'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import PrimaryPageLayout from '@/layouts/PrimaryPageLayout'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { usePrimaryPage } from '@/PageManager'
|
||||
import {
|
||||
NotificationUserPreferenceContext,
|
||||
useNotificationUserPreference
|
||||
} from '@/providers/NotificationUserPreferenceProvider'
|
||||
import localStorage from '@/services/local-storage.service'
|
||||
import { TPageRef } from '@/types'
|
||||
import { Bell } from 'lucide-react'
|
||||
import { forwardRef, useEffect, useRef } from 'react'
|
||||
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const NotificationListPage = forwardRef<TPageRef>((_, ref) => {
|
||||
const { current } = usePrimaryPage()
|
||||
const [hideIndirect, setHideIndirect] = useState(localStorage.getHideIndirectNotifications())
|
||||
const firstRenderRef = useRef(true)
|
||||
const notificationListRef = useRef<{ refresh: () => void }>(null)
|
||||
|
||||
|
|
@ -20,7 +26,21 @@ const NotificationListPage = forwardRef<TPageRef>((_, ref) => {
|
|||
firstRenderRef.current = false
|
||||
}, [current])
|
||||
|
||||
const updateHideIndirect = useCallback(
|
||||
(enable: boolean) => {
|
||||
setHideIndirect(enable)
|
||||
localStorage.setHideIndirectNotifications(enable)
|
||||
},
|
||||
[setHideIndirect]
|
||||
)
|
||||
|
||||
return (
|
||||
<NotificationUserPreferenceContext.Provider
|
||||
value={{
|
||||
hideIndirect,
|
||||
updateHideIndirect
|
||||
}}
|
||||
>
|
||||
<PrimaryPageLayout
|
||||
ref={ref}
|
||||
pageName="notifications"
|
||||
|
|
@ -29,6 +49,7 @@ const NotificationListPage = forwardRef<TPageRef>((_, ref) => {
|
|||
>
|
||||
<NotificationList ref={notificationListRef} />
|
||||
</PrimaryPageLayout>
|
||||
</NotificationUserPreferenceContext.Provider>
|
||||
)
|
||||
})
|
||||
NotificationListPage.displayName = 'NotificationListPage'
|
||||
|
|
@ -43,7 +64,25 @@ function NotificationListPageTitlebar() {
|
|||
<Bell />
|
||||
<div className="text-lg font-semibold">{t('Notifications')}</div>
|
||||
</div>
|
||||
<TrustScoreFilter filterId={SPECIAL_TRUST_SCORE_FILTER_ID.NOTIFICATIONS} />
|
||||
<HideUnrelatedNotificationsToggle />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function HideUnrelatedNotificationsToggle() {
|
||||
const { t } = useTranslation()
|
||||
const { hideIndirect, updateHideIndirect } = useNotificationUserPreference()
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
'h-10 px-3 shrink-0 rounded-xl [&_svg]:size-5',
|
||||
hideIndirect ? 'text-foreground bg-muted/40' : 'text-muted-foreground'
|
||||
)}
|
||||
onClick={() => updateHideIndirect(!hideIndirect)}
|
||||
>
|
||||
{t('Hide indirect')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
14
src/providers/NotificationUserPreferenceProvider.tsx
Normal file
14
src/providers/NotificationUserPreferenceProvider.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { createContext, useContext } from 'react'
|
||||
|
||||
type TNotificationUserPreferenceContext = {
|
||||
hideIndirect: boolean
|
||||
updateHideIndirect: (enable: boolean) => void
|
||||
}
|
||||
|
||||
export const NotificationUserPreferenceContext =
|
||||
createContext<TNotificationUserPreferenceContext | null>(null)
|
||||
|
||||
export function useNotificationUserPreference() {
|
||||
const ctx = useContext(NotificationUserPreferenceContext)
|
||||
return ctx ?? { hideIndirect: false, updateHideIndirect: () => {} }
|
||||
}
|
||||
|
|
@ -68,6 +68,7 @@ class LocalStorageService {
|
|||
private mutedWords: string[] = []
|
||||
private minTrustScore: number = 0
|
||||
private minTrustScoreMap: Record<string, number> = {}
|
||||
private hideIndirectNotifications: boolean = false
|
||||
|
||||
constructor() {
|
||||
if (!LocalStorageService.instance) {
|
||||
|
|
@ -319,6 +320,9 @@ class LocalStorageService {
|
|||
}
|
||||
}
|
||||
|
||||
this.hideIndirectNotifications =
|
||||
window.localStorage.getItem(StorageKey.HIDE_INDIRECT_NOTIFICATIONS) === 'true'
|
||||
|
||||
// Clean up deprecated data
|
||||
window.localStorage.removeItem(StorageKey.PINNED_PUBKEYS)
|
||||
window.localStorage.removeItem(StorageKey.ACCOUNT_PROFILE_EVENT_MAP)
|
||||
|
|
@ -684,6 +688,15 @@ class LocalStorageService {
|
|||
this.mutedWords = words
|
||||
window.localStorage.setItem(StorageKey.MUTED_WORDS, JSON.stringify(this.mutedWords))
|
||||
}
|
||||
|
||||
getHideIndirectNotifications() {
|
||||
return this.hideIndirectNotifications
|
||||
}
|
||||
|
||||
setHideIndirectNotifications(onlyShow: boolean) {
|
||||
this.hideIndirectNotifications = onlyShow
|
||||
window.localStorage.setItem(StorageKey.HIDE_INDIRECT_NOTIFICATIONS, onlyShow.toString())
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new LocalStorageService()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue