From 78a942818a638022ad76be0dd5b18d27ee551f18 Mon Sep 17 00:00:00 2001 From: WakuwakuP Date: Thu, 7 Nov 2024 04:53:06 +0900 Subject: [PATCH] Add next and previous article links to article detail view --- .hintrc | 8 +++ src/app/content/detail/[id]/page.tsx | 57 ++++++++++++++++++- .../templates/ContentDetail/ContentDetail.tsx | 38 ++++++++++--- .../containers/ContentCard.module.css | 3 +- .../templates/ContentDetail.module.css | 16 ++++-- 5 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 .hintrc diff --git a/.hintrc b/.hintrc new file mode 100644 index 0000000..cb34607 --- /dev/null +++ b/.hintrc @@ -0,0 +1,8 @@ +{ + "extends": [ + "development" + ], + "hints": { + "no-inline-styles": "off" + } +} \ No newline at end of file diff --git a/src/app/content/detail/[id]/page.tsx b/src/app/content/detail/[id]/page.tsx index a9af069..d9d2d83 100644 --- a/src/app/content/detail/[id]/page.tsx +++ b/src/app/content/detail/[id]/page.tsx @@ -29,7 +29,55 @@ const cachedGetContentDetail = (id: string) => async () => { return await getContentDetail(id) }, - [`content-detail-${id}`], + ['content-detail', id], + { + tags: [`content-detail-${id}`], + }, + ) + +const getNextContent = async (publishedAt: string) => { + return await client + .get({ + endpoint: 'contents', + queries: { + filters: `publishedAt[less_than]${publishedAt}`, + orders: '-publishedAt', + limit: 1, + }, + }) + .catch(() => undefined) +} + +const cachedGetNextContent = (publishedAt: string, id: string) => + unstable_cache( + async () => { + return await getNextContent(publishedAt) + }, + ['content-detail-next', id], + { + tags: [`content-detail-${id}`], + }, + ) + +const getPrevContent = async (publishedAt: string) => { + return await client + .get({ + endpoint: 'contents', + queries: { + filters: `publishedAt[greater_than]${publishedAt}`, + orders: 'publishedAt', + limit: 1, + }, + }) + .catch(() => undefined) +} + +const cachedGetPreviousContent = (publishedAt: string, id: string) => + unstable_cache( + async () => { + return await getPrevContent(publishedAt) + }, + ['content-detail-prev', id], { tags: [`content-detail-${id}`], }, @@ -75,6 +123,11 @@ export default async function ContentDetailPage({ params }: { params: Params }) notFound() } + const getNextContent = cachedGetNextContent(content.publishedAt, id) + const getPreviousContent = cachedGetPreviousContent(content.publishedAt, id) + const nextContent = (await getNextContent()).contents[0] ?? null + const previousContent = (await getPreviousContent()).contents[0] ?? null + const body = content.content.reduce((acc: string, cur: { fieldId: string; html: string }) => acc + cur.html, '') const $ = cheerio.load(body) @@ -116,6 +169,8 @@ export default async function ContentDetailPage({ params }: { params: Params }) }), }} toc={createTableOfContents(body, tocOption)} + nextContent={nextContent ?? null} + previousContent={previousContent ?? null} /> ) } diff --git a/src/components/templates/ContentDetail/ContentDetail.tsx b/src/components/templates/ContentDetail/ContentDetail.tsx index 22d5109..1d97903 100644 --- a/src/components/templates/ContentDetail/ContentDetail.tsx +++ b/src/components/templates/ContentDetail/ContentDetail.tsx @@ -20,10 +20,12 @@ export interface ContentDetailProps { text: string name: string }[] + nextContent?: ContentModify | null + previousContent?: ContentModify | null } // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -export const ContentDetail = ({ content, toc }: ContentDetailProps) => { +export const ContentDetail = ({ content, toc, nextContent, previousContent }: ContentDetailProps) => { const ref = useRef(null) const elemMainarea = useRef(null) const elemToc = useRef(null) @@ -61,13 +63,33 @@ export const ContentDetail = ({ content, toc }: ContentDetailProps) => { ))}
-
+
+
+
+ {previousContent ? ( + +
Previous
+
{previousContent.title}
+ + ) : ( +
+ )} + {nextContent ? ( + +
Next
+
{nextContent.title}
+ + ) : ( +
+ )} +
+
+
diff --git a/src/styles/components/containers/ContentCard.module.css b/src/styles/components/containers/ContentCard.module.css index 42f4c9a..e488de4 100644 --- a/src/styles/components/containers/ContentCard.module.css +++ b/src/styles/components/containers/ContentCard.module.css @@ -1,5 +1,5 @@ .card { - width: clamp(50%,calc((640px - 100%) * 640),100%); + width: clamp(50%, calc((640px - 100%) * 640), 100%); height: auto; margin-bottom: 6px; } @@ -37,4 +37,3 @@ object-fit: cover; aspect-ratio: 16 / 9; } - diff --git a/src/styles/components/templates/ContentDetail.module.css b/src/styles/components/templates/ContentDetail.module.css index 4fa09f9..cbfa183 100644 --- a/src/styles/components/templates/ContentDetail.module.css +++ b/src/styles/components/templates/ContentDetail.module.css @@ -5,15 +5,14 @@ --toc-wrap-width: 800; } - - .content { padding: 0 0.5rem; - width: clamp(calc(100% - var(--toc-width)), calc((800px - 100%) * 800),100%); + width: clamp(calc(100% - var(--toc-width)), calc((800px - 100%) * 800), 100%); + overflow-wrap: break-word; } .toc { - width: clamp(var(--toc-width), calc((800px - 100%) * 800),100%); + width: clamp(var(--toc-width), calc((800px - 100%) * 800), 100%); } .tocWrapper { @@ -26,7 +25,7 @@ } .content img { - width:100%; + width: 100%; height: auto; } @@ -58,3 +57,10 @@ border: solid 1px var(--table-border-color); } +.contentsLink { + display: flex; + gap: 20px; + margin: 2rem 0; + padding: 1rem 1rem; + border-top: solid 1px white; +}