Skip to content

Commit

Permalink
refactor(post): Refactor to smaller components. Lazy load Disqus comm…
Browse files Browse the repository at this point in the history
…ents.
  • Loading branch information
ZL-Asica committed Nov 10, 2024
1 parent c461331 commit 5cbd8a1
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 89 deletions.
46 changes: 35 additions & 11 deletions src/components/common/DisqusComments.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,53 @@
'use client';
// src/components/DisqusComments.tsx
import { useEffect } from 'react';
import { useEffect, useState, useRef } from 'react';

export default function DisqusComments({
disqusShortname,
}: {
disqusShortname: string;
}) {
const [loadDisqus, setLoadDisqus] = useState(false);
const disqusReference = useRef<HTMLDivElement | null>(null);

useEffect(() => {
const disqusScript = document.createElement('script');
disqusScript.src = `https://${disqusShortname}.disqus.com/embed.js`;
disqusScript.dataset.timestamp = `${Date.now()}`;
(document.head || document.body).append(disqusScript);
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
setLoadDisqus(true); // Observer is triggered
observer.disconnect(); // Disconnect observer
}
},
{ threshold: 0.1 }
);

if (disqusReference.current) {
observer.observe(disqusReference.current);
}

return () => {
// Clean up the script if the component is unmounted
if (disqusScript.parentNode) {
disqusScript.remove();
}
observer.disconnect();
};
}, [disqusShortname]);
}, []);

useEffect(() => {
if (loadDisqus) {
const disqusScript = document.createElement('script');
disqusScript.src = `https://${disqusShortname}.disqus.com/embed.js`;
disqusScript.dataset.timestamp = `${Date.now()}`;
(document.head || document.body).append(disqusScript);

return () => {
if (disqusScript.parentNode) {
disqusScript.remove();
}
};
}
}, [loadDisqus, disqusShortname]);

return (
<div
id='disqus_thread'
ref={disqusReference}
className='mx-auto mt-10 w-full max-w-3xl'
></div>
);
Expand Down
224 changes: 146 additions & 78 deletions src/components/layout/PostLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import Image from 'next/image';
import { FaFolder, FaTags } from 'react-icons/fa6';

import ItemLinks from './ItemLinks';

import { getConfig } from '@/services/config';

import DisqusComments from '@/components/common/DisqusComments';
import ItemLinks from '@/components/layout/ItemLinks';

import '@/styles/codeblock.css';
import '@/styles/postContent.css';
Expand All @@ -15,91 +16,158 @@ interface PostLayoutProperties {
showThumbnail?: boolean;
}

export default function PostLayout({
post,
showThumbnail = true,
}: PostLayoutProperties) {
const config = getConfig();

// if showComments is defined in the frontmatter, use that value,
// otherwise default to true
const showComments: boolean = post.frontmatter.showComments ?? true;

function PostLayout({ post, showThumbnail = true }: PostLayoutProperties) {
return (
<article className='container mx-auto p-5'>
<article className='container mx-auto p-6'>
{showThumbnail ? (
<div className='relative h-96 w-full'>
{/* Thumbnail masking effect */}
<Image
src={post.frontmatter.thumbnail}
alt={`Thumbnail for ${post.frontmatter.title}`}
width={1200}
height={500}
className='h-full w-full rounded-lg object-cover'
/>

{/* Mask */}
<div className='absolute inset-0 rounded-lg bg-black bg-opacity-40'></div>

{/* Title and publishing info */}
<div className='absolute bottom-0 left-1/2 w-full max-w-3xl -translate-x-1/2 transform p-4 text-white'>
<h1 className='text-3xl font-bold'>{post.frontmatter.title}</h1>
<p className='left-1 ml-2 flex items-center'>
{post.frontmatter.author}
<span className='mx-3 text-2xl'></span>
{post.frontmatter.date.split(' ')[0]}
</p>
</div>
</div>
<Thumbnail
title={post.frontmatter.title}
src={post.frontmatter.thumbnail}
author={post.frontmatter.author}
date={post.frontmatter.date}
/>
) : (
/* If no thumbnail, display the title at the top */
<div className='mx-auto mb-5 w-full max-w-3xl'>
<h1 className='text-3xl font-bold'>{post.frontmatter.title}</h1>

{(post.frontmatter.title === 'About' ||
post.frontmatter.title === 'Friends') ?? (
<p className='mt-2 flex items-center'>
{post.frontmatter.author}
<span className='mx-3 text-2xl'></span>
{post.frontmatter.date.split(' ')[0]}
</p>
)}
</div>
<TitleHeader
title={post.frontmatter.title}
author={post.frontmatter.author}
date={post.frontmatter.date}
/>
)}

<div className='mx-auto mt-10 w-full max-w-3xl'>
{/* Show categories and tags if any */}
{(post.frontmatter.categories?.length ||
post.frontmatter.tags?.length) && (
<ul className='mx-auto mt-5 flex flex-col gap-4'>
<li className='flex items-center gap-2'>
<FaFolder className='mr-1' />
<span className='font-semibold'>分类:</span>
<ItemLinks
items={post.frontmatter.categories}
type={'category'}
/>
</li>
<li className='flex items-center gap-2'>
<FaTags className='mr-1' />
<span className='font-semibold'>标签:</span>
<ItemLinks
items={post.frontmatter.tags}
type={'tag'}
/>
</li>
</ul>
<CategoriesTagsList
categories={post.frontmatter.categories}
tags={post.frontmatter.tags}
/>
<PostContent contentHtml={post.contentHtml} />
{post.frontmatter.showComments && (
<DisqusComments disqusShortname={getConfig().disqusShortname} />
)}
</div>
</article>
);
}

{/* Post content */}
<div
className='post-content mt-8'
dangerouslySetInnerHTML={{ __html: post.contentHtml }}
function Thumbnail({
title,
src,
author,
date,
}: {
title: string;
src: string;
author: string;
date: string;
}) {
return (
<div className='relative h-96 w-full'>
<Image
src={src}
alt={`Thumbnail for ${title}`}
width={1200}
height={500}
className='h-full w-full rounded-lg object-cover'
/>
<div className='absolute inset-0 rounded-lg bg-black bg-opacity-40'></div>
<MetaInfo
title={title}
author={author}
date={date}
isOverlay
/>
</div>
);
}

function TitleHeader({
title,
author,
date,
}: {
title: string;
author: string;
date: string;
}) {
return (
<div className='mx-auto mb-5 w-full max-w-3xl'>
<h1 className='text-3xl font-bold'>{title}</h1>
{title !== 'About' && title !== 'Friends' && (
<MetaInfo
author={author}
date={date}
/>
</div>
{/* Comment Section */}
{showComments && (
<DisqusComments disqusShortname={config.disqusShortname} />
)}
</article>
</div>
);
}

function MetaInfo({
title,
author,
date,
isOverlay,
}: {
title?: string;
author: string;
date: string;
isOverlay?: boolean;
}) {
return (
<div
className={`absolute ${isOverlay ? 'bottom-0 left-1/2 w-full max-w-3xl -translate-x-1/2 transform p-4 text-white' : 'mt-2 flex items-center'}`}
>
{title && <h1 className='text-3xl font-bold'>{title}</h1>}
<p className='left-1 ml-2 flex items-center'>
{author}
<span className='mx-3 text-2xl'></span>
{date.split(' ')[0]}
</p>
</div>
);
}

function CategoriesTagsList({
categories,
tags,
}: {
categories?: string[];
tags?: string[];
}) {
if (!categories && !tags) return null;

return (
<ul className='mx-auto mt-5 flex flex-col gap-4'>
{categories && (
<li className='flex items-center gap-2'>
<FaFolder className='mr-1' />
<span className='font-semibold'>分类:</span>
<ItemLinks
items={categories}
type='category'
/>
</li>
)}
{tags && (
<li className='flex items-center gap-2'>
<FaTags className='mr-1' />
<span className='font-semibold'>标签:</span>
<ItemLinks
items={tags}
type='tag'
/>
</li>
)}
</ul>
);
}

function PostContent({ contentHtml }: { contentHtml: string }) {
return (
<div
className='post-content mt-8'
dangerouslySetInnerHTML={{ __html: contentHtml }}
/>
);
}

export default PostLayout;

0 comments on commit 5cbd8a1

Please sign in to comment.