Skip to content

Commit

Permalink
Merge pull request #201 from maretol/add-artifact-page
Browse files Browse the repository at this point in the history
Add artifact page
  • Loading branch information
maretol authored Dec 15, 2024
2 parents 06940c7 + 99923d2 commit 2906244
Show file tree
Hide file tree
Showing 13 changed files with 200 additions and 24 deletions.
16 changes: 12 additions & 4 deletions cms-data-fetcher/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,36 @@ export default {
const limit = parseInt(limitStr)
const contents = await getContentsByTag(cmsApiKey, tagIDs, offset, limit)
contents.contents.forEach((c) => {
c.parsed_content = parse(c.content)
const parsed = parse(c.content)
c.parsed_content = parsed.contents_array
c.table_of_contents = parsed.table_of_contents
})
return Response.json(contents)
} else if (pathname.includes('/cms/get_contents')) {
const offset = parseInt(offsetStr)
const limit = parseInt(limitStr)
const contents = await getContents(cmsApiKey, offset, limit)
contents.contents.forEach((c) => {
c.parsed_content = parse(c.content)
const parsed = parse(c.content)
c.parsed_content = parsed.contents_array
c.table_of_contents = parsed.table_of_contents
})
return Response.json(contents)
} else if (pathname.includes('/cms/get_content')) {
const content = await getContent(cmsApiKey, articleID, draftKey)
content.parsed_content = parse(content.content)
const parsed = parse(content.content)
content.parsed_content = parsed.contents_array
content.table_of_contents = parsed.table_of_contents
return Response.json(content)
} else if (pathname.includes('/cms/get_tags')) {
const tags = await getTags(cmsApiKey)
return Response.json(tags)
} else if (pathname.includes('/cms/get_info')) {
const info = await getInfo(cmsApiKey)
info.forEach((i) => {
i.parsed_content = parse(i.main_text)
const parsed = parse(i.main_text)
i.parsed_content = parsed.contents_array
i.table_of_contents = parsed.table_of_contents
})
return Response.json(info)
} else {
Expand Down
22 changes: 20 additions & 2 deletions cms-data-fetcher/src/parse.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { load } from 'cheerio'
import { ParsedContent } from 'api-types'
import { ParsedContent, TableOfContents } from 'api-types'

export function parse(content: string) {
const $ = load(content)

const toc: TableOfContents = []

const details = $('body > *').map((index, element) => {
const tagName = element.tagName
const attrs = element.attribs
Expand All @@ -12,6 +14,16 @@ export function parse(content: string) {
const innerHTML = $(element).html()
const pOpt = tagName === 'p' ? getPOption(text) : null
const sub_texts = getSubText(raw_text)

// 目次の対象( <span class="index"> )がある場合、目次の配列に対象を追加
if ($(element).find('span.index').length > 0) {
toc.push({
id: $(element).attr('id') || '',
title: text,
level: parseInt(tagName.slice(-1)),
})
}

return {
index,
tag_name: tagName,
Expand All @@ -24,7 +36,7 @@ export function parse(content: string) {
} as ParsedContent
})

return details.toArray()
return { contents_array: details.toArray(), table_of_contents: toc }
}

function getPOption(text: string) {
Expand All @@ -46,6 +58,8 @@ function getPOption(text: string) {
pOpt = 'url'
} else if (text === '') {
pOpt = 'empty'
} else if (isCommand(text)) {
pOpt = text.replaceAll('/', '')
}
return pOpt
}
Expand Down Expand Up @@ -96,6 +110,10 @@ function isURL(text: string) {
return text.indexOf('https://') === 0
}

function isCommand(text: string) {
return text.indexOf('/') === 0
}

// サブテキストが付与されている場合、本文のみを返す
function getText(text: string) {
if (text.split('@@').length > 1) {
Expand Down
10 changes: 9 additions & 1 deletion packages/api-types/src/cms_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ type ParsedContent = {
p_option: string | null
}

type TableOfContents = {
id: string
title: string
level: number
}[]

type contentsAPIResult = {
id: string
createdAt: string
Expand All @@ -23,6 +29,7 @@ type contentsAPIResult = {
title: string
content: string
parsed_content: ParsedContent[]
table_of_contents: TableOfContents
ogp_image: string | undefined | null
categories: categoryAPIResult[]
}
Expand All @@ -45,6 +52,7 @@ type infoAPIResult = {
page_pathname: string
main_text: string
parsed_content: ParsedContent[]
table_of_contents: TableOfContents
}

export type { RequestJSONType, ParsedContent, contentsAPIResult, categoryAPIResult, infoAPIResult }
export type { RequestJSONType, ParsedContent, TableOfContents, contentsAPIResult, categoryAPIResult, infoAPIResult }
1 change: 1 addition & 0 deletions pages/app/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default async function About() {
categories={[]}
rawContent={''}
parsedContents={aboutPageContents.parsed_content}
tableOfContents={aboutPageContents.table_of_contents}
type="info"
shareURL={url}
/>
Expand Down
46 changes: 46 additions & 0 deletions pages/app/artifacts/post-for-nostter/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="flex flex-col justify-center gap-10">
<FullArticle
id={'post-for-nostter'}
title="Post for nostter"
publishedAt={postForNostterContent.publishedAt}
updatedAt={postForNostterContent.updatedAt}
categories={[]}
rawContent={''}
parsedContents={postForNostterContent.parsed_content}
tableOfContents={postForNostterContent.table_of_contents}
type="info"
shareURL={url}
/>
</div>
)
}
1 change: 1 addition & 0 deletions pages/app/blog/[article_id]/article.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
/>
Expand Down
1 change: 1 addition & 0 deletions pages/app/blog/test/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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={``}
/>
Expand Down
1 change: 1 addition & 0 deletions pages/app/contact/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
/>
Expand Down
8 changes: 5 additions & 3 deletions pages/components/large/article.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -24,6 +24,7 @@ type FullAtricleProps = ArticleProps & {
publishedAt: string
type: 'blog' | 'info'
shareURL: string
tableOfContents: TableOfContents
}

type ImageArticleProps = ArticleProps & {
Expand Down Expand Up @@ -68,6 +69,7 @@ export async function FullArticle({
updatedAt,
categories,
parsedContents,
tableOfContents,
type,
shareURL,
}: FullAtricleProps) {
Expand All @@ -91,8 +93,8 @@ export async function FullArticle({
</CardContent>
)}
</CardHeader>
<CardContent className="my-8">
<ArticleContent contents={parsedContents} articleID={id} />
<CardContent className="mb-8">
<ArticleContent contents={parsedContents} articleID={id} tableOfContents={tableOfContents} />
</CardContent>
<CardFooter>
<div className="w-full">
Expand Down
14 changes: 12 additions & 2 deletions pages/components/middle/article_content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -43,7 +46,7 @@ export default async function ArticleContent({
// h1 ~ h5
// 正規表現でヒットさせる
if (tagName.match(/h[1-5]/)) {
return <Hn key={i} tag={tagName} text={text} />
return <Hn key={i} tag={tagName} text={text} attrs={attrs} />
}

// hr
Expand Down Expand Up @@ -142,6 +145,13 @@ export default async function ArticleContent({
} else if (pOption === 'empty') {
// 空行の場合。改行をいれる
return <Br key={i} />
} else if (pOption === 'table_of_contents') {
if (tableOfContents) {
return <Table key={i} toc={tableOfContents} />
} else {
// 目次情報がない場合は何も表示しない
return <></>
}
}
// どれにも該当しない場合。ほぼないはずだが、新規の p_option が追加された場合必要になる
return <P key={i} innerHTML={innerHTML || text} attrs={attrs} />
Expand Down
26 changes: 20 additions & 6 deletions pages/components/middle/article_dom/h.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,51 @@
// 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 (
<div className="pt-6 pb-3 space-y-0">
<h2 className="text-xl font-bold pl-2 pb-1 content-h2">{text}</h2>
<h2 id={id} className="text-xl font-bold pl-2 pb-1 content-h2">
{text}
</h2>
</div>
)
}
if (tag === 'h2') {
return (
<div className="pt-6 pb-3 space-y-0">
<h3 className="text-lg font-bold border-blue-900 pl-3 border-l-4">{text}</h3>
<h3 id={id} className="text-lg font-bold border-blue-900 pl-3 border-l-4">
{text}
</h3>
</div>
)
}
if (tag === 'h3') {
return (
<div className="flex flex-row items-center pt-4 space-x-2">
<div className="w-2 h-2 rounded-full bg-blue-900 inline-block"></div>
<h4 className="text-lg font-bold">{text}</h4>
<h4 id={id} className="text-lg font-bold">
{text}
</h4>
</div>
)
}
if (tag === 'h4') {
return (
<div className="flex flex-row items-center pt-4 pb-4 space-x-2">
<div className="w-2 h-1 rounded-full bg-blue-900 inline-block"></div>
<h5 className="font-bold ">{text}</h5>
<h5 id={id} className="font-bold ">
{text}
</h5>
</div>
)
}
if (tag === 'h5') {
return <h6 className="pt-2">{text}</h6>
return (
<h6 id={id} className="pt-2">
{text}
</h6>
)
}
return <p>{text}</p>
}
Loading

0 comments on commit 2906244

Please sign in to comment.