fix(notes): nsfw filter, nsfw post wrapper
This commit is contained in:
parent
b84adf3617
commit
e92d602f3a
@ -43,7 +43,14 @@ export const FeedFilter = React.memo(
|
|||||||
|
|
||||||
{/* nsfw filter options */}
|
{/* nsfw filter options */}
|
||||||
<Dropdown label={filterOptions.nsfw}>
|
<Dropdown label={filterOptions.nsfw}>
|
||||||
<NsfwFilterOptions filterKey={filterKey} />
|
<NsfwFilterOptions
|
||||||
|
filterKey={filterKey}
|
||||||
|
{...(tab === 2
|
||||||
|
? {
|
||||||
|
skipOnlyNsfw: true
|
||||||
|
}
|
||||||
|
: {})}
|
||||||
|
/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|
||||||
{/* source filter options */}
|
{/* source filter options */}
|
||||||
|
@ -7,9 +7,13 @@ import { DEFAULT_FILTER_OPTIONS } from 'utils'
|
|||||||
|
|
||||||
interface NsfwFilterOptionsProps {
|
interface NsfwFilterOptionsProps {
|
||||||
filterKey: string
|
filterKey: string
|
||||||
|
skipOnlyNsfw?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NsfwFilterOptions = ({ filterKey }: NsfwFilterOptionsProps) => {
|
export const NsfwFilterOptions = ({
|
||||||
|
filterKey,
|
||||||
|
skipOnlyNsfw
|
||||||
|
}: NsfwFilterOptionsProps) => {
|
||||||
const [, setFilterOptions] = useLocalStorage<FilterOptions>(
|
const [, setFilterOptions] = useLocalStorage<FilterOptions>(
|
||||||
filterKey,
|
filterKey,
|
||||||
DEFAULT_FILTER_OPTIONS
|
DEFAULT_FILTER_OPTIONS
|
||||||
@ -30,7 +34,11 @@ export const NsfwFilterOptions = ({ filterKey }: NsfwFilterOptionsProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Object.values(NSFWFilter).map((item, index) => (
|
{Object.values(NSFWFilter).map((item, index) => {
|
||||||
|
// Posts feed filter exception
|
||||||
|
if (item === NSFWFilter.Only_NSFW && skipOnlyNsfw) return null
|
||||||
|
|
||||||
|
return (
|
||||||
<Option
|
<Option
|
||||||
key={`nsfwFilterItem-${index}`}
|
key={`nsfwFilterItem-${index}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -52,7 +60,8 @@ export const NsfwFilterOptions = ({ filterKey }: NsfwFilterOptionsProps) => {
|
|||||||
>
|
>
|
||||||
{item}
|
{item}
|
||||||
</Option>
|
</Option>
|
||||||
))}
|
)
|
||||||
|
})}
|
||||||
{showNsfwPopup && (
|
{showNsfwPopup && (
|
||||||
<NsfwAlertPopup
|
<NsfwAlertPopup
|
||||||
handleConfirm={handleConfirm}
|
handleConfirm={handleConfirm}
|
||||||
|
@ -9,16 +9,22 @@ import { Reactions } from 'components/comment/Reactions'
|
|||||||
import { Zap } from 'components/comment/Zap'
|
import { Zap } from 'components/comment/Zap'
|
||||||
import { Dots } from 'components/Spinner'
|
import { Dots } from 'components/Spinner'
|
||||||
import { formatDate } from 'date-fns'
|
import { formatDate } from 'date-fns'
|
||||||
import { useAppSelector, useDidMount, useNDKContext } from 'hooks'
|
import {
|
||||||
|
useAppSelector,
|
||||||
|
useDidMount,
|
||||||
|
useLocalStorage,
|
||||||
|
useNDKContext
|
||||||
|
} from 'hooks'
|
||||||
import { useComments } from 'hooks/useComments'
|
import { useComments } from 'hooks/useComments'
|
||||||
import { nip19 } from 'nostr-tools'
|
import { nip19 } from 'nostr-tools'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { appRoutes, getProfilePageRoute } from 'routes'
|
import { appRoutes, getProfilePageRoute } from 'routes'
|
||||||
import { UserProfile } from 'types'
|
import { FeedPostsFilter, NSFWFilter, UserProfile } from 'types'
|
||||||
import { hexToNpub } from 'utils'
|
import { DEFAULT_FILTER_OPTIONS, hexToNpub } from 'utils'
|
||||||
import { NoteRepostPopup } from './NoteRepostPopup'
|
import { NoteRepostPopup } from './NoteRepostPopup'
|
||||||
import { NoteQuoteRepostPopup } from './NoteQuoteRepostPopup'
|
import { NoteQuoteRepostPopup } from './NoteQuoteRepostPopup'
|
||||||
|
import { NsfwCommentWrapper } from 'components/NsfwCommentWrapper'
|
||||||
|
|
||||||
interface NoteProps {
|
interface NoteProps {
|
||||||
ndkEvent: NDKEvent
|
ndkEvent: NDKEvent
|
||||||
@ -30,6 +36,14 @@ export const Note = ({ ndkEvent }: NoteProps) => {
|
|||||||
const userPubkey = userState.user?.pubkey as string | undefined
|
const userPubkey = userState.user?.pubkey as string | undefined
|
||||||
const [eventProfile, setEventProfile] = useState<UserProfile>()
|
const [eventProfile, setEventProfile] = useState<UserProfile>()
|
||||||
const isRepost = ndkEvent.kind === NDKKind.Repost
|
const isRepost = ndkEvent.kind === NDKKind.Repost
|
||||||
|
const filterKey = 'filter-feed-2'
|
||||||
|
const [filterOptions] = useLocalStorage<FeedPostsFilter>(
|
||||||
|
filterKey,
|
||||||
|
DEFAULT_FILTER_OPTIONS
|
||||||
|
)
|
||||||
|
const isNsfw = ndkEvent
|
||||||
|
.getMatchingTags('L')
|
||||||
|
.some((t) => t[1] === 'content-warning')
|
||||||
const [repostEvent, setRepostEvent] = useState<NDKEvent | undefined>()
|
const [repostEvent, setRepostEvent] = useState<NDKEvent | undefined>()
|
||||||
const [repostProfile, setRepostProfile] = useState<UserProfile | undefined>()
|
const [repostProfile, setRepostProfile] = useState<UserProfile | undefined>()
|
||||||
const noteEvent = repostEvent ?? ndkEvent
|
const noteEvent = repostEvent ?? ndkEvent
|
||||||
@ -192,9 +206,15 @@ export const Note = ({ ndkEvent }: NoteProps) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<NsfwCommentWrapper
|
||||||
|
id={ndkEvent.id}
|
||||||
|
isNsfw={isNsfw}
|
||||||
|
hideNsfwActive={NSFWFilter.Hide_NSFW === filterOptions.nsfw}
|
||||||
|
>
|
||||||
<div className='IBMSMSMBSSCL_CommentBottom'>
|
<div className='IBMSMSMBSSCL_CommentBottom'>
|
||||||
<CommentContent content={noteEvent.content} />
|
<CommentContent content={noteEvent.content} isNsfw={isNsfw} />
|
||||||
</div>
|
</div>
|
||||||
|
</NsfwCommentWrapper>
|
||||||
<div className='IBMSMSMBSSCL_CommentActions'>
|
<div className='IBMSMSMBSSCL_CommentActions'>
|
||||||
<div className='IBMSMSMBSSCL_CommentActionsInside'>
|
<div className='IBMSMSMBSSCL_CommentActionsInside'>
|
||||||
<Reactions {...noteEvent.rawEvent()} />
|
<Reactions {...noteEvent.rawEvent()} />
|
||||||
|
60
src/components/NsfwCommentWrapper.tsx
Normal file
60
src/components/NsfwCommentWrapper.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { useLocalStorage, useSessionStorage } from 'hooks'
|
||||||
|
import { PropsWithChildren, useState } from 'react'
|
||||||
|
import { NsfwAlertPopup } from './NsfwAlertPopup'
|
||||||
|
|
||||||
|
interface NsfwCommentWrapperProps {
|
||||||
|
id: string
|
||||||
|
isNsfw: boolean
|
||||||
|
hideNsfwActive: boolean
|
||||||
|
}
|
||||||
|
export const NsfwCommentWrapper = ({
|
||||||
|
id,
|
||||||
|
isNsfw,
|
||||||
|
hideNsfwActive,
|
||||||
|
children
|
||||||
|
}: PropsWithChildren<NsfwCommentWrapperProps>) => {
|
||||||
|
// Have we approved show nsfw comment button
|
||||||
|
const [viewNsfwComment, setViewNsfwComment] = useSessionStorage<boolean>(
|
||||||
|
id,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
const [showNsfwPopup, setShowNsfwPopup] = useState<boolean>(false)
|
||||||
|
const [confirmNsfw] = useLocalStorage<boolean>('confirm-nsfw', false)
|
||||||
|
const handleConfirm = (confirm: boolean) => {
|
||||||
|
if (confirm) {
|
||||||
|
setShowNsfwPopup(confirm)
|
||||||
|
setViewNsfwComment(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleShowNSFW = () => {
|
||||||
|
if (confirmNsfw) {
|
||||||
|
setViewNsfwComment(true)
|
||||||
|
} else {
|
||||||
|
setShowNsfwPopup(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip NSFW wrapper
|
||||||
|
// if comment is not marked as NSFW
|
||||||
|
// if user clicked View NSFW button
|
||||||
|
// if hide filter is not active
|
||||||
|
if (!isNsfw || viewNsfwComment || !hideNsfwActive) return children
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='IBMSMSMBSSCL_CommentNSFW'>
|
||||||
|
<p>This post is hidden as it's marked as NSFW</p>
|
||||||
|
<button className='btnMain' type='button' onClick={handleShowNSFW}>
|
||||||
|
View this NSFW post
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{showNsfwPopup && (
|
||||||
|
<NsfwAlertPopup
|
||||||
|
handleConfirm={handleConfirm}
|
||||||
|
handleClose={() => setShowNsfwPopup(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -3,9 +3,13 @@ import { useTextLimit } from 'hooks'
|
|||||||
|
|
||||||
interface CommentContentProps {
|
interface CommentContentProps {
|
||||||
content: string
|
content: string
|
||||||
|
isNsfw?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CommentContent = ({ content }: CommentContentProps) => {
|
export const CommentContent = ({
|
||||||
|
content,
|
||||||
|
isNsfw = false
|
||||||
|
}: CommentContentProps) => {
|
||||||
const { text, isTextOverflowing, isExpanded, toggle } = useTextLimit(content)
|
const { text, isTextOverflowing, isExpanded, toggle } = useTextLimit(content)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -26,6 +30,11 @@ export const CommentContent = ({ content }: CommentContentProps) => {
|
|||||||
<p>View full post</p>
|
<p>View full post</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{isNsfw && (
|
||||||
|
<div className='IBMSMSMBSSCL_CommentNSWFTag'>
|
||||||
|
<p>NSFW</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import { useLoaderData } from 'react-router-dom'
|
import { useLoaderData } from 'react-router-dom'
|
||||||
import { FeedPageLoaderResult } from './loader'
|
import { FeedPageLoaderResult } from './loader'
|
||||||
import { useAppSelector, useLocalStorage, useNDKContext } from 'hooks'
|
import { useAppSelector, useNDKContext } from 'hooks'
|
||||||
import { FilterOptions, NSFWFilter } from 'types'
|
|
||||||
import { DEFAULT_FILTER_OPTIONS } from 'utils'
|
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { LoadingSpinner } from 'components/LoadingSpinner'
|
import { LoadingSpinner } from 'components/LoadingSpinner'
|
||||||
import {
|
import {
|
||||||
@ -19,10 +17,7 @@ export const FeedTabPosts = () => {
|
|||||||
const { followList } = useLoaderData() as FeedPageLoaderResult
|
const { followList } = useLoaderData() as FeedPageLoaderResult
|
||||||
const userState = useAppSelector((state) => state.user)
|
const userState = useAppSelector((state) => state.user)
|
||||||
const userPubkey = userState.user?.pubkey as string | undefined
|
const userPubkey = userState.user?.pubkey as string | undefined
|
||||||
const filterKey = 'filter-feed-2'
|
|
||||||
const [filterOptions] = useLocalStorage<FilterOptions>(filterKey, {
|
|
||||||
...DEFAULT_FILTER_OPTIONS
|
|
||||||
})
|
|
||||||
const { ndk } = useNDKContext()
|
const { ndk } = useNDKContext()
|
||||||
const [notes, setNotes] = useState<NDKEvent[]>([])
|
const [notes, setNotes] = useState<NDKEvent[]>([])
|
||||||
const [isFetching, setIsFetching] = useState(false)
|
const [isFetching, setIsFetching] = useState(false)
|
||||||
@ -58,19 +53,6 @@ export const FeedTabPosts = () => {
|
|||||||
const filteredNotes = useMemo(() => {
|
const filteredNotes = useMemo(() => {
|
||||||
let _notes = notes || []
|
let _notes = notes || []
|
||||||
|
|
||||||
// NSFW Filter
|
|
||||||
_notes = _notes.filter((n) => {
|
|
||||||
if (filterOptions.nsfw === NSFWFilter.Only_NSFW) {
|
|
||||||
return n.getMatchingTags('L').some((l) => l[1] === 'content-warning')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filterOptions.nsfw === NSFWFilter.Hide_NSFW) {
|
|
||||||
return !n.getMatchingTags('L').some((l) => l[1] === 'content-warning')
|
|
||||||
}
|
|
||||||
|
|
||||||
return n
|
|
||||||
})
|
|
||||||
|
|
||||||
// Filter source
|
// Filter source
|
||||||
// TODO: Enable source/client filter
|
// TODO: Enable source/client filter
|
||||||
// _notes = _notes.filter(
|
// _notes = _notes.filter(
|
||||||
@ -90,7 +72,7 @@ export const FeedTabPosts = () => {
|
|||||||
|
|
||||||
showing > 0 && _notes.splice(showing)
|
showing > 0 && _notes.splice(showing)
|
||||||
return _notes
|
return _notes
|
||||||
}, [filterOptions.nsfw, notes, showing])
|
}, [notes, showing])
|
||||||
|
|
||||||
if (!userPubkey) return null
|
if (!userPubkey) return null
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.IBMSMSMBSSCL_CommentBottom {
|
.IBMSMSMBSSCL_CommentBottom {
|
||||||
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
color: rgba(255, 255, 255, 0.75);
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
@ -40,3 +40,5 @@ export interface FilterOptions {
|
|||||||
wot: WOTFilterOptions
|
wot: WOTFilterOptions
|
||||||
repost: RepostFilter
|
repost: RepostFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FeedPostsFilter = Pick<FilterOptions, 'nsfw'>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user