refactor(blog): re-render body, latest and filtering

This commit is contained in:
enes 2024-11-06 13:17:13 +01:00
parent 31ee0221b7
commit f30ac01ea6
3 changed files with 132 additions and 61 deletions

View File

@ -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(&quot;https://nichegamer.com/wp-content/uploads/2023/01/onimai-01-07-2023.jpg&quot;) 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(&quot;https://pbs.twimg.com/media/GDrRJOOXYAAeysT.jpg:large&quot;) 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(&quot;assets/img/DEGMods%20Placeholder%20Img.png&quot;) 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>

View File

@ -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,

View File

@ -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>[]
}