diff --git a/web/src/lib/components/repo/issues/IssueCard.stories.svelte b/web/src/lib/components/repo/issues/IssueCard.stories.svelte new file mode 100644 index 00000000..3a462985 --- /dev/null +++ b/web/src/lib/components/repo/issues/IssueCard.stories.svelte @@ -0,0 +1,31 @@ + + + + + diff --git a/web/src/lib/components/repo/issues/IssueCard.svelte b/web/src/lib/components/repo/issues/IssueCard.svelte new file mode 100644 index 00000000..3b693367 --- /dev/null +++ b/web/src/lib/components/repo/issues/IssueCard.svelte @@ -0,0 +1,47 @@ + + + diff --git a/web/src/routes/[handle]/[repo]/issues/+page.svelte b/web/src/routes/[handle]/[repo]/issues/+page.svelte new file mode 100644 index 00000000..4c80616e --- /dev/null +++ b/web/src/routes/[handle]/[repo]/issues/+page.svelte @@ -0,0 +1,20 @@ + + +
+ +
+ +
+ +
diff --git a/web/src/routes/[handle]/[repo]/issues/+page.ts b/web/src/routes/[handle]/[repo]/issues/+page.ts new file mode 100644 index 00000000..8596ca8e --- /dev/null +++ b/web/src/routes/[handle]/[repo]/issues/+page.ts @@ -0,0 +1,56 @@ +import { createBobbinClient } from "$lib/api/client"; +import { count } from "$lib/api/count"; +import { IdentityCache } from "$lib/api/identity"; +import { listIssues } from "$lib/api/records"; +import { didFromUri, rkeyFromUri } from "$lib/api/uri"; +import type { IssueSummary } from "$lib/components/repo/types"; +import type { PageLoad } from "./$types"; + +export const load: PageLoad = async (event) => { + const parent = await event.parent(); + const state = event.url.searchParams.get("state") === "closed" ? "closed" : "open"; + const repoDid = parent.repo.repoDid; + + // no repoDid means bobbin never indexed this repo, so there is nothing to list + if (!repoDid) { + return { + state: state as "open" | "closed", + issues: [] as IssueSummary[], + openCount: parent.counts.issues, + closedCount: 0 + }; + } + + const ctx = createBobbinClient({ serviceUrl: parent.publicConfig.bobbinUrl, fetch: event.fetch }); + + const [page, closed] = await Promise.all([ + listIssues(ctx, repoDid, { state }), + // the layout only knows the open count; the closed tab needs its own + count(ctx, "sh.tangled.repo.countIssues", repoDid, { state: "closed" }).catch(() => null) + ]); + + const identity = new IdentityCache(ctx); + const issues: IssueSummary[] = await Promise.all( + page.items.map(async (item): Promise => { + const authorDid = didFromUri(item.uri); + const author = await identity.resolve(authorDid).catch(() => null); + return { + uri: item.uri, + rkey: rkeyFromUri(item.uri), + title: item.value.title, + state: item.state === "closed" ? "closed" : "open", + authorDid, + authorHandle: author?.handle ?? authorDid, + createdAt: item.value.createdAt, + commentCount: item.commentCount + }; + }) + ); + + return { + state: state as "open" | "closed", + issues, + openCount: parent.counts.issues, + closedCount: closed?.count ?? 0 + }; +};