fix(blog): nsfw filtering, use L tag instead nsfw
All checks were successful
Release to Staging / build_and_release (push) Successful in 54s
All checks were successful
Release to Staging / build_and_release (push) Successful in 54s
This commit is contained in:
parent
352179f1d9
commit
b49ae9537b
@ -1,4 +1,5 @@
|
||||
import { NDKFilter } from '@nostr-dev-kit/ndk'
|
||||
import { PROFILE_BLOG_FILTER_LIMIT } from '../../constants'
|
||||
import { NDKContextType } from 'contexts/NDKContext'
|
||||
import { kinds, nip19 } from 'nostr-tools'
|
||||
import { LoaderFunctionArgs, redirect } from 'react-router-dom'
|
||||
@ -59,33 +60,33 @@ export const blogRouteLoader =
|
||||
) as FilterOptions
|
||||
|
||||
// Fetch 4 in case the current blog is included in the latest
|
||||
const latestModsFilter: NDKFilter = {
|
||||
const latestFilter: NDKFilter = {
|
||||
authors: [pubkey],
|
||||
kinds: [kinds.LongFormArticle],
|
||||
limit: 4
|
||||
}
|
||||
// Add source filter
|
||||
if (filterOptions.source === window.location.host) {
|
||||
latestModsFilter['#r'] = [filterOptions.source]
|
||||
latestFilter['#r'] = [filterOptions.source]
|
||||
}
|
||||
// Filter by NSFW tag
|
||||
// NSFWFilter.Only_NSFW -> fetch with content-warning label
|
||||
// NSFWFilter.Show_NSFW -> filter not needed
|
||||
// NSFWFilter.Only_NSFW -> true
|
||||
// NSFWFilter.Hide_NSFW -> false
|
||||
if (filterOptions.nsfw !== NSFWFilter.Show_NSFW) {
|
||||
latestModsFilter['#nsfw'] = [
|
||||
(filterOptions.nsfw === NSFWFilter.Only_NSFW).toString()
|
||||
]
|
||||
// NSFWFilter.Hide_NSFW -> up the limit and filter after fetch
|
||||
if (filterOptions.nsfw === NSFWFilter.Only_NSFW) {
|
||||
latestFilter['#L'] = ['content-warning']
|
||||
} else if (filterOptions.nsfw === NSFWFilter.Hide_NSFW) {
|
||||
// Up the limit in case we fetch multiple NSFW blogs
|
||||
latestFilter.limit = PROFILE_BLOG_FILTER_LIMIT
|
||||
}
|
||||
|
||||
// Parallel fetch blog event, latest events, mute, and nsfw lists in parallel
|
||||
const settled = await Promise.allSettled([
|
||||
ndkContext.fetchEvent(filter),
|
||||
ndkContext.fetchEvents(latestModsFilter),
|
||||
ndkContext.fetchEvents(latestFilter),
|
||||
ndkContext.getMuteLists(loggedInUserPubkey), // Pass pubkey for logged-in users
|
||||
ndkContext.getNSFWList()
|
||||
])
|
||||
|
||||
const result: BlogPageLoaderResult = {
|
||||
blog: undefined,
|
||||
latest: [],
|
||||
@ -120,6 +121,9 @@ export const blogRouteLoader =
|
||||
result.latest = fetchEventsResult.value
|
||||
.map(extractBlogCardDetails)
|
||||
.filter((b) => b.id !== result.blog?.id) // Filter out current blog if present
|
||||
.filter(
|
||||
(b) => !(b.nsfw && filterOptions.nsfw === NSFWFilter.Hide_NSFW)
|
||||
) // Filter out the NSFW if selected
|
||||
.slice(0, 3) // Take only three
|
||||
} else if (fetchEventsResult.status === 'rejected') {
|
||||
log(
|
||||
|
@ -6,7 +6,7 @@ import { Swiper, SwiperSlide } from 'swiper/react'
|
||||
import { BlogCard } from '../components/BlogCard'
|
||||
import { GameCard } from '../components/GameCard'
|
||||
import { ModCard } from '../components/ModCard'
|
||||
import { LANDING_PAGE_DATA } from '../constants'
|
||||
import { LANDING_PAGE_DATA, PROFILE_BLOG_FILTER_LIMIT } from '../constants'
|
||||
import {
|
||||
useDidMount,
|
||||
useGames,
|
||||
@ -31,11 +31,7 @@ import '../styles/SimpleSlider.css'
|
||||
import '../styles/styles.css'
|
||||
|
||||
// Import Swiper styles
|
||||
import {
|
||||
filterForEventsTaggingId,
|
||||
NDKEvent,
|
||||
NDKFilter
|
||||
} from '@nostr-dev-kit/ndk'
|
||||
import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk'
|
||||
import 'swiper/css'
|
||||
import 'swiper/css/navigation'
|
||||
import 'swiper/css/pagination'
|
||||
@ -332,38 +328,34 @@ const DisplayLatestBlogs = () => {
|
||||
// Show maximum of 4 blog posts
|
||||
// 2 should be featured and the most recent 2 from blog npubs
|
||||
// Populate the filter from known naddr (constants.ts)
|
||||
const filters: NDKFilter[] = []
|
||||
const filter: NDKFilter = {
|
||||
kinds: [kinds.LongFormArticle],
|
||||
authors: [],
|
||||
'#d': []
|
||||
}
|
||||
for (let i = 0; i < LANDING_PAGE_DATA.featuredBlogPosts.length; i++) {
|
||||
try {
|
||||
const naddr = LANDING_PAGE_DATA.featuredBlogPosts[i]
|
||||
const filterId = filterForEventsTaggingId(naddr)
|
||||
if (filterId) {
|
||||
filters.push(filterId)
|
||||
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
|
||||
const { pubkey, identifier } = decoded.data
|
||||
if (!filter.authors?.includes(pubkey)) {
|
||||
filter.authors?.push(pubkey)
|
||||
}
|
||||
if (!filter.authors?.includes(identifier)) {
|
||||
filter['#d']?.push(identifier)
|
||||
}
|
||||
} catch (error) {
|
||||
// Silently ignore
|
||||
}
|
||||
}
|
||||
// Create a single filter based on multiple #a's
|
||||
const filter = filters.reduce(
|
||||
(filter, id) => {
|
||||
const a = id['#a']
|
||||
if (a) {
|
||||
filter['#a']?.push(a[0])
|
||||
}
|
||||
return filter
|
||||
},
|
||||
{
|
||||
'#a': []
|
||||
} as NDKFilter
|
||||
)
|
||||
|
||||
// Prepare filter for the latest
|
||||
const blogNpubs = import.meta.env.VITE_BLOG_NPUBS.split(',')
|
||||
const blogHexkeys = blogNpubs
|
||||
.map(npubToHex)
|
||||
.filter((hexkey) => hexkey !== null)
|
||||
|
||||
// We fetch 4 posts in case of duplicates (from featured)
|
||||
// We fetch more posts in case of duplicates (from featured)
|
||||
const latestFilter: NDKFilter = {
|
||||
authors: blogHexkeys,
|
||||
kinds: [kinds.LongFormArticle],
|
||||
@ -371,17 +363,15 @@ const DisplayLatestBlogs = () => {
|
||||
}
|
||||
|
||||
// Filter by NSFW tag
|
||||
// NSFWFilter.Show_NSFW -> filter not needed
|
||||
// NSFWFilter.Only_NSFW -> true
|
||||
// NSFWFilter.Hide_NSFW -> false
|
||||
if (filterOptions.nsfw !== NSFWFilter.Show_NSFW) {
|
||||
latestFilter['#nsfw'] = [
|
||||
(filterOptions.nsfw === NSFWFilter.Only_NSFW).toString()
|
||||
]
|
||||
if (filterOptions.nsfw === NSFWFilter.Only_NSFW) {
|
||||
latestFilter['#L'] = ['content-warning']
|
||||
} else if (filterOptions.nsfw === NSFWFilter.Hide_NSFW) {
|
||||
// Up the limit in case we fetch multiple NSFW blogs
|
||||
latestFilter.limit = PROFILE_BLOG_FILTER_LIMIT
|
||||
}
|
||||
|
||||
const results = await Promise.allSettled([
|
||||
fetchEvents({ ...filter, kinds: [kinds.LongFormArticle] }),
|
||||
fetchEvents(filter),
|
||||
fetchEvents(latestFilter)
|
||||
])
|
||||
|
||||
@ -403,9 +393,13 @@ const DisplayLatestBlogs = () => {
|
||||
}, new Map())
|
||||
.values()
|
||||
)
|
||||
const latest = unique.slice(0, 4)
|
||||
.map(extractBlogCardDetails)
|
||||
.filter(
|
||||
(b) => !(b.nsfw && filterOptions.nsfw === NSFWFilter.Hide_NSFW)
|
||||
)
|
||||
|
||||
setBlogs(latest.map(extractBlogCardDetails))
|
||||
const latest = unique.slice(0, 4)
|
||||
setBlogs(latest)
|
||||
} catch (error) {
|
||||
log(
|
||||
true,
|
||||
|
@ -704,10 +704,8 @@ const ProfileTabBlogs = () => {
|
||||
filter['#r'] = [host]
|
||||
}
|
||||
|
||||
if (filterOptions.nsfw !== NSFWFilter.Show_NSFW) {
|
||||
filter['#nsfw'] = [
|
||||
(filterOptions.nsfw === NSFWFilter.Only_NSFW).toString()
|
||||
]
|
||||
if (filterOptions.nsfw === NSFWFilter.Only_NSFW) {
|
||||
filter['#L'] = ['content-warning']
|
||||
}
|
||||
|
||||
return filter
|
||||
@ -725,7 +723,7 @@ const ProfileTabBlogs = () => {
|
||||
}
|
||||
fetchEvents(filter)
|
||||
.then((events) => {
|
||||
setBlogs(events.map(extractBlogCardDetails).filter((e) => e.naddr))
|
||||
setBlogs(events.map(extractBlogCardDetails).filter((b) => b.naddr))
|
||||
setHasMore(events.length > PROFILE_BLOG_FILTER_LIMIT)
|
||||
})
|
||||
.finally(() => {
|
||||
@ -752,7 +750,7 @@ const ProfileTabBlogs = () => {
|
||||
setHasMore(nextBlogs.length > PROFILE_BLOG_FILTER_LIMIT)
|
||||
setPage((prev) => prev + 1)
|
||||
setBlogs(
|
||||
nextBlogs.slice(0, PROFILE_BLOG_FILTER_LIMIT).filter((e) => e.naddr)
|
||||
nextBlogs.slice(0, PROFILE_BLOG_FILTER_LIMIT).filter((b) => b.naddr)
|
||||
)
|
||||
})
|
||||
.finally(() => setIsLoading(false))
|
||||
@ -775,7 +773,7 @@ const ProfileTabBlogs = () => {
|
||||
.then((events) => {
|
||||
setHasMore(true)
|
||||
setPage((prev) => prev - 1)
|
||||
setBlogs(events.map(extractBlogCardDetails).filter((e) => e.naddr))
|
||||
setBlogs(events.map(extractBlogCardDetails).filter((b) => b.naddr))
|
||||
})
|
||||
.finally(() => setIsLoading(false))
|
||||
}
|
||||
@ -799,6 +797,11 @@ const ProfileTabBlogs = () => {
|
||||
})
|
||||
}
|
||||
|
||||
// Filter nsfw (Hide_NSFW option)
|
||||
_blogs = _blogs.filter(
|
||||
(b) => !(b.nsfw && filterOptions.nsfw === NSFWFilter.Hide_NSFW)
|
||||
)
|
||||
|
||||
// Only apply filtering if the user is not an admin or the admin has not selected "Unmoderated Fully"
|
||||
// Allow "Unmoderated Fully" when author visits own profile
|
||||
if (!((isAdmin || isOwner) && isUnmoderatedFully)) {
|
||||
|
@ -84,12 +84,7 @@ export const writeRouteAction =
|
||||
.split(',')
|
||||
.map((t) => ['t', t])
|
||||
|
||||
const unsignedEvent: UnsignedEvent = {
|
||||
kind: kinds.LongFormArticle,
|
||||
created_at: currentTimeStamp,
|
||||
pubkey: hexPubkey,
|
||||
content: content,
|
||||
tags: [
|
||||
const tags = [
|
||||
['d', uuid],
|
||||
['a', aTag],
|
||||
['r', rTag],
|
||||
@ -97,9 +92,19 @@ export const writeRouteAction =
|
||||
['title', formSubmit.title!],
|
||||
['image', formSubmit.image!],
|
||||
['summary', formSubmit.summary!],
|
||||
['nsfw', (formSubmit.nsfw === 'on').toString()],
|
||||
...tTags
|
||||
]
|
||||
|
||||
// Add NSFW tag, L label namespace standardized tag
|
||||
// https://github.com/nostr-protocol/nips/blob/2838e3bd51ac00bd63c4cef1601ae09935e7dd56/README.md#standardized-tags
|
||||
if (formSubmit.nsfw === 'on') tags.push(['L', 'content-warning'])
|
||||
|
||||
const unsignedEvent: UnsignedEvent = {
|
||||
kind: kinds.LongFormArticle,
|
||||
created_at: currentTimeStamp,
|
||||
pubkey: hexPubkey,
|
||||
content: content,
|
||||
tags: tags
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -8,8 +8,10 @@ export const extractBlogDetails = (event: NDKEvent): Partial<BlogDetails> => ({
|
||||
content: event.content,
|
||||
summary: getFirstTagValue(event, 'summary'),
|
||||
image: getFirstTagValue(event, 'image'),
|
||||
nsfw: getFirstTagValue(event, 'nsfw') === 'true',
|
||||
|
||||
// Check L label namespace for content warning or nsfw (backwards compatibility)
|
||||
nsfw:
|
||||
getFirstTagValue(event, 'L') === 'content-warning' ||
|
||||
getFirstTagValue(event, 'nsfw') === 'true',
|
||||
id: event.id,
|
||||
author: event.pubkey,
|
||||
published_at: getFirstTagValueAsInt(event, 'published_at'),
|
||||
|
Loading…
Reference in New Issue
Block a user