From 7b6a942e0cd73edd9f559c8af36f59cd9fa12f06 Mon Sep 17 00:00:00 2001 From: Kamil Krzywda Date: Thu, 24 Sep 2026 12:25:27 +0000 Subject: [PATCH] v0.73.0: remove new-package toast notifications --- CHANGELOG.md | 3 +++ lib/deriveEvents.test.ts | 10 +--------- lib/deriveEvents.ts | 13 ++++--------- package.json | 2 +- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99b1d51..27d4ef0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## v0.73.0 — 2026-09-24 12:24 +- **New-package toasts removed.** The client-side snapshot diff in `lib/deriveEvents.ts` no longer emits "New package available" info toasts when a contract appears (the user was spammed by them); the first-snapshot guard and every other event type — delivered, expired, landed, bought, sold — are unchanged. + ## v0.72.0 — 2026-09-24 11:44 - **Admin login fixed.** The seeded admin's scrypt digest in `lib/db/client.ts` was computed with different parameters than the verifier (`verifyPassword()` passes the salt as a plain string to `scryptSync(pw, saltHex, 32)`), so real logins failed with 401 "invalid credentials" since v0.71.0. The seed now carries a digest produced by exactly that call (the plaintext still exists nowhere in code, tests or comments). Because the seed is `INSERT … WHERE NOT EXISTS`, pre-existing databases keep their old row — so every `open()` also runs an idempotent rotation (`UPDATE users SET password_hash = ? WHERE id = 'admin-kamilkrzywda' AND password_hash != ?`): a no-op once healed, it repairs legacy rows at boot and is durable on the next persist. Unit tests cover the fresh seed with the new digest plus the rotate-on-open path (legacy file heals to the current digest; second open is a no-op). diff --git a/lib/deriveEvents.test.ts b/lib/deriveEvents.test.ts index 314561b..1afa283 100644 --- a/lib/deriveEvents.test.ts +++ b/lib/deriveEvents.test.ts @@ -64,7 +64,7 @@ describe('diffSnapshots', () => { expect(diffSnapshots(prev, next)).toEqual([]); }); - it('returns [] for the first snapshot (no baseline — no "new package" spam on page load)', () => { + it('returns [] for the first snapshot (no baseline to diff against)', () => { const next = snapshot({ packages: [basePackage('pkg-1'), basePackage('pkg-2')] }); expect(diffSnapshots(null, next)).toEqual([]); }); @@ -113,14 +113,6 @@ describe('diffSnapshots', () => { expect(fromLoaded).toEqual([{ kind: 'info', message: 'Package expired: Frankfurt → Madrid–Barajas' }]); }); - it('emits a new-package event for ids only in the next snapshot', () => { - const prev = snapshot({ packages: [basePackage('pkg-1')] }); - const next = snapshot({ packages: [basePackage('pkg-1'), basePackage('pkg-2')] }); - expect(diffSnapshots(prev, next)).toEqual([ - { kind: 'info', message: 'New package available: Frankfurt → Madrid–Barajas' }, - ]); - }); - it('emits bought/sold events when the plane id set changes (with the model name)', () => { const bought = diffSnapshots( snapshot(), diff --git a/lib/deriveEvents.ts b/lib/deriveEvents.ts index ef1d60d..4e53c0a 100644 --- a/lib/deriveEvents.ts +++ b/lib/deriveEvents.ts @@ -15,8 +15,7 @@ export interface GameEvent { const planeName = (model: string): string => PLANE_TYPES[model]?.name ?? model; export function diffSnapshots(prev: StateSnapshot | null, next: StateSnapshot): GameEvent[] { - // No baseline yet (first snapshot after load) — nothing to compare against. Emitting a - // "new package" event for every existing row would spam the stack on page load. + // No baseline yet (first snapshot after load) — nothing to diff against, so no events. if (prev === null) return []; const events: GameEvent[] = []; @@ -40,16 +39,12 @@ export function diffSnapshots(prev: StateSnapshot | null, next: StateSnapshot): } } - // Packages: delivered (loaded → delivered, reward paid out in the same tick), expired - // (open|loaded → expired), newly appeared (id only in next — terminal packages are never - // deleted, so an id appearing is always a genuinely new one). + // Packages: delivered (loaded → delivered, reward paid out in the same tick) and expired + // (open|loaded → expired). Ids that appear for the first time emit nothing. const prevPackages = new Map(prev.packages.map((p) => [p.id, p])); for (const pkg of next.packages) { const before = prevPackages.get(pkg.id); - if (!before) { - events.push({ kind: 'info', message: `New package available: ${pkg.originName} → ${pkg.destName}` }); - continue; - } + if (!before) continue; if (before.status === 'loaded' && pkg.status === 'delivered') { events.push({ kind: 'info', message: `Package delivered: +${formatCash(pkg.reward)}` }); } else if ((before.status === 'open' || before.status === 'loaded') && pkg.status === 'expired') { diff --git a/package.json b/package.json index aff8098..ef1b4ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flight-ops", - "version": "0.72.0", + "version": "0.73.0", "private": true, "scripts": { "dev": "next dev -p 3000",