Feed feedback and fixes #231

Merged
enes merged 22 commits from feat/131-feed-issues into staging 2025-02-21 14:06:33 +00:00
8 changed files with 143 additions and 53 deletions
Showing only changes of commit e92d602f3a - Show all commits

View File

@ -43,7 +43,14 @@ export const FeedFilter = React.memo(
{/* nsfw filter options */}
<Dropdown label={filterOptions.nsfw}>
<NsfwFilterOptions filterKey={filterKey} />
<NsfwFilterOptions
filterKey={filterKey}
{...(tab === 2
? {
skipOnlyNsfw: true
}
: {})}
/>
</Dropdown>
{/* source filter options */}

View File

@ -7,9 +7,13 @@ import { DEFAULT_FILTER_OPTIONS } from 'utils'
interface NsfwFilterOptionsProps {
filterKey: string
skipOnlyNsfw?: boolean
}
export const NsfwFilterOptions = ({ filterKey }: NsfwFilterOptionsProps) => {
export const NsfwFilterOptions = ({
filterKey,
skipOnlyNsfw
}: NsfwFilterOptionsProps) => {
const [, setFilterOptions] = useLocalStorage<FilterOptions>(
filterKey,
DEFAULT_FILTER_OPTIONS
@ -30,7 +34,11 @@ export const NsfwFilterOptions = ({ filterKey }: NsfwFilterOptionsProps) => {
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
key={`nsfwFilterItem-${index}`}
onClick={() => {
@ -52,7 +60,8 @@ export const NsfwFilterOptions = ({ filterKey }: NsfwFilterOptionsProps) => {
>
{item}
</Option>
))}
)
})}
{showNsfwPopup && (
<NsfwAlertPopup
handleConfirm={handleConfirm}

View File

@ -9,16 +9,22 @@ import { Reactions } from 'components/comment/Reactions'
import { Zap } from 'components/comment/Zap'
import { Dots } from 'components/Spinner'
import { formatDate } from 'date-fns'
import { useAppSelector, useDidMount, useNDKContext } from 'hooks'
import {
useAppSelector,
useDidMount,
useLocalStorage,
useNDKContext
} from 'hooks'
import { useComments } from 'hooks/useComments'
import { nip19 } from 'nostr-tools'
import { useState } from 'react'
import { Link } from 'react-router-dom'
import { appRoutes, getProfilePageRoute } from 'routes'
import { UserProfile } from 'types'
import { hexToNpub } from 'utils'
import { FeedPostsFilter, NSFWFilter, UserProfile } from 'types'
import { DEFAULT_FILTER_OPTIONS, hexToNpub } from 'utils'
import { NoteRepostPopup } from './NoteRepostPopup'
import { NoteQuoteRepostPopup } from './NoteQuoteRepostPopup'
import { NsfwCommentWrapper } from 'components/NsfwCommentWrapper'
interface NoteProps {
ndkEvent: NDKEvent
@ -30,6 +36,14 @@ export const Note = ({ ndkEvent }: NoteProps) => {
const userPubkey = userState.user?.pubkey as string | undefined
const [eventProfile, setEventProfile] = useState<UserProfile>()
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 [repostProfile, setRepostProfile] = useState<UserProfile | undefined>()
const noteEvent = repostEvent ?? ndkEvent
@ -192,9 +206,15 @@ export const Note = ({ ndkEvent }: NoteProps) => {
)}
</div>
</div>
<NsfwCommentWrapper
id={ndkEvent.id}
isNsfw={isNsfw}
hideNsfwActive={NSFWFilter.Hide_NSFW === filterOptions.nsfw}
>
<div className='IBMSMSMBSSCL_CommentBottom'>
<CommentContent content={noteEvent.content} />
<CommentContent content={noteEvent.content} isNsfw={isNsfw} />
</div>
</NsfwCommentWrapper>
<div className='IBMSMSMBSSCL_CommentActions'>
<div className='IBMSMSMBSSCL_CommentActionsInside'>
<Reactions {...noteEvent.rawEvent()} />

View 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&#39;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)}
/>
)}
</>
)
}

View File

@ -3,9 +3,13 @@ import { useTextLimit } from 'hooks'
interface CommentContentProps {
content: string
isNsfw?: boolean
}
export const CommentContent = ({ content }: CommentContentProps) => {
export const CommentContent = ({
content,
isNsfw = false
}: CommentContentProps) => {
const { text, isTextOverflowing, isExpanded, toggle } = useTextLimit(content)
return (
@ -26,6 +30,11 @@ export const CommentContent = ({ content }: CommentContentProps) => {
<p>View full post</p>
</div>
)}
{isNsfw && (
<div className='IBMSMSMBSSCL_CommentNSWFTag'>
<p>NSFW</p>
</div>
)}
</>
)
}

View File

@ -1,8 +1,6 @@
import { useLoaderData } from 'react-router-dom'
import { FeedPageLoaderResult } from './loader'
import { useAppSelector, useLocalStorage, useNDKContext } from 'hooks'
import { FilterOptions, NSFWFilter } from 'types'
import { DEFAULT_FILTER_OPTIONS } from 'utils'
import { useAppSelector, useNDKContext } from 'hooks'
import { useEffect, useMemo, useState } from 'react'
import { LoadingSpinner } from 'components/LoadingSpinner'
import {
@ -19,10 +17,7 @@ export const FeedTabPosts = () => {
const { followList } = useLoaderData() as FeedPageLoaderResult
const userState = useAppSelector((state) => state.user)
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 [notes, setNotes] = useState<NDKEvent[]>([])
const [isFetching, setIsFetching] = useState(false)
@ -58,19 +53,6 @@ export const FeedTabPosts = () => {
const filteredNotes = useMemo(() => {
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
// TODO: Enable source/client filter
// _notes = _notes.filter(
@ -90,7 +72,7 @@ export const FeedTabPosts = () => {
showing > 0 && _notes.splice(showing)
return _notes
}, [filterOptions.nsfw, notes, showing])
}, [notes, showing])
if (!userPubkey) return null

View File

@ -44,6 +44,7 @@
}
.IBMSMSMBSSCL_CommentBottom {
position: relative;
width: 100%;
padding: 20px;
color: rgba(255, 255, 255, 0.75);

View File

@ -40,3 +40,5 @@ export interface FilterOptions {
wot: WOTFilterOptions
repost: RepostFilter
}
export type FeedPostsFilter = Pick<FilterOptions, 'nsfw'>