From 032d2342eeb2be491d3c8623d62f746df63cd49f Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Fri, 31 Jul 2026 10:23:40 +0100 Subject: [PATCH] web/routes: implement starred-by page TODO: paginate, we probably want page+offset style pagination on bobbin instead of cursor style pagination. Signed-off-by: oppiliappan --- .../lib/components/repo/RepoIndexView.svelte | 4 +- web/src/lib/components/repo/StarredBy.svelte | 16 ++++++++ .../components/repo/StarredByContent.svelte | 30 ++++++++++++++ .../routes/[handle]/[repo]/+layout@.svelte | 4 +- .../routes/[handle]/[repo]/stars/+page.svelte | 20 ++++++++++ web/src/routes/[handle]/[repo]/stars/+page.ts | 40 +++++++++++++++++++ 6 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 web/src/lib/components/repo/StarredBy.svelte create mode 100644 web/src/lib/components/repo/StarredByContent.svelte create mode 100644 web/src/routes/[handle]/[repo]/stars/+page.svelte create mode 100644 web/src/routes/[handle]/[repo]/stars/+page.ts diff --git a/web/src/lib/components/repo/RepoIndexView.svelte b/web/src/lib/components/repo/RepoIndexView.svelte index a17372534..606b239a6 100644 --- a/web/src/lib/components/repo/RepoIndexView.svelte +++ b/web/src/lib/components/repo/RepoIndexView.svelte @@ -31,8 +31,8 @@ const refsCapped = $derived(data.refs.capped); - - {#if data.availability === "needs-upgrade"} + + {#if data.needsUpgrade}
diff --git a/web/src/lib/components/repo/StarredBy.svelte b/web/src/lib/components/repo/StarredBy.svelte new file mode 100644 index 000000000..0f9149155 --- /dev/null +++ b/web/src/lib/components/repo/StarredBy.svelte @@ -0,0 +1,16 @@ + + + + + diff --git a/web/src/lib/components/repo/StarredByContent.svelte b/web/src/lib/components/repo/StarredByContent.svelte new file mode 100644 index 000000000..5c9996201 --- /dev/null +++ b/web/src/lib/components/repo/StarredByContent.svelte @@ -0,0 +1,30 @@ + + +
+
+ +
+
+ + {handle} + +

+ Starred +

+
+
diff --git a/web/src/routes/[handle]/[repo]/+layout@.svelte b/web/src/routes/[handle]/[repo]/+layout@.svelte index c375954ce..bbd51eb1b 100644 --- a/web/src/routes/[handle]/[repo]/+layout@.svelte +++ b/web/src/routes/[handle]/[repo]/+layout@.svelte @@ -13,7 +13,9 @@ const activeTab = $derived.by(() => { const segment = page.route.id?.split("/")[3] ?? ""; - return ["issues", "pulls", "pipelines", "settings"].includes(segment) ? segment : "overview"; + return ["issues", "pulls", "pipelines", "settings"].includes(segment) + ? segment + : "overview"; }); // the pull page runs a discussion rail down the full page height, so it places diff --git a/web/src/routes/[handle]/[repo]/stars/+page.svelte b/web/src/routes/[handle]/[repo]/stars/+page.svelte new file mode 100644 index 000000000..38df14b27 --- /dev/null +++ b/web/src/routes/[handle]/[repo]/stars/+page.svelte @@ -0,0 +1,20 @@ + + + +

Starred by

+ + {#if data.starrers.length === 0} +

No stars yet.

+ {:else} +
+ {#each data.starrers as starrer (starrer.did)} + + {/each} +
+ {/if} +
diff --git a/web/src/routes/[handle]/[repo]/stars/+page.ts b/web/src/routes/[handle]/[repo]/stars/+page.ts new file mode 100644 index 000000000..e510df10f --- /dev/null +++ b/web/src/routes/[handle]/[repo]/stars/+page.ts @@ -0,0 +1,40 @@ +import { createBobbinClient } from "$lib/api/client"; +import { IdentityCache } from "$lib/api/identity"; +import { listStars } from "$lib/api/records"; +import { didFromUri } from "$lib/api/uri"; +import type { PageLoad } from "./$types"; + +const LIMIT = 50; + +export interface Starrer { + did: string; + handle: string; + createdAt: string; +} + +export const load: PageLoad = async (event) => { + const parent = await event.parent(); + const repoDid = parent.repo.repoDid; + + // no repoDid means bobbin never indexed this repo, so there is nothing to list + if (!repoDid) { + return { starrers: [] as Starrer[] }; + } + + const ctx = createBobbinClient({ serviceUrl: parent.publicConfig.bobbinUrl, fetch: event.fetch }); + + // TODO: paginate + const result = await listStars(ctx, repoDid, { limit: LIMIT }).catch(() => null); + const items = result?.items ?? []; + + const identity = new IdentityCache(ctx); + const starrers: Starrer[] = await Promise.all( + items.map(async (item): Promise => { + const did = didFromUri(item.uri); + const doc = await identity.resolve(did).catch(() => null); + return { did, handle: doc?.handle ?? did, createdAt: item.value.createdAt }; + }) + ); + + return { starrers }; +}; -- 2.51.2