Skip to content

Commit b80fe89

Browse files
feat(packages-list): add sidebar badges to the packages list
Adding a bunch of copy-pasted code but yeaaah it's fine
1 parent 3ba49b9 commit b80fe89

File tree

5 files changed

+105
-32
lines changed

5 files changed

+105
-32
lines changed

src/routes/all-package-releases.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { PostHog } from "posthog-node";
2+
import { discoverer } from "$lib/server/package-discoverer";
3+
import { getPackageReleases } from "./package/releases";
4+
5+
/**
6+
* Get all the repositories and releases for all the
7+
* known packages.
8+
*
9+
* @param allPackages all the known packages
10+
* @param posthog the optional PostHog instance
11+
* @returns a map of package names to their awaitable result
12+
*/
13+
export function getAllPackagesReleases(
14+
allPackages: Awaited<ReturnType<typeof discoverer.getOrDiscoverCategorized>>,
15+
posthog?: PostHog
16+
) {
17+
const packages = allPackages.flatMap(({ packages }) => packages);
18+
19+
return packages.reduce<Record<string, ReturnType<typeof getPackageReleases>>>(
20+
(acc, { pkg: { name } }) => {
21+
if (acc[name])
22+
console.warn(
23+
`Duplicate package "${name}" while aggregating packages releases; this should not happen!`
24+
);
25+
acc[name] = getPackageReleases(name, allPackages, posthog);
26+
return acc;
27+
},
28+
{}
29+
);
30+
}

src/routes/package/+layout.server.ts

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,5 @@
1-
import type { PostHog } from "posthog-node";
21
import { discoverer } from "$lib/server/package-discoverer";
3-
import { getPackageReleases } from "./releases";
4-
5-
/**
6-
* Get all the repositories and releases for all the
7-
* known packages.
8-
*
9-
* @param allPackages all the known packages
10-
* @param posthog the optional PostHog instance
11-
* @returns a map of package names to their awaitable result
12-
*/
13-
function getAllPackagesReleases(
14-
allPackages: Awaited<ReturnType<typeof discoverer.getOrDiscoverCategorized>>,
15-
posthog?: PostHog
16-
) {
17-
const packages = allPackages.flatMap(({ packages }) => packages);
18-
19-
return packages.reduce<Record<string, ReturnType<typeof getPackageReleases>>>(
20-
(acc, { pkg: { name } }) => {
21-
if (acc[name])
22-
console.warn(
23-
`Duplicate package "${name}" while aggregating packages releases; this should not happen!`
24-
);
25-
acc[name] = getPackageReleases(name, allPackages, posthog);
26-
return acc;
27-
},
28-
{}
29-
);
30-
}
2+
import { getAllPackagesReleases } from "../all-package-releases";
313

324
/**
335
* The goal of this load function is to serve any `[...package]`
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { discoverer } from "$lib/server/package-discoverer";
2+
import { getAllPackagesReleases } from "../all-package-releases";
3+
4+
export async function load({ locals }) {
5+
// 1. Get all the packages
6+
const categorizedPackages = await discoverer.getOrDiscoverCategorized();
7+
8+
// 2. Use them to get a map of packages to promises of releases
9+
const allReleases = getAllPackagesReleases(categorizedPackages, locals.posthog);
10+
11+
// 3. Send all that down to the page's load function
12+
return { allReleases };
13+
}

src/routes/packages/+page.svelte

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,73 @@
11
<script lang="ts">
2+
import { browser } from "$app/environment";
23
import { ChevronRight } from "@lucide/svelte";
4+
import type { GitHubRelease } from "$lib/server/github-cache";
5+
import { Badge } from "$lib/components/ui/badge";
36
import { Separator } from "$lib/components/ui/separator";
47
58
let { data } = $props();
9+
10+
/**
11+
* Extract the data from the {@link import('./$types').data.otherReleases|otherReleases}
12+
* props.
13+
*
14+
* @param pkgName the package name to extract releases fo
15+
* @returns the {@link Promise} of releases, or `undefined`
16+
*/
17+
function getBadgeDataFromOther(pkgName: string) {
18+
const releases = Object.entries(data.allReleases).find(
19+
([k]) => k.localeCompare(pkgName, undefined, { sensitivity: "base" }) === 0
20+
);
21+
if (!releases) return undefined;
22+
const [, v] = releases;
23+
return v;
24+
}
25+
26+
/**
27+
* Filter the releases to exclude those that have already been seen
28+
*
29+
* @param pkgName the package name for the releases
30+
* @param releases the releases to filter
31+
* @returns the filtered releases
32+
*/
33+
function getUnvisitedReleases(pkgName: string, releases: GitHubRelease[] | undefined) {
34+
if (!releases || !browser) return [];
35+
36+
const lastVisitedItem = localStorage.getItem(`last-visited-${pkgName}`);
37+
if (!lastVisitedItem) {
38+
return releases.filter(
39+
({ created_at, published_at }) =>
40+
new Date(published_at ?? created_at).getTime() > Date.now() - 1000 * 60 * 60 * 24 * 7
41+
);
42+
}
43+
const lastVisitedDate = new Date(lastVisitedItem);
44+
45+
return releases.filter(
46+
({ created_at, published_at }) => new Date(published_at ?? created_at) > lastVisitedDate
47+
);
48+
}
649
</script>
750

51+
{#snippet newBadge(count: number)}
52+
{#if count > 0}
53+
<Badge>{count} new</Badge>
54+
{/if}
55+
{/snippet}
56+
857
<ul class="space-y-8">
958
{#each data.displayablePackages as { category, packages } (category)}
1059
<li>
1160
<h3 class="font-display text-3xl text-primary text-shadow-sm">{category.name}</h3>
1261
<ul class="mt-2">
1362
{#each packages as { repoOwner, repoName, pkg }, index (pkg.name)}
63+
{@const linkedBadgeData = getBadgeDataFromOther(pkg.name)}
1464
{#if index > 0}
1565
<Separator class="mx-auto my-1 w-[95%]" />
1666
{/if}
1767
<li>
1868
<a
1969
href="/package/{pkg.name}"
20-
class="group flex items-center rounded-xl px-4 py-3 transition-colors hover:bg-neutral-100 dark:hover:bg-neutral-800"
70+
class="group flex items-center gap-4 rounded-xl px-4 py-3 transition-colors hover:bg-neutral-100 dark:hover:bg-neutral-800"
2171
>
2272
<div class="flex flex-col">
2373
<h4 class="font-medium">{pkg.name}</h4>
@@ -31,7 +81,14 @@
3181
</span>
3282
</span>
3383
</div>
34-
<ChevronRight class="mr-1 ml-auto transition-transform group-hover:translate-x-1" />
84+
<span class="ml-auto mr-1 shrink-0 flex items-center gap-1">
85+
{#if linkedBadgeData}
86+
{#await linkedBadgeData then d}
87+
{@render newBadge(getUnvisitedReleases(pkg.name, d?.releases).length)}
88+
{/await}
89+
{/if}
90+
<ChevronRight class="transition-transform group-hover:translate-x-1" />
91+
</span>
3592
</a>
3693
</li>
3794
{/each}

src/routes/packages/+page.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { MetaTagsProps } from "svelte-meta-tags";
22

3-
export function load() {
3+
export function load({ data }) {
44
return {
5+
...data,
56
pageMetaTags: Object.freeze({
67
title: "All Packages"
78
}) satisfies MetaTagsProps

0 commit comments

Comments
 (0)