Skip to content

Commit bd1bd6e

Browse files
feat: adapt the details page for discussions
1 parent 56e3b42 commit bd1bd6e

File tree

5 files changed

+116
-19
lines changed

5 files changed

+116
-19
lines changed

src/lib/server/github-cache.ts

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,15 @@ export type Member = Awaited<
2323

2424
type OwnerKeyType = "members";
2525

26-
type RepoKeyType = "releases" | "descriptions" | "issue" | "issues" | "pr" | "prs" | "discussions";
26+
type RepoKeyType =
27+
| "releases"
28+
| "descriptions"
29+
| "issue"
30+
| "issues"
31+
| "pr"
32+
| "prs"
33+
| "discussion"
34+
| "discussions";
2735

2836
export type ItemDetails = {
2937
comments: Awaited<ReturnType<Issues["listComments"]>>["data"];
@@ -44,6 +52,11 @@ export type PullRequestDetails = ItemDetails & {
4452
linkedIssues: LinkedItem[];
4553
};
4654

55+
export type DiscussionDetails = {
56+
info: Discussion;
57+
comments: DiscussionComment[];
58+
};
59+
4760
type TeamDiscussion = Awaited<
4861
ReturnType<InstanceType<typeof Octokit>["rest"]["teams"]["listDiscussionsInOrg"]>
4962
>["data"][number];
@@ -280,14 +293,16 @@ export class GitHubCache {
280293
owner: string,
281294
repo: string,
282295
id: number,
283-
type: ExtractStrict<RepoKeyType, "issue" | "pr"> | undefined = undefined
296+
type: ExtractStrict<RepoKeyType, "issue" | "pr" | "discussions"> | undefined = undefined
284297
) {
285298
// Known type we assume the existence of
286299
switch (type) {
287300
case "issue":
288301
return await this.getIssueDetails(owner, repo, id);
289302
case "pr":
290303
return await this.getPullRequestDetails(owner, repo, id);
304+
case "discussions":
305+
return await this.getDiscussionDetails(owner, repo, id);
291306
}
292307

293308
// Unknown type, try to find or null otherwise
@@ -298,12 +313,18 @@ export class GitHubCache {
298313
}
299314

300315
try {
301-
// comes last because issues will also resolve for prs
316+
// doesn't come first because issues will also resolve for prs
302317
return await this.getIssueDetails(owner, repo, id);
303318
} catch (err: unknown) {
304319
console.error(`Error trying to get issue details for ${owner}/${repo}: ${err}`);
305320
}
306321

322+
try {
323+
return await this.getDiscussionDetails(owner, repo, id);
324+
} catch (err: unknown) {
325+
console.error(`Error trying to get discussion details for ${owner}/${repo}: ${err}`);
326+
}
327+
307328
return null;
308329
}
309330

@@ -365,6 +386,43 @@ export class GitHubCache {
365386
);
366387
}
367388

389+
/**
390+
* Get the discussion from the specified info.
391+
*
392+
* @param owner the GitHub repository owner
393+
* @param repo the GitHub repository name
394+
* @param id the discussion number
395+
* @returns the matching discussion
396+
* @throws Error if the discussion is not found
397+
*/
398+
async getDiscussionDetails(owner: string, repo: string, id: number) {
399+
return await this.#processCached<DiscussionDetails>()(
400+
this.#getRepoKey(owner, repo, "discussion", id),
401+
() =>
402+
Promise.all([
403+
this.#octokit.request("GET /repos/{owner}/{repo}/discussions/{number}", {
404+
owner,
405+
repo,
406+
number: id
407+
}),
408+
this.#octokit.paginate<DiscussionComment>(
409+
"GET /repos/{owner}/{repo}/discussions/{number}/comments",
410+
{
411+
owner,
412+
repo,
413+
number: id,
414+
per_page
415+
}
416+
)
417+
]),
418+
([{ data: discussion }, comments]) => ({
419+
info: discussion,
420+
comments
421+
}),
422+
FULL_DETAILS_TTL
423+
);
424+
}
425+
368426
/**
369427
* Get the pull requests linked to the given issue number.
370428
*

src/params/pid.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export function match(param: string) {
2-
return param === "pull" || param === "issues" || param === "discussion";
2+
return param === "pull" || param === "issues" || param === "discussions";
33
}

src/routes/[pid=pid]/[org]/[repo]/[id=number]/+page.server.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,22 @@ export async function load({ params }) {
1010
error(404, `${type} #${id} doesn't exist in repo ${org}/${repo}`);
1111
}
1212

13-
const realType = "commits" in item ? "pull" : "issues";
13+
const realType = "commits" in item ? "pull" : "category" in item.info ? "discussions" : "issues";
1414
if (type !== realType) {
15-
redirect(303, `/${realType}/${org}/${repo}/${id}`);
15+
redirect(307, `/${realType}/${org}/${repo}/${id}`);
1616
}
1717

1818
return {
1919
itemMetadata: {
2020
org,
2121
repo,
2222
id: numId,
23-
type: type === "issues" ? ("issue" as const) : ("pull" as const)
23+
type:
24+
type === "issues"
25+
? ("issue" as const)
26+
: type === "discussions"
27+
? ("discussion" as const)
28+
: ("pull" as const)
2429
},
2530
item
2631
};

src/routes/[pid=pid]/[org]/[repo]/[id=number]/+page.svelte

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
let commits = $derived("commits" in data.item ? data.item.commits : []);
88
let files = $derived("files" in data.item ? data.item.files : []);
99
let linkedEntities = $derived(
10-
"linkedPrs" in data.item ? data.item.linkedPrs : data.item.linkedIssues
10+
"linkedPrs" in data.item
11+
? data.item.linkedPrs
12+
: "linkedIssues" in data.item
13+
? data.item.linkedIssues
14+
: []
1115
);
1216
</script>
1317

src/routes/[pid=pid]/[org]/[repo]/[id=number]/PageRenderer.svelte

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@
3030
} from "@lucide/svelte";
3131
import rehypeShikiFromHighlighter from "@shikijs/rehype/core";
3232
import type { Plugin } from "svelte-exmarkdown";
33-
import type { IssueDetails, LinkedItem, PullRequestDetails } from "$lib/server/github-cache";
33+
import type {
34+
DiscussionDetails,
35+
IssueDetails,
36+
LinkedItem,
37+
PullRequestDetails
38+
} from "$lib/server/github-cache";
3439
import * as Accordion from "$lib/components/ui/accordion";
3540
import * as Avatar from "$lib/components/ui/avatar";
3641
import { Badge } from "$lib/components/ui/badge";
@@ -64,35 +69,49 @@
6469
metadata: {
6570
org: string;
6671
repo: string;
67-
type: "pull" | "issue";
72+
type: "pull" | "issue" | "discussion";
6873
};
69-
info: IssueDetails["info"] | PullRequestDetails["info"];
70-
comments: IssueDetails["comments"];
74+
info: IssueDetails["info"] | PullRequestDetails["info"] | DiscussionDetails["info"];
75+
comments: IssueDetails["comments"] | DiscussionDetails["comments"];
7176
commits: PullRequestDetails["commits"];
7277
files: PullRequestDetails["files"];
7378
linkedEntities: LinkedItem[];
7479
};
7580
7681
let { metadata, info, comments, commits, files, linkedEntities }: Props = $props();
7782
78-
let rightPartInfo = $derived([
79-
...(info.closed_at
83+
let rightPartInfo = $derived<{ title: string; value: string }[]>([
84+
...("answer_chosen_at" in info && info.answer_chosen_at
85+
? [{ title: "Answered at", value: formatToDateTime(info.answer_chosen_at) }]
86+
: []),
87+
...("answer_chosen_by" in info && info.answer_chosen_by
88+
? [{ title: "Answered by", value: info.answer_chosen_by.login }]
89+
: []),
90+
...("category" in info ? [{ title: "Category", value: info.category.name }] : []),
91+
...("closed_at" in info && info.closed_at
8092
? [
8193
{
8294
title: "merged" in info && info.merged ? "Merged at" : "Closed at",
8395
value: formatToDateTime(info.closed_at)
8496
}
8597
]
8698
: []),
87-
...(info.closed_at && "merged" in info && info.merged
99+
...("closed_at" in info && info.closed_at && "merged" in info && info.merged
88100
? [
89101
{
90102
title: "Merged by",
91103
value: info.merged_by?.name ?? info.merged_by?.login ?? "Unknown"
92104
}
93105
]
94106
: []),
95-
{ title: "Assignees", value: info.assignees?.map(a => a.login).join(", ") || "None" },
107+
...("assignees" in info
108+
? [
109+
{
110+
title: "Assignees",
111+
value: info.assignees?.map(a => a.login).join(", ") || "None"
112+
}
113+
]
114+
: []),
96115
...("requested_reviewers" in info
97116
? [
98117
{
@@ -105,7 +124,14 @@
105124
title: "Labels",
106125
value: info.labels?.map(l => (typeof l === "string" ? l : l.name)).join(", ") || "None"
107126
},
108-
{ title: "Milestone", value: info.milestone?.title || "None" }
127+
...("milestone" in info
128+
? [
129+
{
130+
title: "Milestone",
131+
value: info.milestone?.title || "None"
132+
}
133+
]
134+
: [])
109135
]);
110136
</script>
111137

@@ -180,7 +206,11 @@
180206
{/if}
181207
<div class="flex items-center">
182208
<h3 class="text-2xl font-semibold tracking-tight">
183-
{metadata.type === "pull" ? "Pull request" : "Issue"}
209+
{metadata.type === "pull"
210+
? "Pull request"
211+
: metadata.type === "issue"
212+
? "Issue"
213+
: "Discussion"}
184214
</h3>
185215
{#if info.locked}
186216
<div
@@ -200,7 +230,7 @@
200230
: "state_reason" in info && info.state_reason === "completed"
201231
? "solved"
202232
: "closed"
203-
: info.draft
233+
: "draft" in info && info.draft
204234
? "draft"
205235
: "open"}
206236
class={{ "ml-auto": !info.locked, "ml-3 xs:ml-4": info.locked }}

0 commit comments

Comments
 (0)