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
4 changed files with 174 additions and 11 deletions
Showing only changes of commit b39f8a4a4c - Show all commits

View File

@ -21,7 +21,7 @@ import { useState } from 'react'
import { Link } from 'react-router-dom'
import { appRoutes, getProfilePageRoute } from 'routes'
import { FeedPostsFilter, NSFWFilter, UserProfile } from 'types'
import { DEFAULT_FILTER_OPTIONS, hexToNpub } from 'utils'
import { DEFAULT_FILTER_OPTIONS, hexToNpub, log, LogType } from 'utils'
import { NoteRepostPopup } from './NoteRepostPopup'
import { NoteQuoteRepostPopup } from './NoteQuoteRepostPopup'
import { NsfwCommentWrapper } from 'components/NsfwCommentWrapper'
@ -62,10 +62,26 @@ export const Note = ({ ndkEvent }: NoteProps) => {
ndkEvent.author.fetchProfile().then((res) => setEventProfile(res))
if (isRepost) {
const parsedEvent = JSON.parse(ndkEvent.content)
const ndkRepostEvent = new NDKEvent(ndk, parsedEvent)
setRepostEvent(ndkRepostEvent)
ndkRepostEvent.author.fetchProfile().then((res) => setRepostProfile(res))
try {
const parsedEvent = JSON.parse(ndkEvent.content)
const ndkRepostEvent = new NDKEvent(ndk, parsedEvent)
setRepostEvent(ndkRepostEvent)
ndkRepostEvent.author
.fetchProfile()
.then((res) => setRepostProfile(res))
} catch (error) {
if (error instanceof SyntaxError) {
log(
true,
LogType.Error,
'Event content malformed',
error,
ndkEvent.content
)
} else {
log(true, LogType.Error, error)
}
}
}
const repostFilter: NDKFilter = {

View File

@ -7,7 +7,7 @@ import { CommentContent } from 'components/comment/CommentContent'
import { getProfilePageRoute } from 'routes'
import { nip19 } from 'nostr-tools'
import { UserProfile } from 'types'
import { hexToNpub } from 'utils'
import { hexToNpub, log, LogType } from 'utils'
import { formatDate } from 'date-fns'
interface NoteRepostProps {
@ -28,8 +28,16 @@ export const NoteRepostPopup = ({
useDidMount(async () => {
const repost = await ndkEvent.repost(false)
setContent(JSON.parse(repost.content).content)
ndkEvent.author.fetchProfile().then((res) => setProfile(res))
try {
setContent(JSON.parse(repost.content).content)
} catch (error) {
if (error instanceof SyntaxError) {
log(true, LogType.Error, 'Repost event content malformed', error)
} else {
log(true, LogType.Error, error)
}
}
})
const profileRoute = getProfilePageRoute(

View File

@ -22,9 +22,9 @@ export const CommentContent = ({
<p>Hide full post</p>
</div>
)}
<p className='IBMSMSMBSSCL_CBText'>
<div className='IBMSMSMBSSCL_CBText'>
<NoteRender content={text} />
</p>
</div>
{isTextOverflowing && !isExpanded && (
<div className='IBMSMSMBSSCL_CBExpand' onClick={toggle}>
<p>View full post</p>

View File

@ -1,4 +1,9 @@
import { NDKFilter, NDKKind } from '@nostr-dev-kit/ndk'
import {
NDKEvent,
NDKFilter,
NDKKind,
NDKSubscriptionCacheUsage
} from '@nostr-dev-kit/ndk'
import { LoadingSpinner } from 'components/LoadingSpinner'
import { ModCard } from 'components/ModCard'
import { ModFilter } from 'components/Filters/ModsFilter'
@ -19,10 +24,12 @@ import { toast } from 'react-toastify'
import { appRoutes } from 'routes'
import {
BlogCardDetails,
FeedPostsFilter,
FilterOptions,
ModDetails,
ModeratedFilter,
NSFWFilter,
RepostFilter,
SortBy,
UserRelaysType
} from 'types'
@ -42,6 +49,8 @@ import { CheckboxField } from 'components/Inputs'
import { ProfilePageLoaderResult } from './loader'
import { BlogCard } from 'components/BlogCard'
import { BlogsFilter } from 'components/Filters/BlogsFilter'
import { FeedFilter } from 'components/Filters/FeedFilter'
import { Note } from 'components/Notes/Note'
export const ProfilePage = () => {
const {
@ -451,7 +460,7 @@ export const ProfilePage = () => {
)}
{tab === 1 && <ProfileTabBlogs />}
{tab === 2 && <>WIP</>}
{tab === 2 && <ProfileTabPosts />}
</div>
</div>
</div>
@ -870,3 +879,133 @@ const ProfileTabBlogs = () => {
</>
)
}
const ProfileTabPosts = () => {
const SHOWING_STEP = 20
const { profilePubkey } = useLoaderData() as ProfilePageLoaderResult
const { ndk } = useNDKContext()
const [notes, setNotes] = useState<NDKEvent[]>([])
const [isFetching, setIsFetching] = useState(false)
const [isLoadMoreVisible, setIsLoadMoreVisible] = useState(true)
const [showing, setShowing] = useState(SHOWING_STEP)
const filterKey = 'filter-feed-2'
const [filterOptions] = useLocalStorage<FeedPostsFilter>(
filterKey,
DEFAULT_FILTER_OPTIONS
)
useEffect(() => {
setIsFetching(true)
setIsLoadMoreVisible(true)
const filter: NDKFilter = {
authors: [profilePubkey],
kinds: [NDKKind.Text, NDKKind.Repost],
limit: 50
}
ndk
.fetchEvents(filter, {
closeOnEose: true,
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
})
.then((ndkEventSet) => {
const ndkEvents = Array.from(ndkEventSet)
setNotes(ndkEvents)
})
.finally(() => {
setIsFetching(false)
})
}, [ndk, profilePubkey])
const filteredNotes = useMemo(() => {
let _notes = notes || []
_notes = _notes.filter((n) => {
if (n.kind === NDKKind.Text) {
// Filter out the replies (Kind 1 events with e tags are replies to other kind 1 events)
return n.getMatchingTags('e').length === 0
}
// Filter repost events if the option is set to hide reposts
return !(
n.kind === NDKKind.Repost &&
filterOptions.repost === RepostFilter.Hide_Repost
)
})
_notes = _notes.sort((a, b) => (b.created_at ?? 0) - (a.created_at ?? 0))
showing > 0 && _notes.splice(showing)
return _notes
}, [filterOptions.repost, notes, showing])
const handleLoadMore = () => {
const LOAD_MORE_STEP = SHOWING_STEP * 2
setShowing((prev) => prev + SHOWING_STEP)
const lastNote = filteredNotes[filteredNotes.length - 1]
const filter: NDKFilter = {
authors: [profilePubkey],
kinds: [NDKKind.Text, NDKKind.Repost],
limit: LOAD_MORE_STEP
}
filter.until = lastNote.created_at
setIsFetching(true)
ndk
.fetchEvents(filter, {
closeOnEose: true,
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
})
.then((ndkEventSet) => {
setNotes((prevNotes) => {
const newNotes = Array.from(ndkEventSet)
const combinedNotes = [...prevNotes, ...newNotes]
const uniqueBlogs = Array.from(
new Set(combinedNotes.map((b) => b.id))
)
.map((id) => combinedNotes.find((b) => b.id === id))
.filter((b) => b !== undefined)
if (newNotes.length < LOAD_MORE_STEP) {
setIsLoadMoreVisible(false)
}
return uniqueBlogs
})
})
.finally(() => {
setIsFetching(false)
})
}
return (
<>
<FeedFilter tab={2} />
{isFetching && <LoadingSpinner desc='Fetching notes from relays' />}
{filteredNotes.length === 0 && !isFetching && (
<div className='IBMSMListFeedNoPosts'>
<p>There are no posts to show</p>
</div>
)}
<div className='IBMSMSplitMainFullSideSec IBMSMSMFSSContent'>
<div className='IBMSMSMFSSContentPosts'>
{filteredNotes.map((note) => (
<Note key={note.id} ndkEvent={note} />
))}
</div>
</div>
{!isFetching && isLoadMoreVisible && filteredNotes.length > 0 && (
<div className='IBMSMListFeedLoadMore'>
<button
className='btn btnMain IBMSMListFeedLoadMoreBtn'
type='button'
onClick={handleLoadMore}
>
Load More
</button>
</div>
)}
</>
)
}