feat(feed): add blogs feed
This commit is contained in:
parent
31d9a2b258
commit
2991dd448c
@ -1,8 +1,21 @@
|
||||
import { useAppSelector, useLocalStorage } from 'hooks'
|
||||
import { FilterOptions } from 'types'
|
||||
import { DEFAULT_FILTER_OPTIONS } from 'utils'
|
||||
import {
|
||||
NDKFilter,
|
||||
NDKKind,
|
||||
NDKSubscriptionCacheUsage
|
||||
} from '@nostr-dev-kit/ndk'
|
||||
import { useAppSelector, useLocalStorage, useNDKContext } from 'hooks'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { BlogCardDetails, FilterOptions, NSFWFilter, SortBy } from 'types'
|
||||
import { DEFAULT_FILTER_OPTIONS, extractBlogCardDetails } from 'utils'
|
||||
import { FeedPageLoaderResult } from './loader'
|
||||
import { useLoaderData } from 'react-router-dom'
|
||||
import { LoadingSpinner } from 'components/LoadingSpinner'
|
||||
import { BlogCard } from 'components/BlogCard'
|
||||
|
||||
export const FeedTabBlogs = () => {
|
||||
const SHOWING_STEP = 10
|
||||
const { muteLists, nsfwList, followList } =
|
||||
useLoaderData() as FeedPageLoaderResult
|
||||
const userState = useAppSelector((state) => state.user)
|
||||
const userPubkey = userState.user?.pubkey as string | undefined
|
||||
|
||||
@ -10,7 +23,171 @@ export const FeedTabBlogs = () => {
|
||||
const [filterOptions] = useLocalStorage<FilterOptions>(filterKey, {
|
||||
...DEFAULT_FILTER_OPTIONS
|
||||
})
|
||||
const { ndk } = useNDKContext()
|
||||
const [blogs, setBlogs] = useState<Partial<BlogCardDetails>[]>([])
|
||||
const [isFetching, setIsFetching] = useState(false)
|
||||
const [isLoadMoreVisible, setIsLoadMoreVisible] = useState(true)
|
||||
const [showing, setShowing] = useState(SHOWING_STEP)
|
||||
|
||||
const handleLoadMore = () => {
|
||||
const LOAD_MORE_STEP = SHOWING_STEP * 2
|
||||
setShowing((prev) => prev + SHOWING_STEP)
|
||||
const lastBlog = filteredBlogs[filteredBlogs.length - 1]
|
||||
const filter: NDKFilter = {
|
||||
authors: [...followList],
|
||||
kinds: [NDKKind.Article],
|
||||
limit: LOAD_MORE_STEP
|
||||
}
|
||||
|
||||
if (filterOptions.nsfw === NSFWFilter.Only_NSFW) {
|
||||
filter['#L'] = ['content-warning']
|
||||
}
|
||||
|
||||
if (filterOptions.source === window.location.host) {
|
||||
filter['#r'] = [window.location.host]
|
||||
}
|
||||
|
||||
if (filterOptions.sort === SortBy.Latest) {
|
||||
filter.until = lastBlog.published_at
|
||||
} else if (filterOptions.sort === SortBy.Oldest) {
|
||||
filter.since = lastBlog.published_at
|
||||
}
|
||||
|
||||
setIsFetching(true)
|
||||
ndk
|
||||
.fetchEvents(filter, {
|
||||
closeOnEose: true,
|
||||
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
|
||||
})
|
||||
.then((ndkEventSet) => {
|
||||
setBlogs((prevBlogs) => {
|
||||
const newBlogs = Array.from(ndkEventSet)
|
||||
const combinedBlogs = [
|
||||
...prevBlogs,
|
||||
...newBlogs.map(extractBlogCardDetails)
|
||||
]
|
||||
const uniqueBlogs = Array.from(
|
||||
new Set(combinedBlogs.map((b) => b.id))
|
||||
)
|
||||
.map((id) => combinedBlogs.find((b) => b.id === id))
|
||||
.filter((b): b is BlogCardDetails => b !== undefined)
|
||||
|
||||
if (newBlogs.length < LOAD_MORE_STEP) {
|
||||
setIsLoadMoreVisible(false)
|
||||
}
|
||||
|
||||
return uniqueBlogs
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
setIsFetching(false)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setIsFetching(true)
|
||||
setIsLoadMoreVisible(true)
|
||||
const filter: NDKFilter = {
|
||||
authors: [...followList],
|
||||
kinds: [NDKKind.Article],
|
||||
limit: 50
|
||||
}
|
||||
|
||||
if (filterOptions.nsfw === NSFWFilter.Only_NSFW) {
|
||||
filter['#L'] = ['content-warning']
|
||||
}
|
||||
|
||||
if (filterOptions.source === window.location.host) {
|
||||
filter['#r'] = [window.location.host]
|
||||
}
|
||||
|
||||
ndk
|
||||
.fetchEvents(filter, {
|
||||
closeOnEose: true,
|
||||
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
|
||||
})
|
||||
.then((ndkEventSet) => {
|
||||
const ndkEvents = Array.from(ndkEventSet)
|
||||
setBlogs(ndkEvents.map(extractBlogCardDetails))
|
||||
})
|
||||
.finally(() => {
|
||||
setIsFetching(false)
|
||||
})
|
||||
}, [filterOptions.nsfw, filterOptions.source, followList, ndk])
|
||||
|
||||
const filteredBlogs = useMemo(() => {
|
||||
let _blogs = blogs || []
|
||||
|
||||
// Add nsfw tag to blogs included in nsfwList
|
||||
if (filterOptions.nsfw !== NSFWFilter.Hide_NSFW) {
|
||||
_blogs = _blogs.map((b) => {
|
||||
return !b.nsfw && b.aTag && nsfwList.includes(b.aTag)
|
||||
? { ...b, nsfw: true }
|
||||
: b
|
||||
})
|
||||
}
|
||||
// Filter nsfw (Hide_NSFW option)
|
||||
_blogs = _blogs.filter(
|
||||
(b) => !(b.nsfw && filterOptions.nsfw === NSFWFilter.Hide_NSFW)
|
||||
)
|
||||
|
||||
_blogs = _blogs.filter(
|
||||
(b) =>
|
||||
!muteLists.admin.authors.includes(b.author!) &&
|
||||
!muteLists.admin.replaceableEvents.includes(b.aTag!)
|
||||
)
|
||||
|
||||
if (filterOptions.sort === SortBy.Latest) {
|
||||
_blogs.sort((a, b) =>
|
||||
a.published_at && b.published_at ? b.published_at - a.published_at : 0
|
||||
)
|
||||
} else if (filterOptions.sort === SortBy.Oldest) {
|
||||
_blogs.sort((a, b) =>
|
||||
a.published_at && b.published_at ? a.published_at - b.published_at : 0
|
||||
)
|
||||
}
|
||||
|
||||
showing > 0 && _blogs.splice(showing)
|
||||
return _blogs
|
||||
}, [
|
||||
blogs,
|
||||
filterOptions.nsfw,
|
||||
filterOptions.sort,
|
||||
muteLists.admin.authors,
|
||||
muteLists.admin.replaceableEvents,
|
||||
nsfwList,
|
||||
showing
|
||||
])
|
||||
|
||||
if (!userPubkey) return null
|
||||
return <></>
|
||||
return (
|
||||
<>
|
||||
{isFetching && (
|
||||
<LoadingSpinner desc='Fetching blog details from relays' />
|
||||
)}
|
||||
<div className='IBMSMSplitMainFullSideSec IBMSMSMFSSContent'>
|
||||
<div className='IBMSMList IBMSMListFeed'>
|
||||
{filteredBlogs.map((blog) => (
|
||||
<BlogCard key={blog.id} {...blog} />
|
||||
))}
|
||||
{filteredBlogs.length === 0 && !isFetching && (
|
||||
<div className='IBMSMListFeedNoPosts'>
|
||||
<p>You aren't following people (or there are no posts to show)</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{!isFetching && isLoadMoreVisible && (
|
||||
<div className='IBMSMListFeedLoadMore'>
|
||||
<button
|
||||
className='btn btnMain IBMSMListFeedLoadMoreBtn'
|
||||
type='button'
|
||||
onClick={handleLoadMore}
|
||||
>
|
||||
Load More
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import { LoadingSpinner } from 'components/LoadingSpinner'
|
||||
import { ModCard } from 'components/ModCard'
|
||||
|
||||
export const FeedTabMods = () => {
|
||||
const SHOWING_STEP = 10
|
||||
const { muteLists, nsfwList, repostList, followList } =
|
||||
useLoaderData() as FeedPageLoaderResult
|
||||
const userState = useAppSelector((state) => state.user)
|
||||
@ -36,15 +37,16 @@ export const FeedTabMods = () => {
|
||||
const [mods, setMods] = useState<ModDetails[]>([])
|
||||
const [isFetching, setIsFetching] = useState(false)
|
||||
const [isLoadMoreVisible, setIsLoadMoreVisible] = useState(true)
|
||||
const [showing, setShowing] = useState(10)
|
||||
const [showing, setShowing] = useState(SHOWING_STEP)
|
||||
|
||||
const handleLoadMore = () => {
|
||||
setShowing((prev) => prev + 10)
|
||||
const lastMod = mods[mods.length - 1]
|
||||
const LOAD_MORE_STEP = SHOWING_STEP * 2
|
||||
setShowing((prev) => prev + SHOWING_STEP)
|
||||
const lastMod = filteredModList[filteredModList.length - 1]
|
||||
const filter: NDKFilter = {
|
||||
authors: [...followList],
|
||||
kinds: [NDKKind.Classified],
|
||||
limit: 20
|
||||
limit: LOAD_MORE_STEP
|
||||
}
|
||||
|
||||
if (filterOptions.source === window.location.host) {
|
||||
@ -78,7 +80,7 @@ export const FeedTabMods = () => {
|
||||
.map((id) => combinedMods.find((mod) => mod.id === id))
|
||||
.filter((mod): mod is ModDetails => mod !== undefined)
|
||||
|
||||
if (newMods.length < 20) {
|
||||
if (newMods.length < LOAD_MORE_STEP) {
|
||||
setIsLoadMoreVisible(false)
|
||||
}
|
||||
|
||||
@ -92,6 +94,7 @@ export const FeedTabMods = () => {
|
||||
|
||||
useEffect(() => {
|
||||
setIsFetching(true)
|
||||
setIsLoadMoreVisible(true)
|
||||
const filter: NDKFilter = {
|
||||
authors: [...followList],
|
||||
kinds: [NDKKind.Classified],
|
||||
|
Loading…
x
Reference in New Issue
Block a user