Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions scripts/check-whats-new.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ assert.match(
);

/** One run of the effect, with the store and the network faked. */
async function run({ seen, version, app, offline, storeUser }) {
async function run({ seen, version, app, offline, storeUser, joined }) {
const store = { value: seen };
const shown = [];
const fetched = [];

const fn = new Function(
"getUserValue", "setUserValue", "fetch", "setEntry", "version", "AbortController", "SEEN_KEY", "CHANGELOG_URL", "storeUser",
"getUserValue", "setUserValue", "fetch", "setEntry", "version", "AbortController", "SEEN_KEY", "CHANGELOG_URL", "storeUser", "hasJoinedAnything",
`return (async () => { const cleanup = (() => ${body})(); await new Promise(r => setTimeout(r, 0)); return cleanup; })();`,
);

Expand All @@ -72,6 +72,7 @@ async function run({ seen, version, app, offline, storeUser }) {
SEEN_KEY,
CHANGELOG_URL,
storeUser === undefined ? "user_1" : storeUser,
() => joined ?? false,
);

return { seen: store.value, shown, fetched };
Expand All @@ -95,7 +96,7 @@ const LINE = { version: "1.10.3", date: "2026-09-08", line: "Joining voice waits

// A fresh install announces nothing, and records so the next update does.
{
const r = await run({ seen: null, version: "1.10.3", app: [LINE] });
const r = await run({ seen: null, version: "1.10.3", app: [LINE], joined: false });
assert.deepEqual(r.shown, [], "a fresh install is greeted with a what's-new modal");
assert.deepEqual(r.fetched, [], "a fresh install fetches the changelog for nothing");
assert.equal(r.seen, "1.10.3", "a fresh install did not record its version");
Expand Down Expand Up @@ -233,6 +234,31 @@ assert.deepEqual(
assert.equal(readableDate("not-a-date"), "not-a-date", "a bad date renders as Invalid Date");
}

/* An install that has joined a server and has nothing recorded ran a build
where the recording never happened. It is not new. GRYT-1102. */
{
const r = await run({ seen: null, version: "1.10.3", app: [LINE], joined: true });
assert.deepEqual(
r.shown,
[LINE],
"an install that has joined servers was treated as fresh, so the release that fixed this says nothing",
);
assert.equal(r.seen, "1.10.3", "it showed the line without recording the version");
}

// And the two cases are told apart by what the helper actually reads.
{
const fn = new Function(
"getUserValue",
block(source, "function hasJoinedAnything(): boolean {", "hasJoinedAnything")
.slice(1, -1)
.replace("<Record<string, unknown> | null>", ""),
);
assert.equal(fn(() => null), false, "no servers entry reads as having joined something");
assert.equal(fn(() => ({})), false, "an empty servers entry reads as having joined something");
assert.equal(fn(() => ({ "host:1": {} })), true, "a joined server reads as a fresh install");
}

/* ── it waits for the per-user store ─────────────────────────────────────── */

// Read on mount, the store is empty every launch: a fresh install that says
Expand Down
12 changes: 9 additions & 3 deletions src/components/whatsNew.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ interface Entry {
note?: boolean;
}

/** Whether this install has ever joined a server, which a fresh one has not. */
function hasJoinedAnything(): boolean {
const servers = getUserValue<Record<string, unknown> | null>("servers", null);
return !!servers && Object.keys(servers).length > 0;
}

/**
* What changed, the first time somebody opens a version they have not seen. The
* hand-written changelog lines, not the release body of commit subjects.
Expand All @@ -39,9 +45,9 @@ export function WhatsNew() {
const seen = getUserValue<string | null>(SEEN_KEY, null);
if (seen === version) return;

/* A fresh install has nothing to compare against. Announcing the version
somebody just chose to install reads as a bug, so record and stay quiet. */
if (seen === null) {
/* Nothing recorded: a fresh install, or one that ran 1.11.0 or 1.11.1 and
never got to write it (GRYT-1101). Only the first should stay quiet. */
if (seen === null && !hasJoinedAnything()) {
setUserValue(SEEN_KEY, version);
return;
}
Expand Down
Loading