the update dialog no longer shows an unreleased version as the target

This commit is contained in:
Jarek Krochmalski committed 2026-09-14 05:25:04 +02:00
1 parent 6d0e375943
commit 067318887f
5 files changed
+60 -4

No files matched your search

+6 -2
View File
@@ -3,6 +3,7 @@
import { Button } from '$lib/components/ui/button';
import { Sparkles, Bug, Zap, CheckCircle, ScrollText } from 'lucide-svelte';
import { compareVersions } from '$lib/utils/version';
import { releasedEntries } from '$lib/utils/changelog-filter';
import ChangelogText from '$lib/components/ChangelogText.svelte';
interface ChangelogEntry {
@@ -10,6 +11,7 @@
date: string;
changes: Array<{ type: string; text: string }>;
imageTag?: string;
comingSoon?: boolean;
}
interface Props {
@@ -22,9 +24,11 @@
let { open = $bindable(), version, changelog, lastSeenVersion, onDismiss }: Props = $props();
// Filter to show versions newer than lastSeenVersion, limited to 3 most recent
// Versions newer than lastSeenVersion, 3 most recent. Coming-soon (unreleased)
// entries are excluded: the user is on a released build, so an unreleased version
// must never appear as something they "missed".
const missedReleases = $derived(
changelog
releasedEntries(changelog)
.filter((r) => {
if (!lastSeenVersion) return true; // Show all if first time
return compareVersions(r.version, lastSeenVersion.replace(/^v/, '')) > 0;
+13
View File
@@ -0,0 +1,13 @@
// A coming-soon changelog entry (comingSoon: true) is an unreleased placeholder
// used for the webpage's "what's next" section. It must never appear in the app's
// released-version surfaces (About list, What's New, self-update release notes),
// which otherwise render it as the latest shipped version.
export interface ChangelogEntryLike {
comingSoon?: boolean;
}
/** Drop unreleased (comingSoon) entries, keeping only shipped releases. */
export function releasedEntries<T extends ChangelogEntryLike>(entries: T[]): T[] {
return entries.filter((e) => !e.comingSoon);
}
+5 -1
View File
@@ -1,6 +1,7 @@
<script lang="ts">
// Trigger rebuild for debug logging changes
import * as Card from '$lib/components/ui/card';
import { releasedEntries } from '$lib/utils/changelog-filter';
import { Badge } from '$lib/components/ui/badge';
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
@@ -33,6 +34,7 @@
date: string;
changes: ChangelogChange[];
imageTag: string;
comingSoon?: boolean;
}
let dependencies = $state<Dependency[]>([]);
@@ -84,7 +86,9 @@
try {
const res = await fetch('/api/changelog');
if (!res.ok) throw new Error('Failed to fetch changelog');
changelog = await res.json();
// Shipped releases only: an unreleased (coming-soon) entry must not show as
// the "Latest" version.
changelog = releasedEntries(await res.json());
} catch (e) {
changelogError = e instanceof Error ? e.message : 'Unknown error';
} finally {
@@ -1,5 +1,6 @@
<script lang="ts">
import * as Dialog from '$lib/components/ui/dialog';
import { releasedEntries } from '$lib/utils/changelog-filter';
import { Button } from '$lib/components/ui/button';
import { Badge } from '$lib/components/ui/badge';
import { Progress } from '$lib/components/ui/progress';
@@ -12,6 +13,7 @@
date: string;
changes: Array<{ type: string; text: string }>;
imageTag?: string;
comingSoon?: boolean;
}
interface Props {
@@ -159,7 +161,9 @@
);
if (response.ok) {
const changelog: ChangelogEntry[] = await response.json();
// Drop coming-soon (unreleased) entries: the update only ever goes to a
// released version, so an unreleased changelog entry must not appear here.
const changelog: ChangelogEntry[] = releasedEntries(await response.json());
if (currentVersion && changelog.length > 0) {
const newer = changelog.filter(entry => compareVersions(entry.version, currentVersion) > 0);
releaseNotes = newer.length > 0 ? newer : changelog.slice(0, 1);
+31
View File
@@ -0,0 +1,31 @@
import { describe, test, expect } from 'bun:test';
import { releasedEntries } from '../src/lib/utils/changelog-filter';
describe('releasedEntries', () => {
test('drops coming-soon (unreleased) entries', () => {
const entries = [
{ version: '1.0.48', comingSoon: true },
{ version: '1.0.47' },
{ version: '1.0.46', comingSoon: false }
];
expect(releasedEntries(entries).map((e) => e.version)).toEqual(['1.0.47', '1.0.46']);
});
test('keeps everything when nothing is coming-soon', () => {
const entries = [{ version: '1.0.47' }, { version: '1.0.46' }];
expect(releasedEntries(entries)).toHaveLength(2);
});
test('empty in, empty out', () => {
expect(releasedEntries([])).toEqual([]);
});
test('the top entry being coming-soon does not become the latest released', () => {
// The regression case: user on an older build, changelog[0] is an unreleased entry.
const entries = [
{ version: '1.0.48', comingSoon: true },
{ version: '1.0.47' }
];
expect(releasedEntries(entries)[0].version).toBe('1.0.47');
});
});