From 6c059509cac3afb6b0dbd661bdd95c59cf289d2e Mon Sep 17 00:00:00 2001 From: gapry Date: Thu, 5 Mar 2026 12:09:16 +0800 Subject: [PATCH] blog: list posts on the home page --- .gitignore | 3 + build.js | 53 ++++++++---- public/posts/2025/2025-12-13-Test7.md | 1 + public/posts/2026/2026-01-29-Test3.md | 1 + public/posts/2026/2026-01-31-Test123.md | 1 + public/posts/2026/2026-02-03-Test5.md | 1 + .../2026/{Test.md => 2026-03-05-Test.md} | 0 src/App.jsx | 86 +++++++++++-------- src/Home.jsx | 37 ++++++++ 9 files changed, 132 insertions(+), 51 deletions(-) create mode 100644 public/posts/2025/2025-12-13-Test7.md create mode 100644 public/posts/2026/2026-01-29-Test3.md create mode 100644 public/posts/2026/2026-01-31-Test123.md create mode 100644 public/posts/2026/2026-02-03-Test5.md rename public/posts/2026/{Test.md => 2026-03-05-Test.md} (100%) create mode 100644 src/Home.jsx diff --git a/.gitignore b/.gitignore index a547bf3..e114360 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Posts +public/posts.json + # Logs logs *.log diff --git a/build.js b/build.js index 5af3c71..00de931 100644 --- a/build.js +++ b/build.js @@ -3,7 +3,7 @@ import path from 'path'; const postsDir = './public/posts'; const distDir = './dist'; - +const allPosts = []; const years = fs.readdirSync(postsDir); years.forEach(year => { @@ -12,23 +12,46 @@ years.forEach(year => { const files = fs.readdirSync(yearPath); files.forEach(file => { if (file.endsWith('.md')) { - const slug = file.replace('.md', ''); - const targetDir = path.join(distDir, year); - - if (!fs.existsSync(targetDir)) { - fs.mkdirSync(targetDir, { - recursive: true - }); - } - fs.copyFileSync(path.join(distDir, 'index.html'), path.join(targetDir, `${slug}.html`)); + const fileName = file.replace('.md', ''); + const parts = fileName.split('-'); + const date = parts.slice(0, 3).join('-'); + const slug = parts.slice(3).join('-'); + + allPosts.push({ + year, + date, + slug, + originalName: fileName, + title: slug.replace(/-/g, ' ') + }); } }); } }); -fs.copyFileSync( - path.join(distDir, 'index.html'), - path.join(distDir, '404.html') -); +allPosts.sort((a, b) => b.date.localeCompare(a.date)); + +const postsData = JSON.stringify(allPosts, null, 2); +fs.writeFileSync('./public/posts.json', postsData); + +if (fs.existsSync(path.join(distDir, 'index.html'))) { + fs.writeFileSync(path.join(distDir, 'posts.json'), postsData); + + allPosts.forEach(post => { + const targetDir = path.join(distDir, post.year); + if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }); + } + fs.copyFileSync( + path.join(distDir, 'index.html'), + path.join(targetDir, `${post.slug}.html`) + ); + }); + + fs.copyFileSync( + path.join(distDir, 'index.html'), + path.join(distDir, '404.html') + ); +} -console.log('✅ Build HTML from Markdown Post'); \ No newline at end of file +console.log(`✅ Build ${allPosts.length} Posts Successfully`); diff --git a/public/posts/2025/2025-12-13-Test7.md b/public/posts/2025/2025-12-13-Test7.md new file mode 100644 index 0000000..7b82441 --- /dev/null +++ b/public/posts/2025/2025-12-13-Test7.md @@ -0,0 +1 @@ +Test7 file \ No newline at end of file diff --git a/public/posts/2026/2026-01-29-Test3.md b/public/posts/2026/2026-01-29-Test3.md new file mode 100644 index 0000000..0c1d4e5 --- /dev/null +++ b/public/posts/2026/2026-01-29-Test3.md @@ -0,0 +1 @@ +test3 file \ No newline at end of file diff --git a/public/posts/2026/2026-01-31-Test123.md b/public/posts/2026/2026-01-31-Test123.md new file mode 100644 index 0000000..a208335 --- /dev/null +++ b/public/posts/2026/2026-01-31-Test123.md @@ -0,0 +1 @@ +test 123 file \ No newline at end of file diff --git a/public/posts/2026/2026-02-03-Test5.md b/public/posts/2026/2026-02-03-Test5.md new file mode 100644 index 0000000..4f1bd33 --- /dev/null +++ b/public/posts/2026/2026-02-03-Test5.md @@ -0,0 +1 @@ +test5 \ No newline at end of file diff --git a/public/posts/2026/Test.md b/public/posts/2026/2026-03-05-Test.md similarity index 100% rename from public/posts/2026/Test.md rename to public/posts/2026/2026-03-05-Test.md diff --git a/src/App.jsx b/src/App.jsx index 683cbd7..a55115d 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,61 +2,75 @@ import { useState, useEffect } from 'react'; import ReactMarkdown from 'react-markdown'; import Analytics from './Analytics'; import NotFound from './NotFound'; +import Home from './Home'; -const allPostFiles = import.meta.glob('/public/posts/**/*.md', { query: '?url', import: 'default' }); - -function App() { +export default function App() { const [content, setContent] = useState(''); + const [posts, setPosts] = useState([]); const [status, setStatus] = useState('loading'); useEffect(() => { const params = new URLSearchParams(window.location.search); const redirectedPath = params.get('p'); - const currentPath = redirectedPath || window.location.pathname; + let currentPath = redirectedPath || window.location.pathname; if (redirectedPath) { window.history.replaceState(null, '', redirectedPath); } - if (currentPath === '/' || currentPath === '/index.html') { - setContent('# Welcome My Blog'); - setStatus('success'); - return; - } + fetch('/posts.json') + .then(res => res.json()) + .then(data => { + setPosts(data); + + const pathClean = currentPath.replace(/\.html$/, ''); + const parts = pathClean.split('/').filter(Boolean); - const parts = currentPath.replace(/\.html$/, '').split('/').filter(Boolean); - const [year, slug] = parts; - - if (year && slug) { - const expectedPath = `/public/posts/${year}/${slug}.md`; - - if (allPostFiles[expectedPath]) { - fetch(`/posts/${year}/${slug}.md`) - .then(res => res.text()) - .then(text => { - setContent(text); - setStatus('success'); - }) - .catch(() => setStatus('404')); - } else { - setStatus('404'); - } - } else { - setStatus('404'); - } + if (parts.length === 0 || (parts.length === 1 && parts[0] === 'index')) { + setStatus('home'); + return; + } + + if (parts.length === 2) { + const [year, slug] = parts; + const found = data.find(p => p.year === year && p.slug === slug); + + if (found) { + fetch(`/posts/${year}/${found.originalName}.md`) + .then(res => res.text()) + .then(text => { + setContent(text); + setStatus('post'); + }) + .catch(() => setStatus('404')); + } else { + setStatus('404'); + } + } else { + setStatus('404'); + } + }) + .catch(() => setStatus('404')); }, []); if (status === 'loading') return
Loading...
; - if (status === '404') return ; return ( <> -
- {content} -
+
+ {status === '404' ? ( + + ) : status === 'home' ? ( + + ) : ( + + )} +
); -} - -export default App; \ No newline at end of file +} \ No newline at end of file diff --git a/src/Home.jsx b/src/Home.jsx new file mode 100644 index 0000000..e27028d --- /dev/null +++ b/src/Home.jsx @@ -0,0 +1,37 @@ +import { useState } from 'react'; + +const POSTS_PER_PAGE = 10; + +export default function Home({ posts }) { + const [currentPage, setCurrentPage] = useState(0); + + const startIndex = currentPage * POSTS_PER_PAGE; + const currentPosts = posts.slice(startIndex, startIndex + POSTS_PER_PAGE); + const hasNext = startIndex + POSTS_PER_PAGE < posts.length; + const hasPrev = currentPage > 0; + + return ( +
+

Recent Posts

+ + + +
+ ); +} \ No newline at end of file