diff --git a/components/Layout/ContentTree.tsx b/components/Layout/ContentTree.tsx new file mode 100644 index 0000000..4c2d3a0 --- /dev/null +++ b/components/Layout/ContentTree.tsx @@ -0,0 +1,50 @@ +import Link from 'next/link'; +import { FC } from 'react'; +import { Badge } from 'react-bootstrap'; + +import { XContent } from '../../models/Wiki'; + +export interface ContentTreeProps { + nodes: XContent[]; + basePath: string; + level?: number; + metaKey?: string; +} + +export const ContentTree: FC = ({ + nodes, + basePath, + level = 0, + metaKey = 'category', +}) => ( +
    + {nodes.map(({ path, name, type, meta, children }) => ( +
  1. 0 ? 'ms-3' : ''}> + {type !== 'dir' ? ( + + {name} + + {meta?.[metaKey] && ( + + {meta[metaKey]} + + )} + + ) : ( + children?.[0] && ( +
    + {name} + + +
    + ) + )} +
  2. + ))} +
+); diff --git a/components/Navigator/MainNavigator.tsx b/components/Navigator/MainNavigator.tsx index 58b8d41..8894306 100644 --- a/components/Navigator/MainNavigator.tsx +++ b/components/Navigator/MainNavigator.tsx @@ -60,6 +60,7 @@ const topNavBarMenu = ({ t }: typeof i18n): MenuItem[] => [ subs: [ { href: '/wiki', title: t('wiki') }, { href: '/policy', title: t('policy') }, + { href: '/recipe', title: t('recipe') }, ], }, ]; diff --git a/models/Wiki.ts b/models/Wiki.ts index 36648bb..d4448ae 100644 --- a/models/Wiki.ts +++ b/models/Wiki.ts @@ -13,6 +13,8 @@ export interface XContent extends Content { export const policyContentStore = new ContentModel('fpsig', 'open-source-policy'); +export const recipeContentStore = new ContentModel('Gar-b-age', 'CookLikeHOC'); + export class MyWikiNodeModel extends WikiNodeModel { client = lark.client; } diff --git a/next.config.ts b/next.config.ts index f47a8f3..53b2eac 100644 --- a/next.config.ts +++ b/next.config.ts @@ -38,6 +38,10 @@ const rewrites: NextConfig['rewrites'] = async () => ({ source: '/proxy/geo.datav.aliyun.com/:path*', destination: 'https://geo.datav.aliyun.com/:path*', }, + { + source: '/recipe/images/:path*', + destination: 'https://raw.githubusercontent.com/Gar-b-age/CookLikeHOC/main/images/:path*', + }, ], afterFiles: [], }); diff --git a/pages/api/core.ts b/pages/api/core.ts index df6f99b..cec836a 100644 --- a/pages/api/core.ts +++ b/pages/api/core.ts @@ -2,6 +2,7 @@ import 'core-js/full/array/from-async'; import { Context, Middleware } from 'koa'; import { HTTPError } from 'koajax'; +import { Content } from 'mobx-github'; import { DataObject } from 'mobx-restful'; import { KoaOption, withKoa } from 'next-ssr-middleware'; import Path from 'path'; @@ -123,3 +124,17 @@ export function* traverseTree>( yield* traverseTree(node as N, key); } } + +export const filterMarkdownFiles = (nodes: Content[]) => + nodes + .filter( + ({ path, type, name }) => + !path.startsWith('.') && + !name.startsWith('.') && + (type !== 'file' || MD_pattern.test(name)), + ) + .map(({ content, ...rest }) => { + const { meta, markdown } = content ? splitFrontMatter(content) : {}; + + return { ...rest, content: markdown, meta }; + }); diff --git a/pages/policy/[...slug].tsx b/pages/policy/[...slug].tsx index 59528bd..eec2d4e 100644 --- a/pages/policy/[...slug].tsx +++ b/pages/policy/[...slug].tsx @@ -1,9 +1,10 @@ import { marked } from 'marked'; import { observer } from 'mobx-react'; +import { BadgeBar } from 'mobx-restful-table'; import { GetStaticPaths, GetStaticProps } from 'next'; import { ParsedUrlQuery } from 'querystring'; import { FC, useContext } from 'react'; -import { Badge, Breadcrumb, Button, Container } from 'react-bootstrap'; +import { Breadcrumb, Button, Container } from 'react-bootstrap'; import { decodeBase64 } from 'web-utility'; import { PageHead } from '../../components/Layout/PageHead'; @@ -66,49 +67,29 @@ const WikiPage: FC = observer(({ name, path, parent_path, content, met

{name}

- {meta && ( -
-
    - {meta['主题分类'] && ( -
  • - {meta['主题分类']} -
  • - )} - {meta['发文机构'] && ( -
  • - {meta['发文机构']} -
  • - )} - {meta['有效性'] && ( -
  • - - {meta['有效性']} - -
  • - )} -
-
- )} + {meta && ({ text }))} />}
-
+
{meta?.['成文日期'] && ( - - {t('creation_date')}: {meta['成文日期']} - + <> +
{t('creation_date')}:
+
{meta['成文日期']}
+ )} {meta?.['发布日期'] && meta['发布日期'] !== meta['成文日期'] && ( - - {t('publication_date')}: {meta['发布日期']} - + <> +
{t('publication_date')}:
+
{meta['发布日期']}
+ )} -
+
+ {meta?.url && ( + + )} +
+
+
+ +
+ + + + + ); +}); + +export default RecipePage; diff --git a/pages/recipe/index.tsx b/pages/recipe/index.tsx new file mode 100644 index 0000000..7bdf978 --- /dev/null +++ b/pages/recipe/index.tsx @@ -0,0 +1,73 @@ +import { observer } from 'mobx-react'; +import { GetStaticProps } from 'next'; +import React, { FC, useContext } from 'react'; +import { Alert, Button, Card, Container } from 'react-bootstrap'; +import { treeFrom } from 'web-utility'; + +import { ContentTree } from '../../components/Layout/ContentTree'; +import { PageHead } from '../../components/Layout/PageHead'; +import { I18nContext } from '../../models/Translation'; +import { recipeContentStore, XContent } from '../../models/Wiki'; +import { filterMarkdownFiles } from '../api/core'; + +export const getStaticProps: GetStaticProps<{ nodes: XContent[] }> = async () => { + const nodes = filterMarkdownFiles(await recipeContentStore.getAll()); + + return { + props: JSON.parse(JSON.stringify({ nodes })), + revalidate: 300, // Revalidate every 5 minutes + }; +}; + +const RecipeIndexPage: FC<{ nodes: XContent[] }> = observer(({ nodes }) => { + const { t } = useContext(I18nContext); + + return ( + + + +
+

+ {t('recipe')} ({nodes.length}) +

+ +
+ + + 本菜谱原创自 + + 《老乡鸡菜品溯源报告》 + + ,并由{' '} + + CookLikeHOC 开源菜谱项目 + + 整理,感谢原作者们的贡献与分享。 + + + {nodes[0] ? ( + + ) : ( + + +

{t('no_docs_available')}

+

{t('docs_auto_load_from_github')}

+
+
+ )} +
+ ); +}); + +export default RecipeIndexPage; diff --git a/translation/en-US.ts b/translation/en-US.ts index c168b10..846ea9e 100644 --- a/translation/en-US.ts +++ b/translation/en-US.ts @@ -102,6 +102,11 @@ export default { github_document_description: 'This is a document page based on a GitHub repository.', view_or_edit_on_github: 'View or edit this content on GitHub', + // Recipe + recipe: 'Recipe', + servings: 'Servings', + preparation_time: 'Preparation time', + // China NGO Map NGO: 'NGO', China_NGO_DB: 'China NGO Database', diff --git a/translation/zh-CN.ts b/translation/zh-CN.ts index 79323d9..9885bc1 100644 --- a/translation/zh-CN.ts +++ b/translation/zh-CN.ts @@ -100,6 +100,11 @@ export default { github_document_description: '这是一个基于 GitHub 仓库的文档页面。', view_or_edit_on_github: '在 GitHub 上查看或编辑此内容', + // Recipe + recipe: '菜谱', + servings: '份数', + preparation_time: '准备时间', + // China Public Interest Map NGO: '公益', China_NGO_DB: '中国公益数据库', diff --git a/translation/zh-TW.ts b/translation/zh-TW.ts index f1a0612..073657d 100644 --- a/translation/zh-TW.ts +++ b/translation/zh-TW.ts @@ -100,6 +100,11 @@ export default { github_document_description: '這是一個基於 GitHub 存儲庫的文檔頁面。', view_or_edit_on_github: '在 GitHub 上查看或編輯此內容', + // Recipe + recipe: '菜譜', + servings: '份數', + preparation_time: '準備時間', + // China Public Interest Map NGO: '公益', China_NGO_DB: '中國公益數據庫',