diff --git a/pages/app/about/page.tsx b/pages/app/about/page.tsx index 4e00349..3744eb1 100644 --- a/pages/app/about/page.tsx +++ b/pages/app/about/page.tsx @@ -37,6 +37,7 @@ export default async function About() { categories={[]} rawContent={''} parsedContents={aboutPageContents.parsed_content} + tableOfContents={aboutPageContents.table_of_contents} type="info" shareURL={url} /> diff --git a/pages/app/artifacts/post-for-nostter/page.tsx b/pages/app/artifacts/post-for-nostter/page.tsx new file mode 100644 index 0000000..9e01f74 --- /dev/null +++ b/pages/app/artifacts/post-for-nostter/page.tsx @@ -0,0 +1,46 @@ +import { getHostname } from '@/lib/env' +import { getInfo } from '@/lib/api/workers' +import { FullArticle } from '@/components/large/article' + +export const runtime = 'edge' + +export function generateMetadata() { + return { + title: 'Post for nostter | Maretol Base', + description: 'about Post for nostter', + openGraph: { + title: 'Post for nostter | Maretol Base', + description: 'about Post for nostter', + url: getHostname() + '/artifacts/post-for-nostter', + }, + } +} + +export default async function PostForNostter() { + const contents = await getInfo() + + const postForNostterContent = contents.filter( + (c) => c.page_pathname === '/artifacts/post-for-nostter' || c.page_pathname === 'artifacts/post-for-nostter' + )[0] + + const host = getHostname() + const path = '/artifacts/post-for-nostter' + const url = `${host}${path}` + + return ( +
+ +
+ ) +} diff --git a/pages/app/blog/[article_id]/article.tsx b/pages/app/blog/[article_id]/article.tsx index ac77572..28fc0b9 100644 --- a/pages/app/blog/[article_id]/article.tsx +++ b/pages/app/blog/[article_id]/article.tsx @@ -22,6 +22,7 @@ export default async function BlogPageArticle({ categories={content.categories} rawContent={''} parsedContents={content.parsed_content} + tableOfContents={content.table_of_contents} type="blog" shareURL={url} /> diff --git a/pages/app/blog/test/page.tsx b/pages/app/blog/test/page.tsx index 03fe4b8..22df497 100644 --- a/pages/app/blog/test/page.tsx +++ b/pages/app/blog/test/page.tsx @@ -17,6 +17,7 @@ export default async function BlogArticleTestPage() { categories={[]} rawContent={testPageContent.main_text} parsedContents={testPageContent.parsed_content} + tableOfContents={testPageContent.table_of_contents} type="blog" shareURL={``} /> diff --git a/pages/app/contact/page.tsx b/pages/app/contact/page.tsx index 8bb4384..9f7208d 100644 --- a/pages/app/contact/page.tsx +++ b/pages/app/contact/page.tsx @@ -37,6 +37,7 @@ export default async function ContactPage() { categories={[]} rawContent={contactPageContents.main_text} parsedContents={contactPageContents.parsed_content} + tableOfContents={contactPageContents.table_of_contents} type="info" shareURL={url} /> diff --git a/pages/components/large/article.tsx b/pages/components/large/article.tsx index 2f68178..dea4382 100644 --- a/pages/components/large/article.tsx +++ b/pages/components/large/article.tsx @@ -9,7 +9,7 @@ import ArticleContent from '../middle/article_content' import { rewriteImageURL } from '@/lib/image' import { originImageOption } from '@/lib/static' import ClientImage from '../small/client_image' -import { categoryAPIResult, ParsedContent } from 'api-types' +import { categoryAPIResult, ParsedContent, TableOfContents } from 'api-types' type ArticleProps = { id: string @@ -24,6 +24,7 @@ type FullAtricleProps = ArticleProps & { publishedAt: string type: 'blog' | 'info' shareURL: string + tableOfContents: TableOfContents } type ImageArticleProps = ArticleProps & { @@ -68,6 +69,7 @@ export async function FullArticle({ updatedAt, categories, parsedContents, + tableOfContents, type, shareURL, }: FullAtricleProps) { @@ -91,8 +93,8 @@ export async function FullArticle({ )} - - + +
diff --git a/pages/components/middle/article_content.tsx b/pages/components/middle/article_content.tsx index 4968c2a..50d272d 100644 --- a/pages/components/middle/article_content.tsx +++ b/pages/components/middle/article_content.tsx @@ -8,20 +8,23 @@ import LinkCard from './article_dom/linkcard' import { cn } from '@/lib/utils' import Br from './article_dom/br' import Blockquote from './article_dom/blockquote' -import { ParsedContent } from 'api-types' +import { ParsedContent, TableOfContents } from 'api-types' import { Suspense } from 'react' import LoadingLinkcard from './loading_dom/loading_linkcard' import BlogCard from './article_dom/blogcard' import LoadingBlogCard from './loading_dom/loading_blogcard' +import Table from './article_dom/table_of_contents' export default async function ArticleContent({ contents, articleID, sample, + tableOfContents, }: { contents: ParsedContent[] articleID: string sample?: boolean + tableOfContents?: TableOfContents }) { const sampleFlag = sample || false const sampleClassName = 'content-sample line-clamp-6 max-h-72' @@ -43,7 +46,7 @@ export default async function ArticleContent({ // h1 ~ h5 // 正規表現でヒットさせる if (tagName.match(/h[1-5]/)) { - return + return } // hr @@ -142,6 +145,13 @@ export default async function ArticleContent({ } else if (pOption === 'empty') { // 空行の場合。改行をいれる return
+ } else if (pOption === 'table_of_contents') { + if (tableOfContents) { + return + } else { + // 目次情報がない場合は何も表示しない + return <> + } } // どれにも該当しない場合。ほぼないはずだが、新規の p_option が追加された場合必要になる return

diff --git a/pages/components/middle/article_dom/h.tsx b/pages/components/middle/article_dom/h.tsx index 9422269..cbcb453 100644 --- a/pages/components/middle/article_dom/h.tsx +++ b/pages/components/middle/article_dom/h.tsx @@ -1,16 +1,22 @@ // CMSデータの h1, h2... は、ブログ内のコンテンツに合わせたときタイトルを h1 とする都合でそれぞれ一つずつ下の階層に置換する -export default function Hn({ tag, text }: { tag: string; text: string }) { +export default function Hn({ tag, text, attrs }: { tag: string; text: string; attrs?: { [key: string]: string } }) { + const id = attrs?.id ? attrs.id : undefined + if (tag === 'h1') { return (

-

{text}

+

+ {text} +

) } if (tag === 'h2') { return (
-

{text}

+

+ {text} +

) } @@ -18,7 +24,9 @@ export default function Hn({ tag, text }: { tag: string; text: string }) { return (
-

{text}

+

+ {text} +

) } @@ -26,12 +34,18 @@ export default function Hn({ tag, text }: { tag: string; text: string }) { return (
-
{text}
+
+ {text} +
) } if (tag === 'h5') { - return
{text}
+ return ( +
+ {text} +
+ ) } return

{text}

} diff --git a/pages/components/middle/article_dom/table_of_contents.tsx b/pages/components/middle/article_dom/table_of_contents.tsx new file mode 100644 index 0000000..ddbcbd2 --- /dev/null +++ b/pages/components/middle/article_dom/table_of_contents.tsx @@ -0,0 +1,63 @@ +import { TableOfContents } from 'api-types' + +export default function Table({ toc }: { toc: TableOfContents }) { + let beforeLevel = 0 + return ( +
+

Contents

+
+ {toc.map((t, i) => { + if (t.level <= 1) { + const levelDown = beforeLevel > t.level + beforeLevel = t.level + return ( + + ) + } else if (t.level === 2) { + const levelDown = beforeLevel > t.level + beforeLevel = t.level + return ( + + ) + } else if (t.level === 3) { + const levelDown = beforeLevel > t.level + beforeLevel = t.level + return ( + + ) + } else { + const levelDown = beforeLevel > t.level + beforeLevel = t.level + return ( + + ) + } + })} +
+
+ ) +} diff --git a/pages/lib/api/workers.ts b/pages/lib/api/workers.ts index 5671a75..de47c5d 100644 --- a/pages/lib/api/workers.ts +++ b/pages/lib/api/workers.ts @@ -1,5 +1,8 @@ import { getRequestContext } from '@cloudflare/next-on-pages' import { categoryAPIResult, contentsAPIResult, infoAPIResult, OGPResult } from 'api-types' +import { getNodeEnv } from '../env' + +const revalidateTime = getNodeEnv() === 'production' ? 60 : 0 async function getOGPData(targetURL: string) { const { env } = getRequestContext() @@ -11,7 +14,7 @@ async function getOGPData(targetURL: string) { const request = new Request(url, { headers: { 'x-api-key': ogpAPIKey }, method: 'GET' }) - const res = await fetch(request) + const res = await fetch(request, { next: { revalidate: revalidateTime } }) const data = (await res.json()) as OGPResult return data @@ -28,7 +31,7 @@ async function getCMSContents(offset?: number, limit?: number) { const request = new Request(url, { headers: { 'x-api-key': cmsAPIKey }, method: 'GET' }) - const res = await fetch(request) + const res = await fetch(request, { next: { revalidate: revalidateTime } }) const data = (await res.json()) as { contents: contentsAPIResult[]; total: number } return data @@ -45,7 +48,7 @@ async function getCMSContent(articleID: string, draftKey?: string) { const request = new Request(url, { headers: { 'x-api-key': cmsAPIKey }, method: 'GET' }) - const res = await fetch(request) + const res = await fetch(request, { next: { revalidate: revalidateTime } }) const data = (await res.json()) as contentsAPIResult return data @@ -63,7 +66,7 @@ async function getCMSContentsWithTags(tagIDs: string[], offset?: number, limit?: const request = new Request(url, { headers: { 'x-api-key': cmsAPIKey }, method: 'GET' }) - const res = await fetch(request) + const res = await fetch(request, { next: { revalidate: revalidateTime } }) const data = (await res.json()) as { contents: contentsAPIResult[]; total: number } return data @@ -78,7 +81,7 @@ async function getTags() { const request = new Request(url, { headers: { 'x-api-key': cmsAPIKey }, method: 'GET' }) - const res = await fetch(request) + const res = await fetch(request, { next: { revalidate: revalidateTime } }) const data = (await res.json()) as categoryAPIResult[] return data @@ -93,7 +96,7 @@ async function getInfo() { const request = new Request(url, { headers: { 'x-api-key': cmsAPIKey }, method: 'GET' }) - const res = await fetch(request) + const res = await fetch(request, { next: { revalidate: revalidateTime } }) const data = (await res.json()) as infoAPIResult[] return data