feat(blog) add footer for post reading part

This commit is contained in:
lumither committed 2024-07-26 19:30:43 +08:00
1 parent dbe84bc9f4
commit 36f686c563
11 files changed
+177 -22

No files matched your search

+1 -1
View File
@@ -230,7 +230,7 @@ $$
_insert_columns TEXT := 'post_id'; _insert_columns TEXT := 'post_id';
_insert_values TEXT := quote_literal(_post_id); _insert_values TEXT := quote_literal(_post_id);
_update_set TEXT := ''; _update_set TEXT := '';
_blacklist TEXT[] := ARRAY['id', 'tags']; _blacklist TEXT[] := ARRAY['tags'];
BEGIN BEGIN
SELECT EXISTS(SELECT 1 FROM meta WHERE post_id = _post_id) INTO _exists; SELECT EXISTS(SELECT 1 FROM meta WHERE post_id = _post_id) INTO _exists;
+1 -1
View File
@@ -7,7 +7,7 @@ function Layout({
}) { }) {
return ( return (
// <div className={ 'flex flex-auto p-7 pl-2 md:pl-7 items-stretch justify-center' }> // <div className={ 'flex flex-auto p-7 pl-2 md:pl-7 items-stretch justify-center' }>
<div className={ 'flex flex-auto p-7 pl-2 md:pl-7 w-full justify-center' }> <div className={ 'flex flex-auto p-7 pl-2 lg:pl-7 w-full justify-center' }>
{ children } { children }
</div> </div>
); );
+90
View File
@@ -0,0 +1,90 @@
import React from 'react';
import MinifyPostCard from '@/app/blog/post/[id]/MinifyPostCard';
import Link from 'next/link';
import { FaCreativeCommons } from 'react-icons/fa6';
type Props = {
post: any
}
const Footer = (props: Props) => {
const { post } = props;
const header_img = post['header_img'];
const adjacent_posts = post['adj'];
const prev_post = adjacent_posts['prev'];
const next_post = adjacent_posts['next'];
const link = post['meta']['link'] ?? `https://lumither.com/blog/post/${ post['meta']['id'] }`;
const author = post['meta']['author'] ?? 'Tao';
const author_link = post['meta']['author_link'] ?? '/about';
const post_date = new Date(post['first_update']);
const update_date = new Date(post['last_update']);
const cc = post['meta']['cc'] ?? 'by-nc-sa';
return (
<div className={ `w-full` }>
<div className={ 'flex transition bg-zinc-300/80 dark:bg-gray-600/25' }>
<div className={ `p-8 w-full` }>
<div className={ 'flex flex-col space-y-4 relative' }>
<div className={ 'z-[2]' }>
<p className={ 'text-xl font-bold' }>{ post['meta']['title'] }</p>
<Link href={ link }>
<p className={ 'text-sm underline decoration-indigo-500 dark:decoration-sky-500' }>{ link }</p>
</Link>
</div>
<div className={ 'z-[2] flex flex-row flex-wrap gap-x-7 gap-y-2' }>
<div>
<p className={ 'text-lg font-bold' }>Author</p>
<Link href={ author_link }>
<p className={ 'underline decoration-indigo-500 dark:decoration-sky-500' }>{ author }</p>
</Link>
</div>
<div>
<p className={ 'text-lg font-bold' }>Posted on</p>
<p>{ post_date.toLocaleString('default', {
month: `long`,
day: `numeric`,
year: `numeric`
}) }</p>
</div>
<div>
<p className={ 'text-lg font-bold' }>Updated on</p>
<p>{ update_date.toLocaleString('default', {
month: `long`,
day: `numeric`,
year: `numeric`
}) }</p>
</div>
<div>
<p className={ 'text-lg font-bold' }>Published under</p>
<Link href={ `https://creativecommons.org/licenses/${ cc }/4.0/` }>
<p className={ 'underline decoration-indigo-500 dark:decoration-sky-500' }>{ `CC ${ cc.toUpperCase() } 4.0` }</p>
</Link>
</div>
</div>
<FaCreativeCommons size={ '80px' }
className={ 'absolute inset-y-0 right-0 opacity-50 brightness-75 z-0' } />
</div>
</div>
</div>
<div className={ `flex justify-center` }>
<div className={ `w-full shadow-2xl ` }>
<div className={ `flex flex-col md:flex-row w-full` }>
{
next_post &&
<MinifyPostCard post={ next_post } desc={ 'prev' }></MinifyPostCard>
}
{
prev_post &&
<MinifyPostCard post={ prev_post } desc={ 'next' }></MinifyPostCard>
}
</div>
</div>
</div>
</div>
);
};
export default Footer;
+9 -9
View File
@@ -10,15 +10,15 @@ type Props = {
const Headers = (props: Props) => { const Headers = (props: Props) => {
let post = props.post; let post = props.post;
let title = (post as any)['title']; let title = post['title'];
let sub_title = (post as any)['sub_title']; let sub_title = post['sub_title'];
// let summary = (post as any)['summary']; // let summary = (post as any)['summary'];
let category = (post as any)['category']; let category = post['category'];
let tags = (post as any)['tags']; let tags = post['tags'];
let last_update = new Date((post as any)['last_update']); // let last_update = new Date((post as any)['last_update']);
let first_update = new Date((post as any)['first_update']); let first_update = new Date(post['first_update']);
let post_id = (post as any)['post_id']; // let post_id = (post as any)['post_id'];
let header_img = (post as any)['header_img']; let header_img = post['header_img'];
return ( return (
<> <>
@@ -39,7 +39,7 @@ const Headers = (props: Props) => {
<div <div
className={ header_img !== '' ? className={ header_img !== '' ?
'mx-8 -mt-16 backdrop-blur bg-zinc-300/80 dark:bg-gray-600/25 rounded-2xl z-30' 'mx-8 -mt-16 backdrop-blur transition bg-zinc-300/80 dark:bg-gray-600/25 rounded-2xl z-30'
: 'mx-6 mt-6' : 'mx-6 mt-6'
}> }>
<div className={ `p-4` }> <div className={ `p-4` }>
@@ -0,0 +1,56 @@
import React from 'react';
import { Card, CardFooter, CardHeader } from '@nextui-org/card';
import Image from 'next/image';
import Link from 'next/link';
type Props = {
post: any
desc: string
}
const MinifyPostCard = (props: Props) => {
const { post, desc } = props;
const img_url = post['header_img'] as string;
return (
<div className={ 'w-full transition hover:brightness-75' }>
<Card isFooterBlurred
className="w-full h-[300px] shadow-none border-0 rounded-none overflow-hidden"
as={ Link }
isPressable
href={ `/blog/post/${ post['id'] }` }
>
<CardHeader className="absolute z-10 top-1 flex-col items-start">
<p className={ `text-tiny uppercase font-bold ${ img_url ? 'text-white/60' : 'text-black/60 dark:text-white/60' }` }>{ desc }</p>
</CardHeader>
{ img_url &&
<div>
<Image
className={ 'object-cover object-center ' }
src={ img_url }
alt={ post.title }
fill
/>
<CardFooter
className="absolute transition-colors bg-zinc-300/80 dark:bg-gray-600/25 dark:backdrop-brightness-75 bottom-0 justify-between rounded-none">
<div>
<p className="text-2xl font-bold">{ post['title'] }</p>
<p className="">{ post['sub_title'] }</p>
</div>
</CardFooter>
</div>
}
{ !img_url &&
<div className={ 'h-full grid p-6 place-content-center' }>
<div className={ 'flex flex-row items-center text-center max-h-fit' }>
<p className={ 'text-3xl font-bold' }>{ post['title'] }</p>
<div className={ 'h-full w-1 mx-2 bg-zinc-500 dark:bg-zinc-400' }></div>
<p className={ 'text-xl' }>{ post['sub_title'] }</p>
</div>
</div>
}
</Card>
</div>
);
};
export default MinifyPostCard;
+7
View File
@@ -10,6 +10,7 @@ import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import rehypeRaw from 'rehype-raw'; import rehypeRaw from 'rehype-raw';
import rehypeSanitize from 'rehype-sanitize'; import rehypeSanitize from 'rehype-sanitize';
import Headers from '@/app/blog/post/[id]/Headers'; import Headers from '@/app/blog/post/[id]/Headers';
import Footer from '@/app/blog/post/[id]/Footer';
type Props = { type Props = {
id: string id: string
@@ -87,6 +88,12 @@ async function PostCard(props: Props) {
} } } }
>{ (post_data as any)['content'] }</Markdown> >{ (post_data as any)['content'] }</Markdown>
</CardBody> </CardBody>
{/* card footer */ }
<Footer post={ post_data } />
</Card> </Card>
</div> </div>
); );
+1 -1
View File
@@ -18,7 +18,7 @@ export default function RootLayout({
}>) { }>) {
return ( return (
<html lang="en" suppressHydrationWarning> <html lang="en" suppressHydrationWarning>
<body className={ `dark:bg-[#282830] min-h-screen` }> <body className={ `transition duration-300 dark:bg-[#282830] min-h-screen` }>
<NextThemesProvider attribute="class" defaultTheme="dark"> <NextThemesProvider attribute="class" defaultTheme="dark">
<div className={ `justify-center flex flex-row w-full max-w-full` }> <div className={ `justify-center flex flex-row w-full max-w-full` }>
<Navbar /> <Navbar />
+8 -8
View File
@@ -89,7 +89,7 @@ function Navbar() {
}, [ path ]); }, [ path ]);
const minMobileWidth: number = 768; const minMobileWidth: number = 1024;
const [ isMobile, setIsMobile ] = useState(false); const [ isMobile, setIsMobile ] = useState(false);
useEffect(() => { useEffect(() => {
const updateMobileWidth = () => { const updateMobileWidth = () => {
@@ -111,9 +111,9 @@ function Navbar() {
return ( return (
<> <>
<div <div
className={ `flex flex-col min-w-[50px] md:min-w-[256px] mr-0 md:mr-2 ml-2 md:ml-8 min-h-dvh pt-8 sticky` }> className={ `flex flex-col min-w-[50px] lg:min-w-[256px] mr-0 lg:mr-2 ml-2 lg:ml-8 min-h-dvh pt-8 sticky` }>
<div className={ `fixed min-h-screen h-full` }> <div className={ `fixed min-h-screen h-full` }>
<div className={ `hidden md:block` }> <div className={ `hidden lg:block` }>
<div className={ `flex justify-center items-center my-7` }> <div className={ `flex justify-center items-center my-7` }>
<NextImage <NextImage
src={ 'https://oss.lumither.com/blog/pictures/compr/1717153492846703409_avatar.webp' } src={ 'https://oss.lumither.com/blog/pictures/compr/1717153492846703409_avatar.webp' }
@@ -132,7 +132,7 @@ function Navbar() {
</div> </div>
<div> <div>
<ul className={ `flex flex-col space-y-2 md:space-y-0 md:flex-row items-center my-4 md:space-x-2` }> <ul className={ `flex flex-col space-y-2 lg:space-y-0 lg:flex-row items-center my-4 lg:space-x-2` }>
{ {
connections.map((conn, key) => ( connections.map((conn, key) => (
<li key={ key }> <li key={ key }>
@@ -154,7 +154,7 @@ function Navbar() {
</div> </div>
<div <div
className={ `flex justify-center md:justify-start w-full mt-8 ml-0 md:-ml-3` }> className={ `flex justify-center lg:justify-start w-full mt-8 ml-0 lg:-ml-3` }>
<ul className={ `flex flex-col space-y-2 w-full` }> <ul className={ `flex flex-col space-y-2 w-full` }>
{ {
entries.map((meta, key) => ( entries.map((meta, key) => (
@@ -165,11 +165,11 @@ function Navbar() {
color={ `default` } color={ `default` }
fullWidth fullWidth
isIconOnly={ isMobile } isIconOnly={ isMobile }
className={ 'flex md:justify-start w-fit md:w-full' } className={ 'flex lg:justify-start w-fit lg:w-full' }
aria-label={ `navbar: ${ meta.display_name }` } aria-label={ `navbar: ${ meta.display_name }` }
href={ meta.href }> href={ meta.href }>
{ meta.icon } { meta.icon }
<p className={ `font-bold hidden md:block` }>{ meta.display_name }</p> <p className={ `font-bold hidden lg:block` }>{ meta.display_name }</p>
</Button> </Button>
</li> </li>
)) ))
@@ -177,7 +177,7 @@ function Navbar() {
</ul> </ul>
</div> </div>
<div className={ `mt-8 flex md:hidden justify-center w-full` }> <div className={ `mt-8 flex lg:hidden justify-center w-full` }>
<p className={ `text-center sideways-lr font-bold text-2xl text-zinc-500 dark:text-zinc-400` }> <p className={ `text-center sideways-lr font-bold text-2xl text-zinc-500 dark:text-zinc-400` }>
{ shownPath } { shownPath }
</p> </p>
+1 -1
View File
@@ -33,7 +33,7 @@ function ThemeSwitcher() {
} }
} } } }
></Switch> ></Switch>
<p className={ `font-bold hidden md:block` }>{ `Toggle Theme` }</p> <p className={ `font-bold hidden lg:block` }>{ `Toggle Theme` }</p>
</div> </div>
</div> </div>
); );
+1
View File
@@ -33,6 +33,7 @@
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
"autoprefixer": "^10.4.19",
"eslint": "^8", "eslint": "^8",
"eslint-config-next": "14.2.2", "eslint-config-next": "14.2.2",
"postcss": "^8", "postcss": "^8",
+2 -1
View File
@@ -2,7 +2,8 @@
const config = { const config = {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
}, autoprefixer: {}
}
}; };
export default config; export default config;