the update dialog no longer shows an unreleased version as the target
This commit is contained in:
5 files changed
+60
-4
No files matched your search
@@ -3,6 +3,7 @@
|
|||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import { Sparkles, Bug, Zap, CheckCircle, ScrollText } from 'lucide-svelte';
|
import { Sparkles, Bug, Zap, CheckCircle, ScrollText } from 'lucide-svelte';
|
||||||
import { compareVersions } from '$lib/utils/version';
|
import { compareVersions } from '$lib/utils/version';
|
||||||
|
import { releasedEntries } from '$lib/utils/changelog-filter';
|
||||||
import ChangelogText from '$lib/components/ChangelogText.svelte';
|
import ChangelogText from '$lib/components/ChangelogText.svelte';
|
||||||
|
|
||||||
interface ChangelogEntry {
|
interface ChangelogEntry {
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
date: string;
|
date: string;
|
||||||
changes: Array<{ type: string; text: string }>;
|
changes: Array<{ type: string; text: string }>;
|
||||||
imageTag?: string;
|
imageTag?: string;
|
||||||
|
comingSoon?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -22,9 +24,11 @@
|
|||||||
|
|
||||||
let { open = $bindable(), version, changelog, lastSeenVersion, onDismiss }: Props = $props();
|
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(
|
const missedReleases = $derived(
|
||||||
changelog
|
releasedEntries(changelog)
|
||||||
.filter((r) => {
|
.filter((r) => {
|
||||||
if (!lastSeenVersion) return true; // Show all if first time
|
if (!lastSeenVersion) return true; // Show all if first time
|
||||||
return compareVersions(r.version, lastSeenVersion.replace(/^v/, '')) > 0;
|
return compareVersions(r.version, lastSeenVersion.replace(/^v/, '')) > 0;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
// Trigger rebuild for debug logging changes
|
// Trigger rebuild for debug logging changes
|
||||||
import * as Card from '$lib/components/ui/card';
|
import * as Card from '$lib/components/ui/card';
|
||||||
|
import { releasedEntries } from '$lib/utils/changelog-filter';
|
||||||
import { Badge } from '$lib/components/ui/badge';
|
import { Badge } from '$lib/components/ui/badge';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import { Input } from '$lib/components/ui/input';
|
import { Input } from '$lib/components/ui/input';
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
date: string;
|
date: string;
|
||||||
changes: ChangelogChange[];
|
changes: ChangelogChange[];
|
||||||
imageTag: string;
|
imageTag: string;
|
||||||
|
comingSoon?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dependencies = $state<Dependency[]>([]);
|
let dependencies = $state<Dependency[]>([]);
|
||||||
@@ -84,7 +86,9 @@
|
|||||||
try {
|
try {
|
||||||
const res = await fetch('/api/changelog');
|
const res = await fetch('/api/changelog');
|
||||||
if (!res.ok) throw new Error('Failed to fetch 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) {
|
} catch (e) {
|
||||||
changelogError = e instanceof Error ? e.message : 'Unknown error';
|
changelogError = e instanceof Error ? e.message : 'Unknown error';
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import * as Dialog from '$lib/components/ui/dialog';
|
import * as Dialog from '$lib/components/ui/dialog';
|
||||||
|
import { releasedEntries } from '$lib/utils/changelog-filter';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import { Badge } from '$lib/components/ui/badge';
|
import { Badge } from '$lib/components/ui/badge';
|
||||||
import { Progress } from '$lib/components/ui/progress';
|
import { Progress } from '$lib/components/ui/progress';
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
date: string;
|
date: string;
|
||||||
changes: Array<{ type: string; text: string }>;
|
changes: Array<{ type: string; text: string }>;
|
||||||
imageTag?: string;
|
imageTag?: string;
|
||||||
|
comingSoon?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -159,7 +161,9 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.ok) {
|
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) {
|
if (currentVersion && changelog.length > 0) {
|
||||||
const newer = changelog.filter(entry => compareVersions(entry.version, currentVersion) > 0);
|
const newer = changelog.filter(entry => compareVersions(entry.version, currentVersion) > 0);
|
||||||
releaseNotes = newer.length > 0 ? newer : changelog.slice(0, 1);
|
releaseNotes = newer.length > 0 ? newer : changelog.slice(0, 1);
|
||||||
|
|||||||
@@ -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');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in new issue
Block a user