Skip to content

Commit 009bbe8

Browse files
committed
Also fetch build list serverside for download pages
1 parent 74e5dea commit 009bbe8

File tree

10 files changed

+106
-53
lines changed

10 files changed

+106
-53
lines changed

bun.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"astro": "5.14.5",
2222
"astro-icon": "^1.1.5",
2323
"clsx": "^2.1.1",
24+
"runed": "^0.35.1",
2425
"svelte": "^5.40.2",
2526
"tailwindcss": "^4.1.14",
2627
"typescript": "^5.9.3"

src/components/data/SoftwareDownload.svelte

Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<script lang="ts">
2-
import { getVersionBuilds } from "@/utils/fill";
3-
import type { Build, ProjectDescriptor } from "@/utils/types";
2+
import type { ProjectDescriptor } from "@/utils/types";
43
54
import SoftwareDownloadButton from "@/components/data/SoftwareDownloadButton.svelte";
65
import SoftwareBuilds from "@/components/data/SoftwareBuilds.svelte";
@@ -10,16 +9,28 @@
109
import FoliaIconUrl from "@/assets/brand/folia.svg?url";
1110
import WaterfallIconUrl from "@/assets/brand/waterfall.svg?url";
1211
import type { Snippet } from "svelte";
12+
import { fetchBuildsOrError, type ProjectBuildsOrError } from "@/utils/download";
13+
import { watch } from "runed";
14+
1315
interface Props {
1416
id: "paper" | "velocity" | "folia" | "waterfall" | (string & {});
1517
project: ProjectDescriptor;
18+
builds: ProjectBuildsOrError;
1619
description?: string;
1720
Description?: Snippet;
1821
experimentalWarning?: string;
1922
eol?: boolean;
2023
}
2124
22-
let { id, project, description = undefined, Description = undefined, experimentalWarning = undefined, eol = false }: Props = $props();
25+
let {
26+
id,
27+
project,
28+
builds,
29+
description = undefined,
30+
Description = undefined,
31+
experimentalWarning = undefined,
32+
eol = false,
33+
}: Props = $props();
2334
2435
const ICONS: Record<string, string | undefined> = {
2536
paper: PaperIconUrl,
@@ -32,37 +43,6 @@
3243
3344
let version = $derived(isStable ? project?.latestStableVersion : (project?.latestExperimentalVersion ?? project?.latestStableVersion));
3445
35-
let builds: Build[] = $state([]);
36-
let latestBuild: Build | undefined = $state();
37-
let buildsLoading = $state(false);
38-
let buildsError: string | null = $state(null);
39-
40-
async function fetchBuilds() {
41-
if (!id || !version) {
42-
builds = [];
43-
latestBuild = undefined;
44-
return;
45-
}
46-
buildsLoading = true;
47-
buildsError = null;
48-
try {
49-
const res = await getVersionBuilds(id, version);
50-
builds = Array.isArray(res) ? res : [];
51-
latestBuild = builds[0] || undefined;
52-
} catch (e) {
53-
console.error(e);
54-
buildsError = `Failed to load builds for ${id} ${version}`;
55-
builds = [];
56-
latestBuild = undefined;
57-
} finally {
58-
buildsLoading = false;
59-
}
60-
}
61-
62-
$effect(() => {
63-
fetchBuilds();
64-
});
65-
6646
function toggleStable() {
6747
isStable = !isStable;
6848
}
@@ -72,6 +52,18 @@
7252
const c = (channel ?? "").toLowerCase();
7353
return `text-channel-${c}-primary`;
7454
}
55+
56+
watch(
57+
() => version,
58+
(ver, oldVer) => {
59+
// Only handle changes, not initial page load where builds are prerendered on server.
60+
if (oldVer !== undefined && ver !== oldVer) {
61+
fetchBuildsOrError({ value: project }, !isStable).then((result) => {
62+
builds = result;
63+
});
64+
}
65+
}
66+
);
7567
</script>
7668

7769
<header class="mx-auto flex max-w-7xl flex-row flex-wrap gap-16 px-4 pt-32 pb-16 lg:pt-48 lg:pb-26">
@@ -93,7 +85,7 @@
9385

9486
<h2 class="text-4xl leading-normal font-medium lg:text-5xl lg:leading-normal">
9587
Get {project.name}
96-
<span class={channelClass(latestBuild?.channel)}>{version}</span>
88+
<span class={channelClass(builds?.value?.latest?.channel)}>{version}</span>
9789
</h2>
9890

9991
<p class="mt-4 text-xl">
@@ -113,7 +105,14 @@
113105
</p>
114106

115107
<div class="mt-8 flex flex-col gap-4">
116-
<SoftwareDownloadButton projectId={id} {project} build={latestBuild} {version} eol={!!eol} disabled={buildsLoading || !latestBuild} />
108+
<SoftwareDownloadButton
109+
projectId={id}
110+
{project}
111+
build={builds.value?.latest}
112+
{version}
113+
eol={!!eol}
114+
disabled={builds.value?.latest === null || builds.value?.latest === undefined}
115+
/>
117116

118117
{#if project.latestExperimentalVersion}
119118
<button
@@ -143,12 +142,10 @@
143142
</span>
144143
</p>
145144

146-
{#if buildsLoading}
147-
<div class="text-center text-sm text-gray-400">Loading builds…</div>
148-
{:else if buildsError}
149-
<div class="text-center text-sm text-red-500">{buildsError}</div>
150-
{:else if builds.length > 0}
151-
<SoftwareBuilds project={id} {version} {builds} eol={!!eol} />
145+
{#if builds.error}
146+
<div class="text-center text-sm text-red-500">{builds.error}</div>
147+
{:else if builds.value && builds.value.builds && builds.value.builds.length > 0}
148+
<SoftwareBuilds project={id} {version} builds={builds.value.builds} eol={!!eol} />
152149
{/if}
153150
</section>
154151

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
11
<script lang="ts">
22
import { onMount, type Snippet } from "svelte";
33
import SoftwareDownload from "@/components/data/SoftwareDownload.svelte";
4-
import type { ProjectDescriptor } from "@/utils/types";
4+
import type { ProjectBuildsOrError, ProjectDescriptorOrError } from "@/utils/download";
55
66
interface Props {
77
id: string;
88
description?: string;
99
experimentalWarning?: string;
1010
eol?: boolean;
1111
Description?: Snippet;
12-
project: { error?: string; value?: ProjectDescriptor };
12+
project: ProjectDescriptorOrError;
13+
builds: ProjectBuildsOrError;
1314
}
1415
15-
let { id, description = undefined, experimentalWarning = undefined, eol = false, Description = undefined, project }: Props = $props();
16+
let {
17+
id,
18+
description = undefined,
19+
experimentalWarning = undefined,
20+
eol = false,
21+
Description = undefined,
22+
project,
23+
builds,
24+
}: Props = $props();
1625
</script>
1726

1827
{#if project.error}
1928
<header class="mx-auto max-w-7xl px-4 pt-32 pb-16 lg:pt-48 lg:pb-26">
2029
<div class="font-semibold text-red-500">{project.error}</div>
2130
</header>
2231
{:else if project.value}
23-
<SoftwareDownload {id} project={project.value} {eol} {experimentalWarning} {Description} {description} />
32+
<SoftwareDownload {id} project={project.value} {builds} {eol} {experimentalWarning} {Description} {description} />
2433
{/if}

src/pages/downloads/folia.astro

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
export const prerender = false;
33
import Layout from "@/layouts/Layout.astro";
44
import SoftwareDownloadPage from "@/components/data/SoftwareDownloadPage.svelte";
5-
import { getProjectDescriptorOrError } from "@/utils/download";
5+
import { fetchBuildsOrError, getProjectDescriptorOrError } from "@/utils/download";
66
77
Astro.response.headers.set("Cache-Control", "public, max-age=300");
88
Astro.response.headers.set("CDN-Cache-Control", "public, max-age=600");
99
const projectResult = await getProjectDescriptorOrError("folia");
10+
const buildsResult = await fetchBuildsOrError(projectResult, false);
1011
---
1112

1213
<Layout
@@ -21,5 +22,6 @@ const projectResult = await getProjectDescriptorOrError("folia");
2122
experimentalWarning="Download experimental builds of Folia, our new fork of Paper that adds regionized multithreading to the server. Proceed with caution!"
2223
client:load="svelte"
2324
project={projectResult}
25+
builds={buildsResult}
2426
/>
2527
</Layout>

src/pages/downloads/paper.astro

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
export const prerender = false;
33
import Layout from "@/layouts/Layout.astro";
44
import SoftwareDownloadPage from "@/components/data/SoftwareDownloadPage.svelte";
5-
import { getProjectDescriptorOrError } from "@/utils/download";
5+
import { getProjectDescriptorOrError, fetchBuildsOrError } from "@/utils/download";
66
77
Astro.response.headers.set("Cache-Control", "public, max-age=300");
88
Astro.response.headers.set("CDN-Cache-Control", "public, max-age=600");
99
const projectResult = await getProjectDescriptorOrError("paper");
10+
const buildsResult = await fetchBuildsOrError(projectResult, false);
1011
---
1112

1213
<Layout
@@ -21,5 +22,6 @@ const projectResult = await getProjectDescriptorOrError("paper");
2122
experimentalWarning="Download experimental builds of Paper, our Minecraft server software offering unrivaled performance and stability. Proceed with caution!"
2223
client:load="svelte"
2324
project={projectResult}
25+
builds={buildsResult}
2426
/>
2527
</Layout>

src/pages/downloads/velocity.astro

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
export const prerender = false;
33
import Layout from "@/layouts/Layout.astro";
44
import SoftwareDownloadPage from "@/components/data/SoftwareDownloadPage.svelte";
5-
import { getProjectDescriptorOrError } from "@/utils/download";
5+
import { getProjectDescriptorOrError, fetchBuildsOrError } from "@/utils/download";
66
77
Astro.response.headers.set("Cache-Control", "public, max-age=300");
88
Astro.response.headers.set("CDN-Cache-Control", "public, max-age=600");
99
const projectResult = await getProjectDescriptorOrError("velocity");
10+
const buildsResult = await fetchBuildsOrError(projectResult, false);
1011
---
1112

1213
<Layout
@@ -20,5 +21,6 @@ const projectResult = await getProjectDescriptorOrError("velocity");
2021
description="Download Velocity, our high-performance Minecraft proxy."
2122
client:load="svelte"
2223
project={projectResult}
24+
builds={buildsResult}
2325
/>
2426
</Layout>

src/pages/downloads/waterfall.astro

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
export const prerender = false;
33
import Layout from "@/layouts/Layout.astro";
44
import SoftwareDownloadPage from "@/components/data/SoftwareDownloadPage.svelte";
5-
import { getProjectDescriptorOrError } from "@/utils/download";
5+
import { getProjectDescriptorOrError, fetchBuildsOrError } from "@/utils/download";
66
77
Astro.response.headers.set("Cache-Control", "public, max-age=300");
88
Astro.response.headers.set("CDN-Cache-Control", "public, max-age=600");
99
const projectResult = await getProjectDescriptorOrError("waterfall");
10+
const buildsResult = await fetchBuildsOrError(projectResult, false);
1011
1112
const descriptionHtml = `
1213
Waterfall has reached end of life. We recommend you transition to
@@ -32,5 +33,12 @@ const descriptionHtml = `
3233
keywords={["papermc", "minecraft", "performance", "bungeecord", "waterfall", "downloads", "jar"]}
3334
canonical="/downloads/waterfall"
3435
>
35-
<SoftwareDownloadPage id="waterfall" description={descriptionHtml} eol client:load="svelte" project={projectResult} />
36+
<SoftwareDownloadPage
37+
id="waterfall"
38+
description={descriptionHtml}
39+
eol
40+
client:load="svelte"
41+
project={projectResult}
42+
builds={buildsResult}
43+
/>
3644
</Layout>

src/utils/download.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,33 @@
11
import { getProject, getVersionBuilds } from "@/utils/fill";
22
import { getHangarProjects } from "@/utils/hangar";
3-
import type { ProjectDescriptor } from "@/utils/types";
3+
import { type ProjectDescriptor, type Build } from "@/utils/types";
44

5-
export async function getProjectDescriptorOrError(id: string): Promise<{ error?: string; value?: ProjectDescriptor }> {
5+
export type ProjectDescriptorOrError = { error?: string; value?: ProjectDescriptor };
6+
export type ProjectBuildsOrError = { error?: string; value?: { latest?: Build; builds: Build[] } };
7+
8+
export async function fetchBuildsOrError(project: ProjectDescriptorOrError, experimental: boolean): Promise<ProjectBuildsOrError> {
9+
const projectId = project.value?.id;
10+
if (!projectId) {
11+
return { error: `Project id not found` };
12+
}
13+
let versionId = project.value?.latestStableVersion ?? project.value?.latestExperimentalVersion;
14+
if (experimental && project.value?.latestExperimentalVersion) {
15+
versionId = project.value?.latestExperimentalVersion;
16+
}
17+
if (!versionId) {
18+
return { error: `No versions found` };
19+
}
20+
try {
21+
const res = await getVersionBuilds(projectId, versionId);
22+
const builds = Array.isArray(res) ? res : [];
23+
const latestBuild = builds[0] || undefined;
24+
return { value: { latest: latestBuild, builds } };
25+
} catch (e) {
26+
return { error: `Failed to load builds for ${projectId} ${versionId}: ${e}` };
27+
}
28+
}
29+
30+
export async function getProjectDescriptorOrError(id: string): Promise<ProjectDescriptorOrError> {
631
try {
732
const result = await getProjectDescriptor(id);
833
if (result == null) {
@@ -37,6 +62,7 @@ export async function getProjectDescriptor(id: string): Promise<ProjectDescripto
3762
latestStableVersion !== flattenedVersions[flattenedVersions.length - 1] ? flattenedVersions[flattenedVersions.length - 1] : null;
3863

3964
return {
65+
id,
4066
name: projectData.project.name,
4167
latestStableVersion,
4268
latestExperimentalVersion,

src/utils/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export interface ProjectsResponse {
6666
}
6767

6868
export interface ProjectDescriptor {
69+
id: string;
6970
name: string;
7071
latestStableVersion: string;
7172
latestExperimentalVersion: string | null;

0 commit comments

Comments
 (0)