diff --git a/CHANGELOG.md b/CHANGELOG.md index 90a55a23..9e7a3a02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- A long error in Admin now stays on screen long enough to read, up to fifteen + seconds. + ## [1.6.0] - 2026-08-13 ### Added diff --git a/ui/js/admin-logic.js b/ui/js/admin-logic.js index a0ca5b37..4662c86f 100644 --- a/ui/js/admin-logic.js +++ b/ui/js/admin-logic.js @@ -227,3 +227,10 @@ export function rejectionLines(rejections, { withName = true } = {}) { export function refusedNoticeKey(count) { return count === 1 ? 'widgetCfg.refused' : 'widgetCfg.refusedPlural'; } + +/* Long errors need longer than short confirmations. 60ms per character is + about 16 characters a second, under a slow reading speed. */ +export function toastMs(message) { + const len = typeof message === 'string' ? message.length : 0; + return Math.min(15000, Math.max(3000, len * 60)); +} diff --git a/ui/js/admin-shared.js b/ui/js/admin-shared.js index d6676732..b8540ad0 100644 --- a/ui/js/admin-shared.js +++ b/ui/js/admin-shared.js @@ -1,5 +1,5 @@ /* Stateless helpers shared by the admin modules. Mutable state stays out. */ -import { nextActiveIndex, recoversSession } from '/js/admin-logic.js?v=056a11e9'; +import { nextActiveIndex, recoversSession, toastMs } from '/js/admin-logic.js?v=056a11e9'; import { el, qa, q } from '/js/utils.js?v=84d58686'; import { t } from '/js/i18n.js?v=133a7aac'; @@ -11,7 +11,7 @@ export const toast = (m, t = 'ok') => { e.textContent = m; e.className = `show ${t}`; clearTimeout(tt); - tt = setTimeout(() => (e.className = ''), 3000); + tt = setTimeout(() => (e.className = ''), toastMs(m)); }; /* Carry `kind` and `detail`, so callers branch on data, never on message diff --git a/ui/test/admin-logic.test.mjs b/ui/test/admin-logic.test.mjs index e6b261f1..ac974b61 100644 --- a/ui/test/admin-logic.test.mjs +++ b/ui/test/admin-logic.test.mjs @@ -16,6 +16,7 @@ import { settingsSaveBlocker, clearsStoredPassword, recoversSession, + toastMs, BLOCK, } from '../js/admin-logic.js'; /* The real strength check, so these assert the rule the save actually applies. */ @@ -485,3 +486,11 @@ test('recoversSession tolerates a missing path', () => { assert.equal(recoversSession(undefined, 401), true); assert.equal(recoversSession(null, 200), false); }); + +test('toastMs holds a long message longer than a short one', () => { + assert.equal(toastMs('Saved'), 3000); + assert.equal(toastMs(''), 3000); + assert.equal(toastMs('x'.repeat(100)), 6000); + assert.equal(toastMs('x'.repeat(1000)), 15000); + assert.equal(toastMs(undefined), 3000); +});