feat: add NSFW display policy setting
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { useSecondaryPage } from '@/PageManager'
|
import { useSecondaryPage } from '@/PageManager'
|
||||||
import { ExtendedKind, SUPPORTED_KINDS } from '@/constants'
|
import { ExtendedKind, NSFW_DISPLAY_POLICY, SUPPORTED_KINDS } from '@/constants'
|
||||||
import { getParentStuff, isNsfwEvent } from '@/lib/event'
|
import { getParentStuff, isNsfwEvent } from '@/lib/event'
|
||||||
import { toExternalContent, toNote } from '@/lib/link'
|
import { toExternalContent, toNote } from '@/lib/link'
|
||||||
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
|
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
|
||||||
@@ -55,10 +55,14 @@ export default function Note({
|
|||||||
const { parentEventId, parentExternalContent } = useMemo(() => {
|
const { parentEventId, parentExternalContent } = useMemo(() => {
|
||||||
return getParentStuff(event)
|
return getParentStuff(event)
|
||||||
}, [event])
|
}, [event])
|
||||||
const { defaultShowNsfw } = useContentPolicy()
|
const { nsfwDisplayPolicy } = useContentPolicy()
|
||||||
const [showNsfw, setShowNsfw] = useState(false)
|
const [showNsfw, setShowNsfw] = useState(false)
|
||||||
const { mutePubkeySet } = useMuteList()
|
const { mutePubkeySet } = useMuteList()
|
||||||
const [showMuted, setShowMuted] = useState(false)
|
const [showMuted, setShowMuted] = useState(false)
|
||||||
|
const isNsfw = useMemo(
|
||||||
|
() => (nsfwDisplayPolicy === NSFW_DISPLAY_POLICY.SHOW ? false : isNsfwEvent(event)),
|
||||||
|
[event, nsfwDisplayPolicy]
|
||||||
|
)
|
||||||
|
|
||||||
let content: React.ReactNode
|
let content: React.ReactNode
|
||||||
if (
|
if (
|
||||||
@@ -72,7 +76,7 @@ export default function Note({
|
|||||||
content = <UnknownNote className="mt-2" event={event} />
|
content = <UnknownNote className="mt-2" event={event} />
|
||||||
} else if (mutePubkeySet.has(event.pubkey) && !showMuted) {
|
} else if (mutePubkeySet.has(event.pubkey) && !showMuted) {
|
||||||
content = <MutedNote show={() => setShowMuted(true)} />
|
content = <MutedNote show={() => setShowMuted(true)} />
|
||||||
} else if (!defaultShowNsfw && isNsfwEvent(event) && !showNsfw) {
|
} else if (isNsfw && !showNsfw) {
|
||||||
content = <NsfwNote show={() => setShowNsfw(true)} />
|
content = <NsfwNote show={() => setShowNsfw(true)} />
|
||||||
} else if (event.kind === kinds.Highlights) {
|
} else if (event.kind === kinds.Highlights) {
|
||||||
content = <Highlight className="mt-2" event={event} />
|
content = <Highlight className="mt-2" event={event} />
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Skeleton } from '@/components/ui/skeleton'
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
import { isMentioningMutedUsers } from '@/lib/event'
|
import { NSFW_DISPLAY_POLICY } from '@/constants'
|
||||||
|
import { isMentioningMutedUsers, isNsfwEvent } from '@/lib/event'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
|
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
|
||||||
import { useMuteList } from '@/providers/MuteListProvider'
|
import { useMuteList } from '@/providers/MuteListProvider'
|
||||||
@@ -22,7 +23,7 @@ export default function NoteCard({
|
|||||||
reposters?: string[]
|
reposters?: string[]
|
||||||
}) {
|
}) {
|
||||||
const { mutePubkeySet } = useMuteList()
|
const { mutePubkeySet } = useMuteList()
|
||||||
const { hideContentMentioningMutedUsers } = useContentPolicy()
|
const { hideContentMentioningMutedUsers, nsfwDisplayPolicy } = useContentPolicy()
|
||||||
const shouldHide = useMemo(() => {
|
const shouldHide = useMemo(() => {
|
||||||
if (filterMutedNotes && mutePubkeySet.has(event.pubkey)) {
|
if (filterMutedNotes && mutePubkeySet.has(event.pubkey)) {
|
||||||
return true
|
return true
|
||||||
@@ -30,8 +31,11 @@ export default function NoteCard({
|
|||||||
if (hideContentMentioningMutedUsers && isMentioningMutedUsers(event, mutePubkeySet)) {
|
if (hideContentMentioningMutedUsers && isMentioningMutedUsers(event, mutePubkeySet)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if (nsfwDisplayPolicy === NSFW_DISPLAY_POLICY.HIDE && isNsfwEvent(event)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}, [event, filterMutedNotes, mutePubkeySet])
|
}, [event, filterMutedNotes, mutePubkeySet, nsfwDisplayPolicy])
|
||||||
if (shouldHide) return null
|
if (shouldHide) return null
|
||||||
|
|
||||||
if (event.kind === kinds.Repost || event.kind === kinds.GenericRepost) {
|
if (event.kind === kinds.Repost || event.kind === kinds.GenericRepost) {
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ export const StorageKey = {
|
|||||||
TRANSLATION_SERVICE_CONFIG_MAP: 'translationServiceConfigMap',
|
TRANSLATION_SERVICE_CONFIG_MAP: 'translationServiceConfigMap',
|
||||||
MEDIA_UPLOAD_SERVICE_CONFIG_MAP: 'mediaUploadServiceConfigMap',
|
MEDIA_UPLOAD_SERVICE_CONFIG_MAP: 'mediaUploadServiceConfigMap',
|
||||||
HIDE_UNTRUSTED_NOTES: 'hideUntrustedNotes',
|
HIDE_UNTRUSTED_NOTES: 'hideUntrustedNotes',
|
||||||
DEFAULT_SHOW_NSFW: 'defaultShowNsfw',
|
|
||||||
DISMISSED_TOO_MANY_RELAYS_ALERT: 'dismissedTooManyRelaysAlert',
|
DISMISSED_TOO_MANY_RELAYS_ALERT: 'dismissedTooManyRelaysAlert',
|
||||||
SHOW_KINDS: 'showKinds',
|
SHOW_KINDS: 'showKinds',
|
||||||
SHOW_KINDS_VERSION: 'showKindsVersion',
|
SHOW_KINDS_VERSION: 'showKindsVersion',
|
||||||
@@ -43,6 +42,8 @@ export const StorageKey = {
|
|||||||
FILTER_OUT_ONION_RELAYS: 'filterOutOnionRelays',
|
FILTER_OUT_ONION_RELAYS: 'filterOutOnionRelays',
|
||||||
QUICK_REACTION: 'quickReaction',
|
QUICK_REACTION: 'quickReaction',
|
||||||
QUICK_REACTION_EMOJI: 'quickReactionEmoji',
|
QUICK_REACTION_EMOJI: 'quickReactionEmoji',
|
||||||
|
NSFW_DISPLAY_POLICY: 'nsfwDisplayPolicy',
|
||||||
|
DEFAULT_SHOW_NSFW: 'defaultShowNsfw', // deprecated
|
||||||
PINNED_PUBKEYS: 'pinnedPubkeys', // deprecated
|
PINNED_PUBKEYS: 'pinnedPubkeys', // deprecated
|
||||||
MEDIA_UPLOAD_SERVICE: 'mediaUploadService', // deprecated
|
MEDIA_UPLOAD_SERVICE: 'mediaUploadService', // deprecated
|
||||||
HIDE_UNTRUSTED_EVENTS: 'hideUntrustedEvents', // deprecated
|
HIDE_UNTRUSTED_EVENTS: 'hideUntrustedEvents', // deprecated
|
||||||
@@ -170,6 +171,12 @@ export const MEDIA_AUTO_LOAD_POLICY = {
|
|||||||
NEVER: 'never'
|
NEVER: 'never'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
export const NSFW_DISPLAY_POLICY = {
|
||||||
|
HIDE: 'hide',
|
||||||
|
HIDE_CONTENT: 'hide_content',
|
||||||
|
SHOW: 'show'
|
||||||
|
} as const
|
||||||
|
|
||||||
export const MAX_PINNED_NOTES = 10
|
export const MAX_PINNED_NOTES = 10
|
||||||
|
|
||||||
export const PRIMARY_COLORS = {
|
export const PRIMARY_COLORS = {
|
||||||
|
|||||||
@@ -571,6 +571,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'إذا تم التمكين، يمكنك التفاعل بنقرة واحدة. اضغط مع الاستمرار للمزيد من الخيارات',
|
'إذا تم التمكين، يمكنك التفاعل بنقرة واحدة. اضغط مع الاستمرار للمزيد من الخيارات',
|
||||||
'Quick reaction emoji': 'رمز تعبيري للرد السريع',
|
'Quick reaction emoji': 'رمز تعبيري للرد السريع',
|
||||||
'Select emoji': 'اختر رمز تعبيري'
|
'Select emoji': 'اختر رمز تعبيري',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -587,6 +587,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'Wenn aktiviert, können Sie mit einem Klick reagieren. Klicken und halten Sie für weitere Optionen',
|
'Wenn aktiviert, können Sie mit einem Klick reagieren. Klicken und halten Sie für weitere Optionen',
|
||||||
'Quick reaction emoji': 'Schnellreaktions-Emoji',
|
'Quick reaction emoji': 'Schnellreaktions-Emoji',
|
||||||
'Select emoji': 'Emoji auswählen'
|
'Select emoji': 'Emoji auswählen',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -574,6 +574,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'If enabled, you can react with a single click. Click and hold for more options',
|
'If enabled, you can react with a single click. Click and hold for more options',
|
||||||
'Quick reaction emoji': 'Quick reaction emoji',
|
'Quick reaction emoji': 'Quick reaction emoji',
|
||||||
'Select emoji': 'Select emoji'
|
'Select emoji': 'Select emoji',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -583,6 +583,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'Si está habilitado, puedes reaccionar con un solo clic. Mantén presionado para más opciones',
|
'Si está habilitado, puedes reaccionar con un solo clic. Mantén presionado para más opciones',
|
||||||
'Quick reaction emoji': 'Emoji de reacción rápida',
|
'Quick reaction emoji': 'Emoji de reacción rápida',
|
||||||
'Select emoji': 'Seleccionar emoji'
|
'Select emoji': 'Seleccionar emoji',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,6 +577,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'اگر فعال باشد، میتوانید با یک کلیک واکنش نشان دهید. برای گزینههای بیشتر کلیک کنید و نگه دارید',
|
'اگر فعال باشد، میتوانید با یک کلیک واکنش نشان دهید. برای گزینههای بیشتر کلیک کنید و نگه دارید',
|
||||||
'Quick reaction emoji': 'ایموجی واکنش سریع',
|
'Quick reaction emoji': 'ایموجی واکنش سریع',
|
||||||
'Select emoji': 'انتخاب ایموجی'
|
'Select emoji': 'انتخاب ایموجی',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -584,8 +584,13 @@ export default {
|
|||||||
notes: 'notes',
|
notes: 'notes',
|
||||||
'Quick reaction': 'Réaction rapide',
|
'Quick reaction': 'Réaction rapide',
|
||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'Si activé, vous pouvez réagir en un seul clic. Maintenez enfoncé pour plus d\'options',
|
"Si activé, vous pouvez réagir en un seul clic. Maintenez enfoncé pour plus d'options",
|
||||||
'Quick reaction emoji': 'Emoji de réaction rapide',
|
'Quick reaction emoji': 'Emoji de réaction rapide',
|
||||||
'Select emoji': 'Sélectionner un emoji'
|
'Select emoji': 'Sélectionner un emoji',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -578,6 +578,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'यदि सक्षम है, तो आप एक क्लिक से प्रतिक्रिया दे सकते हैं। अधिक विकल्पों के लिए क्लिक करें और रोकें',
|
'यदि सक्षम है, तो आप एक क्लिक से प्रतिक्रिया दे सकते हैं। अधिक विकल्पों के लिए क्लिक करें और रोकें',
|
||||||
'Quick reaction emoji': 'त्वरित प्रतिक्रिया इमोजी',
|
'Quick reaction emoji': 'त्वरित प्रतिक्रिया इमोजी',
|
||||||
'Select emoji': 'इमोजी चुनें'
|
'Select emoji': 'इमोजी चुनें',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -572,6 +572,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'Ha engedélyezve van, egy kattintással reagálhat. Tartsa lenyomva további lehetőségekért',
|
'Ha engedélyezve van, egy kattintással reagálhat. Tartsa lenyomva további lehetőségekért',
|
||||||
'Quick reaction emoji': 'Gyors reakció emoji',
|
'Quick reaction emoji': 'Gyors reakció emoji',
|
||||||
'Select emoji': 'Emoji kiválasztása'
|
'Select emoji': 'Emoji kiválasztása',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -582,6 +582,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'Se abilitato, puoi reagire con un solo clic. Fai clic e tieni premuto per altre opzioni',
|
'Se abilitato, puoi reagire con un solo clic. Fai clic e tieni premuto per altre opzioni',
|
||||||
'Quick reaction emoji': 'Emoji reazione rapida',
|
'Quick reaction emoji': 'Emoji reazione rapida',
|
||||||
'Select emoji': 'Seleziona emoji'
|
'Select emoji': 'Seleziona emoji',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,6 +577,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'有効にすると、ワンクリックでリアクションできます。長押しで他のオプションを表示',
|
'有効にすると、ワンクリックでリアクションできます。長押しで他のオプションを表示',
|
||||||
'Quick reaction emoji': 'クイックリアクション絵文字',
|
'Quick reaction emoji': 'クイックリアクション絵文字',
|
||||||
'Select emoji': '絵文字を選択'
|
'Select emoji': '絵文字を選択',
|
||||||
|
'NSFW content display': 'NSFWコンテンツの表示',
|
||||||
|
'Hide completely': '完全に非表示',
|
||||||
|
'Show but hide content': '表示するがコンテンツを非表示',
|
||||||
|
'Show directly': '直接表示',
|
||||||
|
'Click to view': 'クリックして表示'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -576,6 +576,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'활성화하면 한 번의 클릭으로 반응할 수 있습니다. 더 많은 옵션을 보려면 길게 누르세요',
|
'활성화하면 한 번의 클릭으로 반응할 수 있습니다. 더 많은 옵션을 보려면 길게 누르세요',
|
||||||
'Quick reaction emoji': '빠른 반응 이모지',
|
'Quick reaction emoji': '빠른 반응 이모지',
|
||||||
'Select emoji': '이모지 선택'
|
'Select emoji': '이모지 선택',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -583,6 +583,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'Jeśli włączone, możesz zareagować jednym kliknięciem. Kliknij i przytrzymaj, aby uzyskać więcej opcji',
|
'Jeśli włączone, możesz zareagować jednym kliknięciem. Kliknij i przytrzymaj, aby uzyskać więcej opcji',
|
||||||
'Quick reaction emoji': 'Emoji szybkiej reakcji',
|
'Quick reaction emoji': 'Emoji szybkiej reakcji',
|
||||||
'Select emoji': 'Wybierz emoji'
|
'Select emoji': 'Wybierz emoji',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -578,6 +578,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'Se ativado, você pode reagir com um único clique. Clique e segure para mais opções',
|
'Se ativado, você pode reagir com um único clique. Clique e segure para mais opções',
|
||||||
'Quick reaction emoji': 'Emoji de reação rápida',
|
'Quick reaction emoji': 'Emoji de reação rápida',
|
||||||
'Select emoji': 'Selecionar emoji'
|
'Select emoji': 'Selecionar emoji',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -581,6 +581,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'Se ativado, pode reagir com um único clique. Clique e mantenha premido para mais opções',
|
'Se ativado, pode reagir com um único clique. Clique e mantenha premido para mais opções',
|
||||||
'Quick reaction emoji': 'Emoji de reação rápida',
|
'Quick reaction emoji': 'Emoji de reação rápida',
|
||||||
'Select emoji': 'Selecionar emoji'
|
'Select emoji': 'Selecionar emoji',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -583,6 +583,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'Если включено, вы можете реагировать одним щелчком. Нажмите и удерживайте для дополнительных параметров',
|
'Если включено, вы можете реагировать одним щелчком. Нажмите и удерживайте для дополнительных параметров',
|
||||||
'Quick reaction emoji': 'Эмодзи быстрой реакции',
|
'Quick reaction emoji': 'Эмодзи быстрой реакции',
|
||||||
'Select emoji': 'Выбрать эмодзи'
|
'Select emoji': 'Выбрать эмодзи',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -570,6 +570,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'หากเปิดใช้งาน คุณสามารถรีแอคได้ด้วยคลิกเดียว คลิกค้างไว้สำหรับตัวเลือกเพิ่มเติม',
|
'หากเปิดใช้งาน คุณสามารถรีแอคได้ด้วยคลิกเดียว คลิกค้างไว้สำหรับตัวเลือกเพิ่มเติม',
|
||||||
'Quick reaction emoji': 'อีโมจิรีแอคชั่นด่วน',
|
'Quick reaction emoji': 'อีโมจิรีแอคชั่นด่วน',
|
||||||
'Select emoji': 'เลือกอีโมจิ'
|
'Select emoji': 'เลือกอีโมจิ',
|
||||||
|
'NSFW content display': 'NSFW content display',
|
||||||
|
'Hide completely': 'Hide completely',
|
||||||
|
'Show but hide content': 'Show but hide content',
|
||||||
|
'Show directly': 'Show directly',
|
||||||
|
'Click to view': 'Click to view'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -563,6 +563,11 @@ export default {
|
|||||||
'If enabled, you can react with a single click. Click and hold for more options':
|
'If enabled, you can react with a single click. Click and hold for more options':
|
||||||
'启用后,您可以通过单击进行点赞。长按以获取更多选项',
|
'启用后,您可以通过单击进行点赞。长按以获取更多选项',
|
||||||
'Quick reaction emoji': '快速点赞表情',
|
'Quick reaction emoji': '快速点赞表情',
|
||||||
'Select emoji': '选择表情'
|
'Select emoji': '选择表情',
|
||||||
|
'NSFW content display': 'NSFW 内容显示',
|
||||||
|
'Hide completely': '完全隐藏',
|
||||||
|
'Show but hide content': '显示但隐藏内容',
|
||||||
|
'Show directly': '直接显示',
|
||||||
|
'Click to view': '点击查看'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import { Button } from '@/components/ui/button'
|
|||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger } from '@/components/ui/select'
|
import { Select, SelectContent, SelectItem, SelectTrigger } from '@/components/ui/select'
|
||||||
import { Switch } from '@/components/ui/switch'
|
import { Switch } from '@/components/ui/switch'
|
||||||
import { MEDIA_AUTO_LOAD_POLICY } from '@/constants'
|
import { MEDIA_AUTO_LOAD_POLICY, NSFW_DISPLAY_POLICY } from '@/constants'
|
||||||
import { LocalizedLanguageNames, TLanguage } from '@/i18n'
|
import { LocalizedLanguageNames, TLanguage } from '@/i18n'
|
||||||
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
|
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
|
||||||
import { cn, isSupportCheckConnectionType } from '@/lib/utils'
|
import { cn, isSupportCheckConnectionType } from '@/lib/utils'
|
||||||
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
|
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
|
||||||
import { useUserPreferences } from '@/providers/UserPreferencesProvider'
|
import { useUserPreferences } from '@/providers/UserPreferencesProvider'
|
||||||
import { useUserTrust } from '@/providers/UserTrustProvider'
|
import { useUserTrust } from '@/providers/UserTrustProvider'
|
||||||
import { TMediaAutoLoadPolicy } from '@/types'
|
import { TMediaAutoLoadPolicy, TNsfwDisplayPolicy } from '@/types'
|
||||||
import { SelectValue } from '@radix-ui/react-select'
|
import { SelectValue } from '@radix-ui/react-select'
|
||||||
import { RotateCcw } from 'lucide-react'
|
import { RotateCcw } from 'lucide-react'
|
||||||
import { forwardRef, HTMLProps, useState } from 'react'
|
import { forwardRef, HTMLProps, useState } from 'react'
|
||||||
@@ -23,8 +23,8 @@ const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
|
|||||||
const {
|
const {
|
||||||
autoplay,
|
autoplay,
|
||||||
setAutoplay,
|
setAutoplay,
|
||||||
defaultShowNsfw,
|
nsfwDisplayPolicy,
|
||||||
setDefaultShowNsfw,
|
setNsfwDisplayPolicy,
|
||||||
hideContentMentioningMutedUsers,
|
hideContentMentioningMutedUsers,
|
||||||
setHideContentMentioningMutedUsers,
|
setHideContentMentioningMutedUsers,
|
||||||
mediaAutoLoadPolicy,
|
mediaAutoLoadPolicy,
|
||||||
@@ -110,10 +110,24 @@ const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
|
|||||||
/>
|
/>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
<SettingItem>
|
<SettingItem>
|
||||||
<Label htmlFor="show-nsfw" className="text-base font-normal">
|
<Label htmlFor="nsfw-display-policy" className="text-base font-normal">
|
||||||
{t('Show NSFW content by default')}
|
{t('NSFW content display')}
|
||||||
</Label>
|
</Label>
|
||||||
<Switch id="show-nsfw" checked={defaultShowNsfw} onCheckedChange={setDefaultShowNsfw} />
|
<Select
|
||||||
|
value={nsfwDisplayPolicy}
|
||||||
|
onValueChange={(value: TNsfwDisplayPolicy) => setNsfwDisplayPolicy(value)}
|
||||||
|
>
|
||||||
|
<SelectTrigger id="nsfw-display-policy" className="w-48">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value={NSFW_DISPLAY_POLICY.HIDE}>{t('Hide completely')}</SelectItem>
|
||||||
|
<SelectItem value={NSFW_DISPLAY_POLICY.HIDE_CONTENT}>
|
||||||
|
{t('Show but hide content')}
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value={NSFW_DISPLAY_POLICY.SHOW}>{t('Show directly')}</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
<SettingItem>
|
<SettingItem>
|
||||||
<Label htmlFor="quick-reaction" className="text-base font-normal">
|
<Label htmlFor="quick-reaction" className="text-base font-normal">
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { MEDIA_AUTO_LOAD_POLICY } from '@/constants'
|
import { MEDIA_AUTO_LOAD_POLICY } from '@/constants'
|
||||||
import storage from '@/services/local-storage.service'
|
import storage from '@/services/local-storage.service'
|
||||||
import { TMediaAutoLoadPolicy } from '@/types'
|
import { TMediaAutoLoadPolicy, TNsfwDisplayPolicy } from '@/types'
|
||||||
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
|
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
type TContentPolicyContext = {
|
type TContentPolicyContext = {
|
||||||
autoplay: boolean
|
autoplay: boolean
|
||||||
setAutoplay: (autoplay: boolean) => void
|
setAutoplay: (autoplay: boolean) => void
|
||||||
|
|
||||||
defaultShowNsfw: boolean
|
nsfwDisplayPolicy: TNsfwDisplayPolicy
|
||||||
setDefaultShowNsfw: (showNsfw: boolean) => void
|
setNsfwDisplayPolicy: (policy: TNsfwDisplayPolicy) => void
|
||||||
|
|
||||||
hideContentMentioningMutedUsers?: boolean
|
hideContentMentioningMutedUsers?: boolean
|
||||||
setHideContentMentioningMutedUsers?: (hide: boolean) => void
|
setHideContentMentioningMutedUsers?: (hide: boolean) => void
|
||||||
@@ -33,7 +33,7 @@ export const useContentPolicy = () => {
|
|||||||
|
|
||||||
export function ContentPolicyProvider({ children }: { children: React.ReactNode }) {
|
export function ContentPolicyProvider({ children }: { children: React.ReactNode }) {
|
||||||
const [autoplay, setAutoplay] = useState(storage.getAutoplay())
|
const [autoplay, setAutoplay] = useState(storage.getAutoplay())
|
||||||
const [defaultShowNsfw, setDefaultShowNsfw] = useState(storage.getDefaultShowNsfw())
|
const [nsfwDisplayPolicy, setNsfwDisplayPolicy] = useState(storage.getNsfwDisplayPolicy())
|
||||||
const [hideContentMentioningMutedUsers, setHideContentMentioningMutedUsers] = useState(
|
const [hideContentMentioningMutedUsers, setHideContentMentioningMutedUsers] = useState(
|
||||||
storage.getHideContentMentioningMutedUsers()
|
storage.getHideContentMentioningMutedUsers()
|
||||||
)
|
)
|
||||||
@@ -72,9 +72,9 @@ export function ContentPolicyProvider({ children }: { children: React.ReactNode
|
|||||||
setAutoplay(autoplay)
|
setAutoplay(autoplay)
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateDefaultShowNsfw = (defaultShowNsfw: boolean) => {
|
const updateNsfwDisplayPolicy = (policy: TNsfwDisplayPolicy) => {
|
||||||
storage.setDefaultShowNsfw(defaultShowNsfw)
|
storage.setNsfwDisplayPolicy(policy)
|
||||||
setDefaultShowNsfw(defaultShowNsfw)
|
setNsfwDisplayPolicy(policy)
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateHideContentMentioningMutedUsers = (hide: boolean) => {
|
const updateHideContentMentioningMutedUsers = (hide: boolean) => {
|
||||||
@@ -97,8 +97,8 @@ export function ContentPolicyProvider({ children }: { children: React.ReactNode
|
|||||||
value={{
|
value={{
|
||||||
autoplay,
|
autoplay,
|
||||||
setAutoplay: updateAutoplay,
|
setAutoplay: updateAutoplay,
|
||||||
defaultShowNsfw,
|
nsfwDisplayPolicy,
|
||||||
setDefaultShowNsfw: updateDefaultShowNsfw,
|
setNsfwDisplayPolicy: updateNsfwDisplayPolicy,
|
||||||
hideContentMentioningMutedUsers,
|
hideContentMentioningMutedUsers,
|
||||||
setHideContentMentioningMutedUsers: updateHideContentMentioningMutedUsers,
|
setHideContentMentioningMutedUsers: updateHideContentMentioningMutedUsers,
|
||||||
autoLoadMedia,
|
autoLoadMedia,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
ExtendedKind,
|
ExtendedKind,
|
||||||
MEDIA_AUTO_LOAD_POLICY,
|
MEDIA_AUTO_LOAD_POLICY,
|
||||||
NOTIFICATION_LIST_STYLE,
|
NOTIFICATION_LIST_STYLE,
|
||||||
|
NSFW_DISPLAY_POLICY,
|
||||||
StorageKey,
|
StorageKey,
|
||||||
TPrimaryColor
|
TPrimaryColor
|
||||||
} from '@/constants'
|
} from '@/constants'
|
||||||
@@ -19,6 +20,7 @@ import {
|
|||||||
TMediaAutoLoadPolicy,
|
TMediaAutoLoadPolicy,
|
||||||
TMediaUploadServiceConfig,
|
TMediaUploadServiceConfig,
|
||||||
TNoteListMode,
|
TNoteListMode,
|
||||||
|
TNsfwDisplayPolicy,
|
||||||
TNotificationStyle,
|
TNotificationStyle,
|
||||||
TRelaySet,
|
TRelaySet,
|
||||||
TThemeSetting,
|
TThemeSetting,
|
||||||
@@ -46,7 +48,6 @@ class LocalStorageService {
|
|||||||
private hideUntrustedNotes: boolean = false
|
private hideUntrustedNotes: boolean = false
|
||||||
private translationServiceConfigMap: Record<string, TTranslationServiceConfig> = {}
|
private translationServiceConfigMap: Record<string, TTranslationServiceConfig> = {}
|
||||||
private mediaUploadServiceConfigMap: Record<string, TMediaUploadServiceConfig> = {}
|
private mediaUploadServiceConfigMap: Record<string, TMediaUploadServiceConfig> = {}
|
||||||
private defaultShowNsfw: boolean = false
|
|
||||||
private dismissedTooManyRelaysAlert: boolean = false
|
private dismissedTooManyRelaysAlert: boolean = false
|
||||||
private showKinds: number[] = []
|
private showKinds: number[] = []
|
||||||
private hideContentMentioningMutedUsers: boolean = false
|
private hideContentMentioningMutedUsers: boolean = false
|
||||||
@@ -60,6 +61,7 @@ class LocalStorageService {
|
|||||||
private filterOutOnionRelays: boolean = !isTorBrowser()
|
private filterOutOnionRelays: boolean = !isTorBrowser()
|
||||||
private quickReaction: boolean = false
|
private quickReaction: boolean = false
|
||||||
private quickReactionEmoji: string | TEmoji = '+'
|
private quickReactionEmoji: string | TEmoji = '+'
|
||||||
|
private nsfwDisplayPolicy: TNsfwDisplayPolicy = NSFW_DISPLAY_POLICY.HIDE_CONTENT
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (!LocalStorageService.instance) {
|
if (!LocalStorageService.instance) {
|
||||||
@@ -161,7 +163,20 @@ class LocalStorageService {
|
|||||||
this.mediaUploadServiceConfigMap = JSON.parse(mediaUploadServiceConfigMapStr)
|
this.mediaUploadServiceConfigMap = JSON.parse(mediaUploadServiceConfigMapStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.defaultShowNsfw = window.localStorage.getItem(StorageKey.DEFAULT_SHOW_NSFW) === 'true'
|
// Migrate old boolean setting to new policy
|
||||||
|
const nsfwDisplayPolicyStr = window.localStorage.getItem(StorageKey.NSFW_DISPLAY_POLICY)
|
||||||
|
if (
|
||||||
|
nsfwDisplayPolicyStr &&
|
||||||
|
Object.values(NSFW_DISPLAY_POLICY).includes(nsfwDisplayPolicyStr as TNsfwDisplayPolicy)
|
||||||
|
) {
|
||||||
|
this.nsfwDisplayPolicy = nsfwDisplayPolicyStr as TNsfwDisplayPolicy
|
||||||
|
} else {
|
||||||
|
// Migration: convert old boolean to new policy
|
||||||
|
const defaultShowNsfwStr = window.localStorage.getItem(StorageKey.DEFAULT_SHOW_NSFW)
|
||||||
|
this.nsfwDisplayPolicy =
|
||||||
|
defaultShowNsfwStr === 'true' ? NSFW_DISPLAY_POLICY.SHOW : NSFW_DISPLAY_POLICY.HIDE_CONTENT
|
||||||
|
window.localStorage.setItem(StorageKey.NSFW_DISPLAY_POLICY, this.nsfwDisplayPolicy)
|
||||||
|
}
|
||||||
|
|
||||||
this.dismissedTooManyRelaysAlert =
|
this.dismissedTooManyRelaysAlert =
|
||||||
window.localStorage.getItem(StorageKey.DISMISSED_TOO_MANY_RELAYS_ALERT) === 'true'
|
window.localStorage.getItem(StorageKey.DISMISSED_TOO_MANY_RELAYS_ALERT) === 'true'
|
||||||
@@ -458,15 +473,6 @@ class LocalStorageService {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultShowNsfw() {
|
|
||||||
return this.defaultShowNsfw
|
|
||||||
}
|
|
||||||
|
|
||||||
setDefaultShowNsfw(defaultShowNsfw: boolean) {
|
|
||||||
this.defaultShowNsfw = defaultShowNsfw
|
|
||||||
window.localStorage.setItem(StorageKey.DEFAULT_SHOW_NSFW, defaultShowNsfw.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
getDismissedTooManyRelaysAlert() {
|
getDismissedTooManyRelaysAlert() {
|
||||||
return this.dismissedTooManyRelaysAlert
|
return this.dismissedTooManyRelaysAlert
|
||||||
}
|
}
|
||||||
@@ -592,6 +598,15 @@ class LocalStorageService {
|
|||||||
typeof emoji === 'string' ? emoji : JSON.stringify(emoji)
|
typeof emoji === 'string' ? emoji : JSON.stringify(emoji)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNsfwDisplayPolicy() {
|
||||||
|
return this.nsfwDisplayPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
setNsfwDisplayPolicy(policy: TNsfwDisplayPolicy) {
|
||||||
|
this.nsfwDisplayPolicy = policy
|
||||||
|
window.localStorage.setItem(StorageKey.NSFW_DISPLAY_POLICY, policy)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = new LocalStorageService()
|
const instance = new LocalStorageService()
|
||||||
|
|||||||
5
src/types/index.d.ts
vendored
5
src/types/index.d.ts
vendored
@@ -1,5 +1,5 @@
|
|||||||
import { Event, Filter, VerifiedEvent } from 'nostr-tools'
|
import { Event, Filter, VerifiedEvent } from 'nostr-tools'
|
||||||
import { MEDIA_AUTO_LOAD_POLICY, NOTIFICATION_LIST_STYLE, POLL_TYPE } from '../constants'
|
import { MEDIA_AUTO_LOAD_POLICY, NOTIFICATION_LIST_STYLE, NSFW_DISPLAY_POLICY, POLL_TYPE } from '../constants'
|
||||||
|
|
||||||
export type TSubRequestFilter = Omit<Filter, 'since' | 'until'> & { limit: number }
|
export type TSubRequestFilter = Omit<Filter, 'since' | 'until'> & { limit: number }
|
||||||
|
|
||||||
@@ -205,3 +205,6 @@ export type TAwesomeRelayCollection = {
|
|||||||
|
|
||||||
export type TMediaAutoLoadPolicy =
|
export type TMediaAutoLoadPolicy =
|
||||||
(typeof MEDIA_AUTO_LOAD_POLICY)[keyof typeof MEDIA_AUTO_LOAD_POLICY]
|
(typeof MEDIA_AUTO_LOAD_POLICY)[keyof typeof MEDIA_AUTO_LOAD_POLICY]
|
||||||
|
|
||||||
|
export type TNsfwDisplayPolicy =
|
||||||
|
(typeof NSFW_DISPLAY_POLICY)[keyof typeof NSFW_DISPLAY_POLICY]
|
||||||
|
|||||||
Reference in New Issue
Block a user