@terrafirmagreg-team/discussions 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -5
- package/src/components/comments-view.tsx +32 -32
- package/src/components/comments.tsx +8 -8
- package/src/components/feedback/block.tsx +366 -366
- package/src/components/feedback/page.tsx +181 -181
- package/src/hooks/comments/use-menu-item.ts +29 -29
- package/src/hooks/comments/use-session.ts +21 -21
- package/src/lib/comments/server.ts +204 -204
- package/src/lib/feedback.ts +74 -74
- package/src/locales/en_us.json +0 -13
- package/src/locales/ru_ru.json +0 -13
- package/src/locales.ts +0 -6
|
@@ -1,204 +1,204 @@
|
|
|
1
|
-
import 'server-only';
|
|
2
|
-
import { NextComment } from '@fuma-comment/server/next';
|
|
3
|
-
type RouteRequest = Request & { nextUrl: { pathname: string } };
|
|
4
|
-
import {
|
|
5
|
-
authConfig,
|
|
6
|
-
authCookie,
|
|
7
|
-
authEnabled,
|
|
8
|
-
authMode,
|
|
9
|
-
devAuthCookie,
|
|
10
|
-
devCookieAuth,
|
|
11
|
-
} from '@terrafirmagreg-team/auth';
|
|
12
|
-
import { env, githubAppToken } from '@terrafirmagreg-team/data';
|
|
13
|
-
import {
|
|
14
|
-
createAuth,
|
|
15
|
-
createGitHubClient,
|
|
16
|
-
createStorage,
|
|
17
|
-
type GitHubDiscussionsConfig,
|
|
18
|
-
} from 'fuma-comment-github-discussions';
|
|
19
|
-
import { discussionsConfig } from '../config';
|
|
20
|
-
import { createDiscussion } from './github';
|
|
21
|
-
import { localComments } from './local';
|
|
22
|
-
|
|
23
|
-
const APP_TOKEN = 'app';
|
|
24
|
-
const CATEGORY = 'General';
|
|
25
|
-
|
|
26
|
-
type DiscussionTarget = {
|
|
27
|
-
dataset: string;
|
|
28
|
-
repo: string;
|
|
29
|
-
repoId: string;
|
|
30
|
-
categoryId: string;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
function datasets(): DiscussionTarget[] {
|
|
34
|
-
return Object.entries(discussionsConfig().datasets).flatMap(
|
|
35
|
-
([dataset, cfg]) =>
|
|
36
|
-
cfg.repo && cfg.discussions
|
|
37
|
-
? [
|
|
38
|
-
{
|
|
39
|
-
dataset,
|
|
40
|
-
repo: cfg.repo,
|
|
41
|
-
repoId: cfg.discussions.repoId,
|
|
42
|
-
categoryId: cfg.discussions.categoryId,
|
|
43
|
-
},
|
|
44
|
-
]
|
|
45
|
-
: [],
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function datasetSegs(): Set<string> {
|
|
50
|
-
return new Set(datasets().map(({ dataset }) => dataset));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function defaultDataset(): DiscussionTarget {
|
|
54
|
-
return datasets().find(({ dataset }) => dataset === 'dev')!;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function datasetOf(page: string) {
|
|
58
|
-
const segs = datasetSegs();
|
|
59
|
-
const seg = page.split('~').find((part) => segs.has(part));
|
|
60
|
-
return datasets().find(({ dataset }) => dataset === seg) ?? defaultDataset();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function pageToTitle(page: string): string {
|
|
64
|
-
const locales = new Set(discussionsConfig().knownLanguages);
|
|
65
|
-
const segs = datasetSegs();
|
|
66
|
-
const pretty = page
|
|
67
|
-
.split('~')
|
|
68
|
-
.slice(0, -1)
|
|
69
|
-
.filter((seg) => seg && !locales.has(seg) && !segs.has(seg))
|
|
70
|
-
.map((seg) =>
|
|
71
|
-
seg.replace(/[-_]+/g, ' ').replace(/\b\w/g, (ch) => ch.toUpperCase()),
|
|
72
|
-
);
|
|
73
|
-
return pretty.join(' › ') || 'Page';
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
type Client = ReturnType<typeof createGitHubClient>;
|
|
77
|
-
type Storage = ReturnType<typeof createStorage>;
|
|
78
|
-
|
|
79
|
-
let siteUrl: string | undefined;
|
|
80
|
-
|
|
81
|
-
function currentSiteUrl(): string {
|
|
82
|
-
return siteUrl ?? discussionsConfig().siteUrl;
|
|
83
|
-
}
|
|
84
|
-
const storages = new Map<string, Storage>();
|
|
85
|
-
|
|
86
|
-
function cfgFor(d: DiscussionTarget): GitHubDiscussionsConfig {
|
|
87
|
-
return {
|
|
88
|
-
repo: d.repo,
|
|
89
|
-
repoId: d.repoId,
|
|
90
|
-
categoryId: d.categoryId,
|
|
91
|
-
category: CATEGORY,
|
|
92
|
-
readToken: APP_TOKEN,
|
|
93
|
-
tokenSecret: authConfig().tokenSecret,
|
|
94
|
-
cookieName: authCookie,
|
|
95
|
-
pageToTitle,
|
|
96
|
-
pageToUrl: (page) => {
|
|
97
|
-
const segs = page.split('~');
|
|
98
|
-
segs.pop();
|
|
99
|
-
return `${currentSiteUrl()}/${segs.filter(Boolean).join('/')}`;
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function wrapClient(d: DiscussionTarget, cfg: GitHubDiscussionsConfig): Client {
|
|
105
|
-
const base = createGitHubClient(cfg);
|
|
106
|
-
const wrapped = { ...base };
|
|
107
|
-
for (const key of Object.keys(base) as (keyof Client)[]) {
|
|
108
|
-
const fn = base[key] as (...args: unknown[]) => unknown;
|
|
109
|
-
(wrapped as Record<string, unknown>)[key] = async (...args: unknown[]) => {
|
|
110
|
-
if (args.includes(APP_TOKEN)) {
|
|
111
|
-
const token = await githubAppToken(d.repo);
|
|
112
|
-
args = args.map((arg) => (arg === APP_TOKEN ? token : arg));
|
|
113
|
-
}
|
|
114
|
-
return fn(...args);
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
wrapped.createDiscussion = async (title, url, token) => {
|
|
118
|
-
const resolved = token === APP_TOKEN ? await githubAppToken(d.repo) : token;
|
|
119
|
-
return createDiscussion(cfg, title, url, resolved);
|
|
120
|
-
};
|
|
121
|
-
return wrapped;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function storageFor(d: DiscussionTarget): Storage {
|
|
125
|
-
let storage = storages.get(d.dataset);
|
|
126
|
-
if (!storage) {
|
|
127
|
-
const cfg = cfgFor(d);
|
|
128
|
-
storage = createStorage(cfg, wrapClient(d, cfg));
|
|
129
|
-
storages.set(d.dataset, storage);
|
|
130
|
-
}
|
|
131
|
-
return storage;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
type RouteHandler = (
|
|
135
|
-
req: RouteRequest,
|
|
136
|
-
context: { params: Promise<{ comment: string[] }> },
|
|
137
|
-
) => Promise<Response>;
|
|
138
|
-
|
|
139
|
-
export function commentsEnabled(): boolean {
|
|
140
|
-
return authEnabled() && datasets().length > 0;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function feedbackEnabled(): boolean {
|
|
144
|
-
return commentsEnabled() && Boolean(env('APP_ID') && env('APP_PRIVATE_KEY'));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function createCommentsRouter(
|
|
148
|
-
options: { siteUrl?: string } = {},
|
|
149
|
-
):
|
|
150
|
-
| { GET: RouteHandler; POST: RouteHandler; PATCH: RouteHandler; DELETE: RouteHandler }
|
|
151
|
-
| undefined {
|
|
152
|
-
if (!commentsEnabled()) return undefined;
|
|
153
|
-
siteUrl = options.siteUrl ?? discussionsConfig().siteUrl;
|
|
154
|
-
|
|
155
|
-
if (authMode() === 'dev' || !(env('APP_ID') && env('APP_PRIVATE_KEY')))
|
|
156
|
-
return NextComment({
|
|
157
|
-
role: 'database',
|
|
158
|
-
mention: { enabled: true },
|
|
159
|
-
storage: localComments(),
|
|
160
|
-
auth: devCookieAuth({ cookieName: devAuthCookie }),
|
|
161
|
-
}) as unknown as { GET: RouteHandler; POST: RouteHandler; PATCH: RouteHandler; DELETE: RouteHandler };
|
|
162
|
-
|
|
163
|
-
const dispatch = (page: string | undefined) =>
|
|
164
|
-
storageFor(datasetOf(page ?? ''));
|
|
165
|
-
|
|
166
|
-
const storage = {
|
|
167
|
-
getComments: (args: Parameters<Storage['getComments']>[0]) =>
|
|
168
|
-
dispatch(args.page as string | undefined).getComments(args),
|
|
169
|
-
postComment: (args: Parameters<Storage['postComment']>[0]) =>
|
|
170
|
-
dispatch(args.page as string | undefined).postComment(args),
|
|
171
|
-
updateComment: (args: Parameters<Storage['updateComment']>[0]) =>
|
|
172
|
-
dispatch(args.page).updateComment(args),
|
|
173
|
-
deleteComment: (args: Parameters<Storage['deleteComment']>[0]) =>
|
|
174
|
-
dispatch(args.page).deleteComment(args),
|
|
175
|
-
setRate: (args: Parameters<Storage['setRate']>[0]) =>
|
|
176
|
-
dispatch(args.page).setRate(args),
|
|
177
|
-
deleteRate: (args: Parameters<Storage['deleteRate']>[0]) =>
|
|
178
|
-
dispatch(args.page).deleteRate(args),
|
|
179
|
-
getCommentAuthor: async (
|
|
180
|
-
args: Parameters<Storage['getCommentAuthor']>[0],
|
|
181
|
-
) => {
|
|
182
|
-
for (const d of datasets()) {
|
|
183
|
-
const author = await Promise.resolve(
|
|
184
|
-
storageFor(d).getCommentAuthor(args),
|
|
185
|
-
).catch(() => null);
|
|
186
|
-
if (author) return author;
|
|
187
|
-
}
|
|
188
|
-
return null;
|
|
189
|
-
},
|
|
190
|
-
getRole: (args: Parameters<Storage['getRole']>[0]) =>
|
|
191
|
-
storageFor(datasetOf(args.page)).getRole(args),
|
|
192
|
-
queryUsers: (args: Parameters<NonNullable<Storage['queryUsers']>>[0]) =>
|
|
193
|
-
dispatch(args.page).queryUsers?.(args) ?? [],
|
|
194
|
-
} as Storage;
|
|
195
|
-
|
|
196
|
-
const cfg = cfgFor(defaultDataset());
|
|
197
|
-
return NextComment({
|
|
198
|
-
role: 'database',
|
|
199
|
-
mention: { enabled: true },
|
|
200
|
-
storage,
|
|
201
|
-
auth: createAuth(cfg, wrapClient(defaultDataset(), cfg)),
|
|
202
|
-
}) as unknown as { GET: RouteHandler; POST: RouteHandler; PATCH: RouteHandler; DELETE: RouteHandler };
|
|
203
|
-
}
|
|
204
|
-
|
|
1
|
+
import 'server-only';
|
|
2
|
+
import { NextComment } from '@fuma-comment/server/next';
|
|
3
|
+
type RouteRequest = Request & { nextUrl: { pathname: string } };
|
|
4
|
+
import {
|
|
5
|
+
authConfig,
|
|
6
|
+
authCookie,
|
|
7
|
+
authEnabled,
|
|
8
|
+
authMode,
|
|
9
|
+
devAuthCookie,
|
|
10
|
+
devCookieAuth,
|
|
11
|
+
} from '@terrafirmagreg-team/auth';
|
|
12
|
+
import { env, githubAppToken } from '@terrafirmagreg-team/data';
|
|
13
|
+
import {
|
|
14
|
+
createAuth,
|
|
15
|
+
createGitHubClient,
|
|
16
|
+
createStorage,
|
|
17
|
+
type GitHubDiscussionsConfig,
|
|
18
|
+
} from 'fuma-comment-github-discussions';
|
|
19
|
+
import { discussionsConfig } from '../config';
|
|
20
|
+
import { createDiscussion } from './github';
|
|
21
|
+
import { localComments } from './local';
|
|
22
|
+
|
|
23
|
+
const APP_TOKEN = 'app';
|
|
24
|
+
const CATEGORY = 'General';
|
|
25
|
+
|
|
26
|
+
type DiscussionTarget = {
|
|
27
|
+
dataset: string;
|
|
28
|
+
repo: string;
|
|
29
|
+
repoId: string;
|
|
30
|
+
categoryId: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function datasets(): DiscussionTarget[] {
|
|
34
|
+
return Object.entries(discussionsConfig().datasets).flatMap(
|
|
35
|
+
([dataset, cfg]) =>
|
|
36
|
+
cfg.repo && cfg.discussions
|
|
37
|
+
? [
|
|
38
|
+
{
|
|
39
|
+
dataset,
|
|
40
|
+
repo: cfg.repo,
|
|
41
|
+
repoId: cfg.discussions.repoId,
|
|
42
|
+
categoryId: cfg.discussions.categoryId,
|
|
43
|
+
},
|
|
44
|
+
]
|
|
45
|
+
: [],
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function datasetSegs(): Set<string> {
|
|
50
|
+
return new Set(datasets().map(({ dataset }) => dataset));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function defaultDataset(): DiscussionTarget {
|
|
54
|
+
return datasets().find(({ dataset }) => dataset === 'dev')!;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function datasetOf(page: string) {
|
|
58
|
+
const segs = datasetSegs();
|
|
59
|
+
const seg = page.split('~').find((part) => segs.has(part));
|
|
60
|
+
return datasets().find(({ dataset }) => dataset === seg) ?? defaultDataset();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function pageToTitle(page: string): string {
|
|
64
|
+
const locales = new Set(discussionsConfig().knownLanguages);
|
|
65
|
+
const segs = datasetSegs();
|
|
66
|
+
const pretty = page
|
|
67
|
+
.split('~')
|
|
68
|
+
.slice(0, -1)
|
|
69
|
+
.filter((seg) => seg && !locales.has(seg) && !segs.has(seg))
|
|
70
|
+
.map((seg) =>
|
|
71
|
+
seg.replace(/[-_]+/g, ' ').replace(/\b\w/g, (ch) => ch.toUpperCase()),
|
|
72
|
+
);
|
|
73
|
+
return pretty.join(' › ') || 'Page';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
type Client = ReturnType<typeof createGitHubClient>;
|
|
77
|
+
type Storage = ReturnType<typeof createStorage>;
|
|
78
|
+
|
|
79
|
+
let siteUrl: string | undefined;
|
|
80
|
+
|
|
81
|
+
function currentSiteUrl(): string {
|
|
82
|
+
return siteUrl ?? discussionsConfig().siteUrl;
|
|
83
|
+
}
|
|
84
|
+
const storages = new Map<string, Storage>();
|
|
85
|
+
|
|
86
|
+
function cfgFor(d: DiscussionTarget): GitHubDiscussionsConfig {
|
|
87
|
+
return {
|
|
88
|
+
repo: d.repo,
|
|
89
|
+
repoId: d.repoId,
|
|
90
|
+
categoryId: d.categoryId,
|
|
91
|
+
category: CATEGORY,
|
|
92
|
+
readToken: APP_TOKEN,
|
|
93
|
+
tokenSecret: authConfig().tokenSecret,
|
|
94
|
+
cookieName: authCookie,
|
|
95
|
+
pageToTitle,
|
|
96
|
+
pageToUrl: (page) => {
|
|
97
|
+
const segs = page.split('~');
|
|
98
|
+
segs.pop();
|
|
99
|
+
return `${currentSiteUrl()}/${segs.filter(Boolean).join('/')}`;
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function wrapClient(d: DiscussionTarget, cfg: GitHubDiscussionsConfig): Client {
|
|
105
|
+
const base = createGitHubClient(cfg);
|
|
106
|
+
const wrapped = { ...base };
|
|
107
|
+
for (const key of Object.keys(base) as (keyof Client)[]) {
|
|
108
|
+
const fn = base[key] as (...args: unknown[]) => unknown;
|
|
109
|
+
(wrapped as Record<string, unknown>)[key] = async (...args: unknown[]) => {
|
|
110
|
+
if (args.includes(APP_TOKEN)) {
|
|
111
|
+
const token = await githubAppToken(d.repo);
|
|
112
|
+
args = args.map((arg) => (arg === APP_TOKEN ? token : arg));
|
|
113
|
+
}
|
|
114
|
+
return fn(...args);
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
wrapped.createDiscussion = async (title, url, token) => {
|
|
118
|
+
const resolved = token === APP_TOKEN ? await githubAppToken(d.repo) : token;
|
|
119
|
+
return createDiscussion(cfg, title, url, resolved);
|
|
120
|
+
};
|
|
121
|
+
return wrapped;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function storageFor(d: DiscussionTarget): Storage {
|
|
125
|
+
let storage = storages.get(d.dataset);
|
|
126
|
+
if (!storage) {
|
|
127
|
+
const cfg = cfgFor(d);
|
|
128
|
+
storage = createStorage(cfg, wrapClient(d, cfg));
|
|
129
|
+
storages.set(d.dataset, storage);
|
|
130
|
+
}
|
|
131
|
+
return storage;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
type RouteHandler = (
|
|
135
|
+
req: RouteRequest,
|
|
136
|
+
context: { params: Promise<{ comment: string[] }> },
|
|
137
|
+
) => Promise<Response>;
|
|
138
|
+
|
|
139
|
+
export function commentsEnabled(): boolean {
|
|
140
|
+
return authEnabled() && datasets().length > 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function feedbackEnabled(): boolean {
|
|
144
|
+
return commentsEnabled() && Boolean(env('APP_ID') && env('APP_PRIVATE_KEY'));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function createCommentsRouter(
|
|
148
|
+
options: { siteUrl?: string } = {},
|
|
149
|
+
):
|
|
150
|
+
| { GET: RouteHandler; POST: RouteHandler; PATCH: RouteHandler; DELETE: RouteHandler }
|
|
151
|
+
| undefined {
|
|
152
|
+
if (!commentsEnabled()) return undefined;
|
|
153
|
+
siteUrl = options.siteUrl ?? discussionsConfig().siteUrl;
|
|
154
|
+
|
|
155
|
+
if (authMode() === 'dev' || !(env('APP_ID') && env('APP_PRIVATE_KEY')))
|
|
156
|
+
return NextComment({
|
|
157
|
+
role: 'database',
|
|
158
|
+
mention: { enabled: true },
|
|
159
|
+
storage: localComments(),
|
|
160
|
+
auth: devCookieAuth({ cookieName: devAuthCookie }),
|
|
161
|
+
}) as unknown as { GET: RouteHandler; POST: RouteHandler; PATCH: RouteHandler; DELETE: RouteHandler };
|
|
162
|
+
|
|
163
|
+
const dispatch = (page: string | undefined) =>
|
|
164
|
+
storageFor(datasetOf(page ?? ''));
|
|
165
|
+
|
|
166
|
+
const storage = {
|
|
167
|
+
getComments: (args: Parameters<Storage['getComments']>[0]) =>
|
|
168
|
+
dispatch(args.page as string | undefined).getComments(args),
|
|
169
|
+
postComment: (args: Parameters<Storage['postComment']>[0]) =>
|
|
170
|
+
dispatch(args.page as string | undefined).postComment(args),
|
|
171
|
+
updateComment: (args: Parameters<Storage['updateComment']>[0]) =>
|
|
172
|
+
dispatch(args.page).updateComment(args),
|
|
173
|
+
deleteComment: (args: Parameters<Storage['deleteComment']>[0]) =>
|
|
174
|
+
dispatch(args.page).deleteComment(args),
|
|
175
|
+
setRate: (args: Parameters<Storage['setRate']>[0]) =>
|
|
176
|
+
dispatch(args.page).setRate(args),
|
|
177
|
+
deleteRate: (args: Parameters<Storage['deleteRate']>[0]) =>
|
|
178
|
+
dispatch(args.page).deleteRate(args),
|
|
179
|
+
getCommentAuthor: async (
|
|
180
|
+
args: Parameters<Storage['getCommentAuthor']>[0],
|
|
181
|
+
) => {
|
|
182
|
+
for (const d of datasets()) {
|
|
183
|
+
const author = await Promise.resolve(
|
|
184
|
+
storageFor(d).getCommentAuthor(args),
|
|
185
|
+
).catch(() => null);
|
|
186
|
+
if (author) return author;
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
},
|
|
190
|
+
getRole: (args: Parameters<Storage['getRole']>[0]) =>
|
|
191
|
+
storageFor(datasetOf(args.page)).getRole(args),
|
|
192
|
+
queryUsers: (args: Parameters<NonNullable<Storage['queryUsers']>>[0]) =>
|
|
193
|
+
dispatch(args.page).queryUsers?.(args) ?? [],
|
|
194
|
+
} as Storage;
|
|
195
|
+
|
|
196
|
+
const cfg = cfgFor(defaultDataset());
|
|
197
|
+
return NextComment({
|
|
198
|
+
role: 'database',
|
|
199
|
+
mention: { enabled: true },
|
|
200
|
+
storage,
|
|
201
|
+
auth: createAuth(cfg, wrapClient(defaultDataset(), cfg)),
|
|
202
|
+
}) as unknown as { GET: RouteHandler; POST: RouteHandler; PATCH: RouteHandler; DELETE: RouteHandler };
|
|
203
|
+
}
|
|
204
|
+
|
package/src/lib/feedback.ts
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
'use server';
|
|
2
|
-
import { githubAppToken } from '@terrafirmagreg-team/data';
|
|
3
|
-
import { createGitHubClient } from 'fuma-comment-github-discussions';
|
|
4
|
-
import { discussionsConfig } from './config';
|
|
5
|
-
import { createDiscussion } from './comments/github';
|
|
6
|
-
import {
|
|
7
|
-
blockFeedback,
|
|
8
|
-
pageFeedback,
|
|
9
|
-
type ActionResponse,
|
|
10
|
-
type BlockFeedback,
|
|
11
|
-
type PageFeedback,
|
|
12
|
-
} from './feedback-schema';
|
|
13
|
-
|
|
14
|
-
function targetOf(pathname: string) {
|
|
15
|
-
const datasets = discussionsConfig().datasets;
|
|
16
|
-
const seg = pathname.split('/').find((part) => part in datasets);
|
|
17
|
-
const cfg = (seg && datasets[seg]) || datasets.dev;
|
|
18
|
-
if (!cfg?.repo || !cfg.discussions) return undefined;
|
|
19
|
-
return {
|
|
20
|
-
repo: cfg.repo,
|
|
21
|
-
repoId: cfg.discussions.repoId,
|
|
22
|
-
categoryId: cfg.discussions.categoryId,
|
|
23
|
-
category: 'General',
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function onPageFeedbackAction(
|
|
28
|
-
feedback: PageFeedback,
|
|
29
|
-
): Promise<ActionResponse> {
|
|
30
|
-
feedback = pageFeedback.parse(feedback);
|
|
31
|
-
const { pathname } = new URL(feedback.url);
|
|
32
|
-
|
|
33
|
-
return createDiscussionThread(
|
|
34
|
-
pathname,
|
|
35
|
-
`[${feedback.opinion}] ${feedback.message}\n\n> Forwarded from user feedback.`,
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export async function onBlockFeedbackAction(
|
|
40
|
-
feedback: BlockFeedback,
|
|
41
|
-
): Promise<ActionResponse> {
|
|
42
|
-
feedback = blockFeedback.parse(feedback);
|
|
43
|
-
const url = new URL(feedback.url);
|
|
44
|
-
url.hash = feedback.blockId;
|
|
45
|
-
|
|
46
|
-
return createDiscussionThread(
|
|
47
|
-
url.pathname,
|
|
48
|
-
`> ${feedback.blockBody}\n\n${feedback.message}\n\n> [Forwarded from user feedback](${url.href}).`,
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async function createDiscussionThread(
|
|
53
|
-
pageId: string,
|
|
54
|
-
body: string,
|
|
55
|
-
): Promise<ActionResponse> {
|
|
56
|
-
const target = targetOf(pageId);
|
|
57
|
-
if (!target) return { githubUrl: '' };
|
|
58
|
-
const github = createGitHubClient(target);
|
|
59
|
-
const auth = await githubAppToken(target.repo);
|
|
60
|
-
const title = `Feedback for ${pageId}`;
|
|
61
|
-
|
|
62
|
-
const discussion =
|
|
63
|
-
(await github.findDiscussion(title, auth)) ??
|
|
64
|
-
(await createDiscussion(
|
|
65
|
-
target,
|
|
66
|
-
title,
|
|
67
|
-
`${discussionsConfig().siteUrl}${pageId}`,
|
|
68
|
-
auth,
|
|
69
|
-
));
|
|
70
|
-
await github.addComment(discussion.id, body, auth);
|
|
71
|
-
|
|
72
|
-
return { githubUrl: discussion.url };
|
|
73
|
-
}
|
|
74
|
-
|
|
1
|
+
'use server';
|
|
2
|
+
import { githubAppToken } from '@terrafirmagreg-team/data';
|
|
3
|
+
import { createGitHubClient } from 'fuma-comment-github-discussions';
|
|
4
|
+
import { discussionsConfig } from './config';
|
|
5
|
+
import { createDiscussion } from './comments/github';
|
|
6
|
+
import {
|
|
7
|
+
blockFeedback,
|
|
8
|
+
pageFeedback,
|
|
9
|
+
type ActionResponse,
|
|
10
|
+
type BlockFeedback,
|
|
11
|
+
type PageFeedback,
|
|
12
|
+
} from './feedback-schema';
|
|
13
|
+
|
|
14
|
+
function targetOf(pathname: string) {
|
|
15
|
+
const datasets = discussionsConfig().datasets;
|
|
16
|
+
const seg = pathname.split('/').find((part) => part in datasets);
|
|
17
|
+
const cfg = (seg && datasets[seg]) || datasets.dev;
|
|
18
|
+
if (!cfg?.repo || !cfg.discussions) return undefined;
|
|
19
|
+
return {
|
|
20
|
+
repo: cfg.repo,
|
|
21
|
+
repoId: cfg.discussions.repoId,
|
|
22
|
+
categoryId: cfg.discussions.categoryId,
|
|
23
|
+
category: 'General',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function onPageFeedbackAction(
|
|
28
|
+
feedback: PageFeedback,
|
|
29
|
+
): Promise<ActionResponse> {
|
|
30
|
+
feedback = pageFeedback.parse(feedback);
|
|
31
|
+
const { pathname } = new URL(feedback.url);
|
|
32
|
+
|
|
33
|
+
return createDiscussionThread(
|
|
34
|
+
pathname,
|
|
35
|
+
`[${feedback.opinion}] ${feedback.message}\n\n> Forwarded from user feedback.`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function onBlockFeedbackAction(
|
|
40
|
+
feedback: BlockFeedback,
|
|
41
|
+
): Promise<ActionResponse> {
|
|
42
|
+
feedback = blockFeedback.parse(feedback);
|
|
43
|
+
const url = new URL(feedback.url);
|
|
44
|
+
url.hash = feedback.blockId;
|
|
45
|
+
|
|
46
|
+
return createDiscussionThread(
|
|
47
|
+
url.pathname,
|
|
48
|
+
`> ${feedback.blockBody}\n\n${feedback.message}\n\n> [Forwarded from user feedback](${url.href}).`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function createDiscussionThread(
|
|
53
|
+
pageId: string,
|
|
54
|
+
body: string,
|
|
55
|
+
): Promise<ActionResponse> {
|
|
56
|
+
const target = targetOf(pageId);
|
|
57
|
+
if (!target) return { githubUrl: '' };
|
|
58
|
+
const github = createGitHubClient(target);
|
|
59
|
+
const auth = await githubAppToken(target.repo);
|
|
60
|
+
const title = `Feedback for ${pageId}`;
|
|
61
|
+
|
|
62
|
+
const discussion =
|
|
63
|
+
(await github.findDiscussion(title, auth)) ??
|
|
64
|
+
(await createDiscussion(
|
|
65
|
+
target,
|
|
66
|
+
title,
|
|
67
|
+
`${discussionsConfig().siteUrl}${pageId}`,
|
|
68
|
+
auth,
|
|
69
|
+
));
|
|
70
|
+
await github.addComment(discussion.id, body, auth);
|
|
71
|
+
|
|
72
|
+
return { githubUrl: discussion.url };
|
|
73
|
+
}
|
|
74
|
+
|
package/src/locales/en_us.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"Bad(ui.feedback.bad)": "Bad",
|
|
3
|
-
"Close(ui.feedback.close)": "Close",
|
|
4
|
-
"Context(ui.feedback.context)": "Context",
|
|
5
|
-
"Feedback(ui.feedback.tooltip)": "Feedback",
|
|
6
|
-
"Good(ui.feedback.good)": "Good",
|
|
7
|
-
"How is this page?(ui.feedback.question)": "How is this page?",
|
|
8
|
-
"Leave your feedback…(ui.feedback.placeholder)": "Leave your feedback…",
|
|
9
|
-
"Submit Again(ui.feedback.submit_again)": "Submit Again",
|
|
10
|
-
"Submit(ui.feedback.submit)": "Submit",
|
|
11
|
-
"Thank you for your feedback!(ui.feedback.thanks)": "Thank you for your feedback!",
|
|
12
|
-
"View on GitHub(ui.feedback.view_on_github)": "View on GitHub"
|
|
13
|
-
}
|
package/src/locales/ru_ru.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"Bad(ui.feedback.bad)": "Плохо",
|
|
3
|
-
"Close(ui.feedback.close)": "Закрыть",
|
|
4
|
-
"Context(ui.feedback.context)": "Контекст",
|
|
5
|
-
"Feedback(ui.feedback.tooltip)": "Отзыв",
|
|
6
|
-
"Good(ui.feedback.good)": "Хорошо",
|
|
7
|
-
"How is this page?(ui.feedback.question)": "Как вам эта страница?",
|
|
8
|
-
"Leave your feedback…(ui.feedback.placeholder)": "Оставьте отзыв…",
|
|
9
|
-
"Submit Again(ui.feedback.submit_again)": "Отправить снова",
|
|
10
|
-
"Submit(ui.feedback.submit)": "Отправить",
|
|
11
|
-
"Thank you for your feedback!(ui.feedback.thanks)": "Спасибо за отзыв!",
|
|
12
|
-
"View on GitHub(ui.feedback.view_on_github)": "Смотреть на GitHub"
|
|
13
|
-
}
|