|
1 | 1 | <script lang="ts"> |
2 | 2 | import type { ClassValue } from "svelte/elements"; |
| 3 | + import { browser } from "$app/environment"; |
3 | 4 | import { page } from "$app/state"; |
4 | 5 | import { ChevronRight } from "@lucide/svelte"; |
5 | 6 | import type { GitHubRelease } from "$lib/server/github-cache"; |
|
13 | 14 | import { Separator } from "$lib/components/ui/separator"; |
14 | 15 | import * as Card from "$lib/components/ui/card"; |
15 | 16 |
|
| 17 | + type CleanRelease = { cleanName: string; cleanVersion: string } & GitHubRelease; |
| 18 | +
|
16 | 19 | type Props = { |
17 | 20 | packageName?: string; |
18 | 21 | allPackages?: Prettify< |
|
33 | 36 | "dataFilter" | "metadataFromTag" | "changelogContentsReplacer" |
34 | 37 | > |
35 | 38 | >; |
36 | | - releases: ({ cleanName: string; cleanVersion: string } & GitHubRelease)[] | undefined; |
| 39 | + releases: CleanRelease[] | undefined; |
37 | 40 | } |
38 | 41 | | undefined |
39 | 42 | >; |
|
58 | 61 | storedPrereleaseState.value = showPrereleases; |
59 | 62 | }); |
60 | 63 | }); |
| 64 | +
|
| 65 | + /** |
| 66 | + * Extract the data from the {@link Props.otherReleases|otherReleases} |
| 67 | + * props. |
| 68 | + * |
| 69 | + * @param pkgName the package name to extract releases fo |
| 70 | + * @returns the {@link Promise} of releases, or `undefined` |
| 71 | + */ |
| 72 | + function getBadgeDataFromOther(pkgName: string) { |
| 73 | + const data = Object.entries(otherReleases).find( |
| 74 | + ([k]) => k.localeCompare(pkgName, undefined, { sensitivity: "base" }) === 0 |
| 75 | + ); |
| 76 | + if (!data) return undefined; |
| 77 | + const [, v] = data; |
| 78 | + return v; |
| 79 | + } |
| 80 | +
|
| 81 | + /** |
| 82 | + * Filter the releases to exclude those that have already been seen |
| 83 | + * |
| 84 | + * @param pkgName the package name for the releases |
| 85 | + * @param releases the releases to filter |
| 86 | + * @returns the filtered releases |
| 87 | + */ |
| 88 | + function getUnvisitedReleases(pkgName: string, releases: CleanRelease[] | undefined) { |
| 89 | + if (!releases || !browser) return []; |
| 90 | +
|
| 91 | + const lastVisitedItem = localStorage.getItem(`last-visited-${pkgName}`); |
| 92 | + if (!lastVisitedItem) return []; |
| 93 | + const lastVisitedDate = new Date(lastVisitedItem); |
| 94 | +
|
| 95 | + return releases.filter( |
| 96 | + ({ created_at, published_at }) => new Date(published_at ?? created_at) > lastVisitedDate |
| 97 | + ); |
| 98 | + } |
61 | 99 | </script> |
62 | 100 |
|
63 | 101 | {#snippet newBadge(count: number)} |
|
98 | 136 | <ul class="space-y-2"> |
99 | 137 | <!-- Sub-items --> |
100 | 138 | {#each packages as { pkg } (pkg.name)} |
101 | | - {@const linkedBadgeData = Object.entries(otherReleases).find( |
102 | | - ([k]) => k.localeCompare(pkg.name, undefined, { sensitivity: "base" }) === 0 |
103 | | - )} |
| 139 | + {@const linkedBadgeData = getBadgeDataFromOther(pkg.name)} |
104 | 140 | <li> |
105 | 141 | {#if page.url.pathname.endsWith(`/${pkg.name}`)} |
106 | 142 | <!-- Active sub-item --> |
|
114 | 150 | <span class="underline-offset-4 group-hover:underline">{pkg.name}</span> |
115 | 151 | <span class="ml-auto flex items-center gap-1"> |
116 | 152 | {#if linkedBadgeData} |
117 | | - {@const [, p] = linkedBadgeData} |
118 | | - {#await p then d} |
119 | | - {@render newBadge(d?.releases?.length ?? 0)} |
| 153 | + {#await linkedBadgeData then data} |
| 154 | + {@render newBadge( |
| 155 | + getUnvisitedReleases(pkg.name, data?.releases).length |
| 156 | + )} |
120 | 157 | {/await} |
121 | 158 | {/if} |
122 | 159 | <ChevronRight |
|
131 | 168 | {:else} |
132 | 169 | <!-- Categories with 1 sub-item --> |
133 | 170 | {@const firstPackageName = packages[0]?.pkg.name ?? ""} |
134 | | - {@const linkedBadgeData = Object.entries(otherReleases).find( |
135 | | - ([k]) => k.localeCompare(firstPackageName, undefined, { sensitivity: "base" }) === 0 |
136 | | - )} |
| 171 | + {@const linkedBadgeData = getBadgeDataFromOther(firstPackageName)} |
137 | 172 | {#if page.url.pathname.endsWith(`/${firstPackageName}`)} |
138 | 173 | <!-- Active category --> |
139 | 174 | <h3 class="text-xl font-bold text-primary underline underline-offset-4"> |
|
148 | 183 | <span class="underline-offset-4 group-hover:underline">{category.name}</span> |
149 | 184 | <span class="ml-auto flex items-center gap-1"> |
150 | 185 | {#if linkedBadgeData} |
151 | | - {@const [, p] = linkedBadgeData} |
152 | | - {#await p then d} |
153 | | - {@render newBadge(d?.releases?.length ?? 0)} |
| 186 | + {#await linkedBadgeData then data} |
| 187 | + {@render newBadge( |
| 188 | + getUnvisitedReleases(firstPackageName, data?.releases).length |
| 189 | + )} |
154 | 190 | {/await} |
155 | 191 | {/if} |
156 | 192 | <ChevronRight |
|
0 commit comments