From 766482c21a42299557519068790990a6d0d47f33 Mon Sep 17 00:00:00 2001
From: dumbmoron <log@riseup.net>
Date: Sat, 29 Jun 2024 18:23:56 +0000
Subject: [PATCH] frontend: setup initial updates page

---
 web/src/components/misc/ChangelogEntry.svelte | 36 ++++++++++++
 .../misc/ChangelogEntryWrapper.svelte         | 11 ++++
 web/src/lib/changelogs.ts                     | 22 +++++++
 web/src/lib/types/changelogs.ts               | 19 ++++++
 web/src/routes/updates/+page.svelte           | 58 +++++++++++++++++--
 5 files changed, 141 insertions(+), 5 deletions(-)
 create mode 100644 web/src/components/misc/ChangelogEntry.svelte
 create mode 100644 web/src/components/misc/ChangelogEntryWrapper.svelte
 create mode 100644 web/src/lib/changelogs.ts
 create mode 100644 web/src/lib/types/changelogs.ts

diff --git a/web/src/components/misc/ChangelogEntry.svelte b/web/src/components/misc/ChangelogEntry.svelte
new file mode 100644
index 00000000..1b278498
--- /dev/null
+++ b/web/src/components/misc/ChangelogEntry.svelte
@@ -0,0 +1,36 @@
+<script lang="ts">
+    export let version: string;
+    export let title: string;
+    export let date: string;
+    export let banner: { file: string, alt: string } | undefined;
+</script>
+<style>
+    img {
+        max-width: 100%;
+    }
+
+    h1, h2 {
+        padding-top: 1em;
+    }
+
+    .contents :global(.text-backdrop) {
+        border-radius: 4px;
+        background-color: var(--button);
+        color: var(--background);
+        padding: 0.3rem;
+    }
+
+    .contents :global(.text-backdrop.link) {
+        text-decoration: underline;
+    }
+</style>
+<div>
+    <h1>{ version }: { title }</h1>
+    <h2>{ date }</h2>
+    {#if banner}
+        <img src={`/update-banners/${banner.file}`} alt={banner.alt} />
+    {/if}
+    <div class="contents">
+        <slot></slot>
+    </div>
+</div>
\ No newline at end of file
diff --git a/web/src/components/misc/ChangelogEntryWrapper.svelte b/web/src/components/misc/ChangelogEntryWrapper.svelte
new file mode 100644
index 00000000..3821fa42
--- /dev/null
+++ b/web/src/components/misc/ChangelogEntryWrapper.svelte
@@ -0,0 +1,11 @@
+<!-- Workaround for https://github.com/pngwn/MDsveX/issues/116 -->
+<script lang="ts">
+    import ChangelogEntry from "./ChangelogEntry.svelte";
+    export let version = '';
+    export let title = '';
+    export let date = '';
+    export let banner = undefined;
+</script>
+<ChangelogEntry {version} {title} {date} {banner}>
+    <slot></slot>
+</ChangelogEntry>
diff --git a/web/src/lib/changelogs.ts b/web/src/lib/changelogs.ts
new file mode 100644
index 00000000..78e7df75
--- /dev/null
+++ b/web/src/lib/changelogs.ts
@@ -0,0 +1,22 @@
+import { compareVersions } from 'compare-versions';
+
+export function getVersionFromPath(path: string) {
+    return path.split('/').pop()?.split('.md').shift()!;
+}
+
+export function getAllChangelogs() {
+    const changelogImports = import.meta.glob("/changelogs/*.md");
+
+    const sortedVersions = Object.keys(changelogImports)
+                                 .map(path => [path, getVersionFromPath(path)])
+                                 .sort(([, a], [, b]) => compareVersions(a, b));
+
+    const sortedChangelogs = sortedVersions.reduce(
+        (obj, [path, version]) => ({
+            [version]: changelogImports[path],
+            ...obj
+        }), {} as typeof changelogImports
+    );
+
+    return sortedChangelogs;
+}
diff --git a/web/src/lib/types/changelogs.ts b/web/src/lib/types/changelogs.ts
new file mode 100644
index 00000000..3dd41afc
--- /dev/null
+++ b/web/src/lib/types/changelogs.ts
@@ -0,0 +1,19 @@
+import type { SvelteComponent } from "svelte"
+
+export interface ChangelogMetadata {
+    title: string,
+    date?: string
+    banner?: {
+        file: string,
+        alt: string
+    }
+};
+
+export interface MarkdownMetadata {
+    metadata: ChangelogMetadata
+};
+
+export type ChangelogImport = {
+    default: SvelteComponent,
+    metadata: ChangelogMetadata
+} | undefined;
diff --git a/web/src/routes/updates/+page.svelte b/web/src/routes/updates/+page.svelte
index dc9670fa..4809d0fa 100644
--- a/web/src/routes/updates/+page.svelte
+++ b/web/src/routes/updates/+page.svelte
@@ -1,13 +1,61 @@
-<script>
-    import { t } from "$lib/i18n/translations";
+<script lang="ts">
+    import { SvelteComponent, onMount } from 'svelte';
+    import type { ChangelogImport, ChangelogMetadata } from '$lib/types/changelogs';
+    import { t } from '$lib/i18n/translations';
+    import { getAllChangelogs } from '$lib/changelogs';
 
-    import Placeholder from "$components/misc/Placeholder.svelte";
+    const changelogs = getAllChangelogs();
+    const versions = Object.keys(changelogs);
+
+    let changelog: ChangelogImport & { version: string } | undefined;
+    let currentIndex = -1;
+
+    const loadChangelog = async () => {
+        const version = versions[currentIndex];
+        const log = await changelogs[version]() as ChangelogImport;
+        if (!log) {
+            return; // FIXME: now wot
+        }
+
+        changelog = {
+            ...log,
+            version
+        };
+    }
+
+    const nextChangelog = async () => {
+        ++currentIndex;
+        await loadChangelog();
+    }
+
+    onMount(async () => {
+        if (versions.length > 0) {
+            await nextChangelog()
+        } else {
+            // TODO: handle if no changelogs are present
+            // (can this happen? maybe)
+        }
+    });
 </script>
 
+<style>
+    .news {
+        max-width: 768px;
+        margin: 0 auto;
+    }
+</style>
+
 <svelte:head>
     <title>
-        {$t("general.cobalt")}: {$t("tabs.donate")}
+        {$t("general.cobalt")}: {$t("tabs.updates")}
     </title>
 </svelte:head>
 
-<Placeholder pageName="updates" />
+<div class="news">
+    {#if changelog}
+        <svelte:component this={changelog.default} version={changelog.version} />
+    {/if}
+    {#if versions[currentIndex + 1]}
+        <button on:click={nextChangelog}>next</button>
+    {/if}
+</div>
\ No newline at end of file