refactor(blog): re-render body, latest and filtering
This commit is contained in:
parent
31ee0221b7
commit
f30ac01ea6
@ -9,37 +9,41 @@ import { marked } from 'marked'
|
||||
import { LoadingSpinner } from 'components/LoadingSpinner'
|
||||
import { ProfileSection } from 'components/ProfileSection'
|
||||
import { Comments } from 'components/comment'
|
||||
import { Addressable, BlogDetails } from 'types'
|
||||
import { Addressable, BlogPageLoaderResult } from 'types'
|
||||
import placeholder from '../../assets/img/DEGMods Placeholder Img.png'
|
||||
import { PublishDetails } from 'components/Internal/PublishDetails'
|
||||
import { Interactions } from 'components/Internal/Interactions'
|
||||
import { BlogCard } from 'components/BlogCard'
|
||||
|
||||
export const BlogPage = () => {
|
||||
const data = useLoaderData() as Partial<BlogDetails>
|
||||
const { blog, latest } = useLoaderData() as BlogPageLoaderResult
|
||||
const [commentCount, setCommentCount] = useState(0)
|
||||
const html = marked.parse(data?.content || '', { async: false })
|
||||
const html = marked.parse(blog?.content || '', { async: false })
|
||||
const sanitized = DOMPurify.sanitize(html)
|
||||
const editor = useEditor({
|
||||
content: sanitized,
|
||||
extensions: [
|
||||
StarterKit,
|
||||
Link,
|
||||
Image.configure({
|
||||
inline: true,
|
||||
HTMLAttributes: {
|
||||
class: 'IBMSMSMBSSPostImg'
|
||||
}
|
||||
})
|
||||
],
|
||||
editable: false
|
||||
})
|
||||
const editor = useEditor(
|
||||
{
|
||||
content: sanitized,
|
||||
extensions: [
|
||||
StarterKit,
|
||||
Link,
|
||||
Image.configure({
|
||||
inline: true,
|
||||
HTMLAttributes: {
|
||||
class: 'IBMSMSMBSSPostImg'
|
||||
}
|
||||
})
|
||||
],
|
||||
editable: false
|
||||
},
|
||||
[sanitized]
|
||||
)
|
||||
|
||||
return (
|
||||
<div className='InnerBodyMain'>
|
||||
<div className='ContainerMain'>
|
||||
<div className='IBMSecMainGroup IBMSecMainGroupAlt'>
|
||||
<div className='IBMSMSplitMain'>
|
||||
{!data ? (
|
||||
{!blog ? (
|
||||
<LoadingSpinner desc={'Loading...'} />
|
||||
) : (
|
||||
<div className='IBMSMSplitMainBigSide'>
|
||||
@ -67,27 +71,27 @@ export const BlogPage = () => {
|
||||
className='IBMSMSMBSSPostPicture'
|
||||
style={{
|
||||
background: `url("${
|
||||
data.image !== '' ? data.image : placeholder
|
||||
blog.image !== '' ? blog.image : placeholder
|
||||
}") center / cover no-repeat`
|
||||
}}
|
||||
></div>
|
||||
<div className='IBMSMSMBSSPostInside'>
|
||||
<div className='IBMSMSMBSSPostTitle'>
|
||||
<h1 className='IBMSMSMBSSPostTitleHeading'>
|
||||
{data.title}
|
||||
{blog.title}
|
||||
</h1>
|
||||
</div>
|
||||
<div className='IBMSMSMBSSPostBody'>
|
||||
<EditorContent editor={editor} />
|
||||
</div>
|
||||
<div className='IBMSMSMBSSTags'>
|
||||
{data.nsfw && (
|
||||
{blog.nsfw && (
|
||||
<div className='IBMSMSMBSSTagsTag IBMSMSMBSSTagsTagNSFW'>
|
||||
<p>NSFW</p>
|
||||
</div>
|
||||
)}
|
||||
{data.tTags &&
|
||||
data.tTags.map((t) => (
|
||||
{blog.tTags &&
|
||||
blog.tTags.map((t) => (
|
||||
<a key={t} className='IBMSMSMBSSTagsTag'>
|
||||
{t}
|
||||
</a>
|
||||
@ -96,48 +100,38 @@ export const BlogPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
<Interactions
|
||||
addressable={data as Addressable}
|
||||
addressable={blog as Addressable}
|
||||
commentCount={commentCount}
|
||||
/>
|
||||
<PublishDetails
|
||||
published_at={data.published_at || 0}
|
||||
edited_at={data.edited_at || 0}
|
||||
site={data.rTag || 'N/A'}
|
||||
published_at={blog.published_at || 0}
|
||||
edited_at={blog.edited_at || 0}
|
||||
site={blog.rTag || 'N/A'}
|
||||
/>
|
||||
{/* <div className="IBMSMSplitMainBigSideSec">
|
||||
<div className="IBMSMSMBSSPostsWrapper">
|
||||
<h4 className="IBMSMSMBSSPostsTitle">Latest POSTER-NAME Posts</h4>
|
||||
<div className="IBMSMList IBMSMListAlt"><a className="cardBlogMainWrapperLink" href="blog-inner.html">
|
||||
<div className="cardBlogMain" style="background: url("https://nichegamer.com/wp-content/uploads/2023/01/onimai-01-07-2023.jpg") center / cover no-repeat;">
|
||||
<div className="cardBlogMainInside" style="background: linear-gradient(rgba(255,255,255,0) 0%, #232323 100%);">
|
||||
<h3 style="display: -webkit-box;-webkit-box-orient: vertical;overflow: hidden;-webkit-line-clamp: 2;font-size: 20px;line-height: 1.5;color: rgba(255,255,255,0.75);text-shadow: 0 0 8px rgba(0,0,0,0.25);">This is a blog title, the best blog title in the world!</h3>
|
||||
</div>
|
||||
</div>
|
||||
</a><a className="cardBlogMainWrapperLink" href="blog-inner.html">
|
||||
<div className="cardBlogMain" style="background: url("https://pbs.twimg.com/media/GDrRJOOXYAAeysT.jpg:large") center / cover no-repeat;">
|
||||
<div className="cardBlogMainInside" style="background: linear-gradient(rgba(255,255,255,0) 0%, #232323 100%);">
|
||||
<h3 style="display: -webkit-box;-webkit-box-orient: vertical;overflow: hidden;-webkit-line-clamp: 2;font-size: 20px;line-height: 1.5;color: rgba(255,255,255,0.75);text-shadow: 0 0 8px rgba(0,0,0,0.25);">This is a blog title, the best blog title in the world!</h3>
|
||||
</div>
|
||||
</div>
|
||||
</a><a className="cardBlogMainWrapperLink" href="blog-inner.html">
|
||||
<div className="cardBlogMain" style="background: url("assets/img/DEGMods%20Placeholder%20Img.png") center / cover no-repeat;">
|
||||
<div className="cardBlogMainInside" style="background: linear-gradient(rgba(255,255,255,0) 0%, #232323 100%);">
|
||||
<h3 style="display: -webkit-box;-webkit-box-orient: vertical;overflow: hidden;-webkit-line-clamp: 2;font-size: 20px;line-height: 1.5;color: rgba(255,255,255,0.75);text-shadow: 0 0 8px rgba(0,0,0,0.25);">This is a blog title, the best blog title in the world!</h3>
|
||||
</div>
|
||||
</div>
|
||||
</a></div>
|
||||
{!!latest.length && (
|
||||
<div className='IBMSMSplitMainBigSideSec'>
|
||||
<div className='IBMSMSMBSSPostsWrapper'>
|
||||
<h4 className='IBMSMSMBSSPostsTitle'>
|
||||
Latest blog posts
|
||||
</h4>
|
||||
<div className='IBMSMList IBMSMListAlt'>
|
||||
{latest.map((b) => (
|
||||
<BlogCard key={b.id} {...b} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
)}
|
||||
<div className='IBMSMSplitMainBigSideSec'>
|
||||
<Comments
|
||||
addressable={data as Addressable}
|
||||
addressable={blog as Addressable}
|
||||
setCommentCount={setCommentCount}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!!data?.author && <ProfileSection pubkey={data.author} />}
|
||||
{!!blog?.author && <ProfileSection pubkey={blog.author} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,10 +1,17 @@
|
||||
import { filterForEventsTaggingId } from '@nostr-dev-kit/ndk'
|
||||
import { filterForEventsTaggingId, NDKFilter } from '@nostr-dev-kit/ndk'
|
||||
import { NDKContextType } from 'contexts/NDKContext'
|
||||
import { kinds, nip19 } from 'nostr-tools'
|
||||
import { LoaderFunctionArgs, redirect } from 'react-router-dom'
|
||||
import { toast } from 'react-toastify'
|
||||
import { appRoutes } from 'routes'
|
||||
import { log, LogType } from 'utils'
|
||||
import { extractBlogDetails } from 'utils/blog'
|
||||
import { BlogPageLoaderResult, FilterOptions, NSFWFilter } from 'types'
|
||||
import {
|
||||
DEFAULT_FILTER_OPTIONS,
|
||||
getLocalStorageItem,
|
||||
log,
|
||||
LogType
|
||||
} from 'utils'
|
||||
import { extractBlogCardDetails, extractBlogDetails } from 'utils/blog'
|
||||
|
||||
export const blogRouteLoader =
|
||||
(ndkContext: NDKContextType) =>
|
||||
@ -15,21 +22,86 @@ export const blogRouteLoader =
|
||||
return redirect(appRoutes.blogs)
|
||||
}
|
||||
|
||||
// Decode author from naddr
|
||||
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
|
||||
const { pubkey } = decoded.data
|
||||
|
||||
try {
|
||||
// Get the filter with #a from naddr for the main blog content
|
||||
const filter = filterForEventsTaggingId(naddr)
|
||||
if (!filter) {
|
||||
log(true, LogType.Error, 'Unable to create filter from blog naddr.')
|
||||
return redirect(appRoutes.blogs)
|
||||
}
|
||||
|
||||
const event = await ndkContext.fetchEvent(filter)
|
||||
if (!event) {
|
||||
log(true, LogType.Error, 'Unable to fetch the blog event.')
|
||||
return null
|
||||
// Get the blog filter options for latest blogs
|
||||
const filterOptions = JSON.parse(
|
||||
getLocalStorageItem('filter-blog', DEFAULT_FILTER_OPTIONS)
|
||||
) as FilterOptions
|
||||
|
||||
// Fetch 4 in case the current blog is included in the latest
|
||||
const latestModsFilter: NDKFilter = {
|
||||
authors: [pubkey],
|
||||
kinds: [kinds.LongFormArticle],
|
||||
limit: 4
|
||||
}
|
||||
// Add source filter
|
||||
if (filterOptions.source === window.location.host) {
|
||||
latestModsFilter['#r'] = [filterOptions.source]
|
||||
}
|
||||
// Filter by NSFW tag
|
||||
// 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()
|
||||
]
|
||||
}
|
||||
|
||||
const blogDetails = extractBlogDetails(event)
|
||||
return blogDetails
|
||||
// Parallel fetch blog event and latest events
|
||||
const settled = await Promise.allSettled([
|
||||
ndkContext.fetchEvent(filter),
|
||||
ndkContext.fetchEvents(latestModsFilter)
|
||||
])
|
||||
|
||||
const result: BlogPageLoaderResult = {
|
||||
blog: undefined,
|
||||
latest: []
|
||||
}
|
||||
|
||||
// Check the blog event result
|
||||
const fetchEventResult = settled[0]
|
||||
if (fetchEventResult.status === 'fulfilled' && fetchEventResult.value) {
|
||||
// Extract the blog details from the event
|
||||
result.blog = extractBlogDetails(fetchEventResult.value)
|
||||
} else if (fetchEventResult.status === 'rejected') {
|
||||
log(
|
||||
true,
|
||||
LogType.Error,
|
||||
'Unable to fetch the blog event.',
|
||||
fetchEventResult.reason
|
||||
)
|
||||
}
|
||||
|
||||
// Check the lateast blog events
|
||||
const fetchEventsResult = settled[1]
|
||||
if (fetchEventsResult.status === 'fulfilled' && fetchEventsResult.value) {
|
||||
// Extract the blog card details from the events
|
||||
result.latest = fetchEventsResult.value
|
||||
.map(extractBlogCardDetails)
|
||||
.filter((b) => b.id !== result.blog?.id) // Filter out current blog if present
|
||||
.slice(0, 3) // Take only three
|
||||
} else if (fetchEventsResult.status === 'rejected') {
|
||||
log(
|
||||
true,
|
||||
LogType.Error,
|
||||
'Unable to fetch the latest blog events.',
|
||||
fetchEventsResult.reason
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
log(
|
||||
true,
|
||||
|
@ -27,3 +27,8 @@ export interface BlogFormErrors extends Partial<BlogEventSubmitForm> {}
|
||||
export interface BlogCardDetails extends BlogDetails {
|
||||
naddr: string
|
||||
}
|
||||
|
||||
export interface BlogPageLoaderResult {
|
||||
blog: Partial<BlogDetails> | undefined
|
||||
latest: Partial<BlogDetails>[]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user