Skip to content

Commit d8b3ead

Browse files
CopilotTechQuery
andcommitted
Changes before error encountered
Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com>
1 parent 0673c86 commit d8b3ead

7 files changed

Lines changed: 409 additions & 71 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
/node_modules
55
/.pnp
66
.pnp.js
7+
package-lock.json
78

89
# testing
910
/coverage

components/Git/Issue/Card.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export const IssueCard: FC<IssueCardProps> = ({
5858
</Card.Header>
5959
<Card.Body
6060
as="article"
61-
dangerouslySetInnerHTML={{ __html: marked(body || '') }}
61+
dangerouslySetInnerHTML={{ __html: marked(body || '') as string }}
6262
/>
6363
<Card.Footer className="d-flex justify-content-between align-items-center">
6464
{user && <Nameplate name={user.name || ''} avatar={user.avatar_url} />}

models/Wiki.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { Issue, RepositoryModel } from 'mobx-github';
2+
import { Filter, ListModel } from 'mobx-restful';
3+
import { buildURLData } from 'web-utility';
4+
5+
import { githubClient, GithubSearchData, makeGithubSearchCondition } from './Base';
6+
7+
export interface WikiNode {
8+
id: number;
9+
title: string;
10+
body: string;
11+
html_url: string;
12+
created_at: string;
13+
updated_at: string;
14+
labels: string[];
15+
number: number;
16+
repository?: {
17+
name: string;
18+
full_name: string;
19+
};
20+
}
21+
22+
export type WikiFilter = Filter<WikiNode>;
23+
24+
export class WikiModel extends ListModel<WikiNode, WikiFilter> {
25+
baseURI = 'search/issues';
26+
client = githubClient;
27+
28+
async loadPage(
29+
page = this.pageIndex,
30+
per_page = this.pageSize,
31+
filter: WikiFilter = {},
32+
) {
33+
// Search for issues labeled as 'wiki' or 'policy' in the Open-Source-Bazaar organization
34+
const searchQuery = [
35+
'org:Open-Source-Bazaar',
36+
'is:issue',
37+
'label:wiki OR label:policy',
38+
'state:open',
39+
].join(' ');
40+
41+
const { body } = await this.client.get<GithubSearchData<Issue>>(
42+
`${this.baseURI}?${buildURLData({
43+
page,
44+
per_page,
45+
q: searchQuery,
46+
sort: 'updated',
47+
order: 'desc'
48+
})}`,
49+
);
50+
51+
const items: WikiNode[] = body!.items.map(issue => ({
52+
id: issue.id,
53+
title: issue.title,
54+
body: issue.body || '',
55+
html_url: issue.html_url,
56+
created_at: issue.created_at,
57+
updated_at: issue.updated_at,
58+
labels: issue.labels.map(label => typeof label === 'string' ? label : label.name || ''),
59+
number: issue.number,
60+
repository: issue.repository ? {
61+
name: issue.repository.name,
62+
full_name: issue.repository.full_name,
63+
} : undefined,
64+
}));
65+
66+
return { pageData: items, totalCount: body!.total_count };
67+
}
68+
69+
async getAll(): Promise<WikiNode[]> {
70+
const { pageData } = await this.loadPage(1, 100);
71+
return pageData;
72+
}
73+
74+
async getOne(id: string | number): Promise<WikiNode> {
75+
const all = await this.getAll();
76+
const found = all.find(item => item.id.toString() === id.toString() || item.number.toString() === id.toString());
77+
if (!found) {
78+
throw new Error(`Wiki node with id ${id} not found`);
79+
}
80+
return found;
81+
}
82+
}
83+
84+
export const wikiStore = new WikiModel();

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"marked": "^16.2.0",
2626
"mime": "^4.0.7",
2727
"mobx": "^6.13.7",
28-
"mobx-github": "^0.3.11",
28+
"mobx-github": "^0.4.0-rc.1",
2929
"mobx-i18n": "^0.7.1",
3030
"mobx-lark": "^2.4.0",
3131
"mobx-react": "^9.2.0",

pages/wiki/[...slug].tsx

Lines changed: 126 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,150 @@
11
import { marked } from 'marked';
2-
import { DataObject } from 'mobx-restful';
3-
import { GetStaticPaths, GetStaticProps, NextPage } from 'next';
2+
import { GetStaticPaths, GetStaticProps } from 'next';
3+
import Link from 'next/link';
44
import { ParsedUrlQuery } from 'querystring';
5+
import { FC } from 'react';
6+
import { Badge, Container } from 'react-bootstrap';
57

6-
import { pageListOf, splitFrontMatter, traverseTree } from '../api/core';
8+
import { PageHead } from '../../components/Layout/PageHead';
9+
import { WikiNode, wikiStore } from '../../models/Wiki';
710

811
interface WikiPageParams extends ParsedUrlQuery {
912
slug: string[];
1013
}
1114

1215
export const getStaticPaths: GetStaticPaths<WikiPageParams> = async () => {
13-
const tree = await Array.fromAsync(pageListOf('wiki', 'public/'));
14-
const list = tree.map(root => [...traverseTree(root, 'subs')]).flat();
15-
const paths = list
16-
.map(({ path }) => path && { params: { slug: path.split('/') } })
17-
.filter(Boolean) as { params: WikiPageParams }[];
16+
try {
17+
const nodes = await wikiStore.getAll();
18+
const paths = nodes.map(({ id }) => ({
19+
params: { slug: [id.toString()] }
20+
}));
1821

19-
return { paths, fallback: 'blocking' };
22+
return { paths, fallback: 'blocking' };
23+
} catch (error) {
24+
console.error('Failed to generate static paths:', error);
25+
26+
return { paths: [], fallback: 'blocking' };
27+
}
2028
};
2129

2230
interface WikiPageProps {
23-
meta?: DataObject;
31+
node?: WikiNode;
2432
markup: string;
2533
}
2634

2735
export const getStaticProps: GetStaticProps<WikiPageProps, WikiPageParams> = async ({ params }) => {
2836
const { slug } = params!;
29-
// https://github.com/vercel/next.js/issues/12851
30-
if (slug[0] !== 'wiki') slug.unshift('wiki');
37+
const [nodeId] = slug;
38+
39+
try {
40+
const node = await wikiStore.getOne(nodeId);
41+
42+
if (!node) {
43+
return { notFound: true };
44+
}
45+
46+
const markup = marked(node.body || '') as string;
47+
48+
return {
49+
props: { node, markup },
50+
revalidate: 300 // Revalidate every 5 minutes
51+
};
52+
} catch (error) {
53+
console.error('Failed to load wiki node:', error);
54+
55+
return { notFound: true };
56+
}
57+
};
58+
59+
const WikiPage: FC<WikiPageProps> = ({ node, markup }) => {
60+
if (!node) {
61+
return (
62+
<Container className="py-4">
63+
<div className="text-center">
64+
<h2>Wiki 页面未找到</h2>
65+
<p>请检查页面链接是否正确。</p>
66+
<Link href="/wiki" className="btn btn-primary">
67+
返回 Wiki 首页
68+
</Link>
69+
</div>
70+
</Container>
71+
);
72+
}
73+
74+
return (
75+
<Container className="py-4">
76+
<PageHead title={node.title} />
77+
78+
<nav aria-label="breadcrumb" className="mb-4">
79+
<ol className="breadcrumb">
80+
<li className="breadcrumb-item">
81+
<Link href="/wiki">Wiki</Link>
82+
</li>
83+
<li className="breadcrumb-item active" aria-current="page">
84+
{node.title}
85+
</li>
86+
</ol>
87+
</nav>
88+
89+
<article>
90+
<header className="mb-4">
91+
<h1>{node.title}</h1>
92+
93+
<div className="d-flex flex-wrap align-items-center gap-3 mb-3">
94+
<div>
95+
{node.labels.map(label => (
96+
<Badge key={label} bg="secondary" className="me-1">
97+
{label}
98+
</Badge>
99+
))}
100+
</div>
101+
102+
{node.repository && (
103+
<small className="text-muted">
104+
来源: {node.repository.full_name}
105+
</small>
106+
)}
107+
</div>
31108

32-
const { meta, markdown } = await splitFrontMatter(`public/${slug.join('/')}.md`);
109+
<div className="d-flex justify-content-between align-items-center text-muted small">
110+
<div>
111+
创建于: {new Date(node.created_at).toLocaleDateString('zh-CN')}
112+
{node.updated_at !== node.created_at && (
113+
<> | 更新于: {new Date(node.updated_at).toLocaleDateString('zh-CN')}</>
114+
)}
115+
</div>
116+
117+
<div>
118+
<a
119+
className="btn btn-sm btn-outline-primary"
120+
href={node.html_url}
121+
rel="noopener noreferrer"
122+
target="_blank"
123+
>
124+
在 GitHub 编辑
125+
</a>
126+
</div>
127+
</div>
128+
</header>
33129

34-
const markup = marked(markdown) as string;
130+
<div
131+
dangerouslySetInnerHTML={{ __html: markup }}
132+
className="wiki-content"
133+
/>
134+
</article>
35135

36-
return { props: JSON.parse(JSON.stringify({ meta, markup })) };
136+
<footer className="mt-5 pt-4 border-top">
137+
<div className="text-center">
138+
<p className="text-muted">
139+
这是一个基于 GitHub Issues 的 Wiki 页面。
140+
<a href={node.html_url} target="_blank" rel="noopener noreferrer" className="ms-2">
141+
在 GitHub 上查看或编辑此内容
142+
</a>
143+
</p>
144+
</div>
145+
</footer>
146+
</Container>
147+
);
37148
};
38149

39-
const WikiPage: NextPage<WikiPageProps> = ({ meta, markup }) => (
40-
<>
41-
{meta && (
42-
<blockquote>
43-
<a target="_blank" href={meta.url} rel="noreferrer">
44-
{meta.url}
45-
</a>
46-
</blockquote>
47-
)}
48-
<article dangerouslySetInnerHTML={{ __html: markup }} />
49-
</>
50-
);
51150
export default WikiPage;

0 commit comments

Comments
 (0)