@bobfrankston/rmfmail 1.2.211 → 1.2.213

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/client/app.js CHANGED
@@ -338,9 +338,16 @@ const alertText = document.getElementById("alert-text");
338
338
  const alertDismiss = document.getElementById("alert-dismiss");
339
339
  const dismissedAlerts = new Set();
340
340
  let alertAutoDismissTimer = null;
341
+ /** Latched once the daemon reports a read-only DB handle. Every other error
342
+ * that follows is a SYMPTOM of it (each account's "attempt to write a
343
+ * readonly database", every failed flag/move), and letting those overwrite
344
+ * the banner buries the one message that names the actual problem. */
345
+ let dbReadOnlyLatched = false;
341
346
  function showAlert(message, key, opts) {
342
347
  if (key && dismissedAlerts.has(key))
343
348
  return;
349
+ if (dbReadOnlyLatched && key !== "db-readonly")
350
+ return;
344
351
  if (alertBanner && alertText) {
345
352
  alertText.textContent = message;
346
353
  alertBanner.hidden = false;
@@ -389,20 +396,14 @@ function hideAlert(opts) {
389
396
  }
390
397
  }
391
398
  alertDismiss?.addEventListener("click", () => hideAlert({ force: true }));
392
- /** Show the alert banner with a "Restart" button wired to the mailxapi
393
- * restartDaemon action. Used when a watched config file whose changes
394
- * don't apply live (accounts.jsonc) has been modified. */
395
- function showRestartForConfigBanner() {
399
+ /** Append a "Restart now" button to the alert banner, wired to the mailxapi
400
+ * restartDaemon action. Shared by every banner whose only cure is a fresh
401
+ * daemon (accounts.jsonc changed, read-only DB handle). No-op if one is
402
+ * already there. */
403
+ function addRestartButton() {
396
404
  if (!alertBanner || !alertText)
397
405
  return;
398
- // Timestamp in the banner so repeated / spurious fires are visually
399
- // distinguishable (and the user can see when the change actually
400
- // happened, useful for debugging false triggers).
401
- const ts = new Date().toLocaleTimeString([], { hour12: false });
402
- alertText.textContent = `[${ts}] accounts.jsonc changed — restart to apply.`;
403
- alertBanner.hidden = false;
404
- alertBanner.dataset.key = "config-restart";
405
- // Avoid duplicate buttons across repeat changes.
406
+ // Avoid duplicate buttons across repeat fires.
406
407
  const existing = alertBanner.querySelector("#alert-restart-btn");
407
408
  if (existing)
408
409
  return;
@@ -435,6 +436,21 @@ function showRestartForConfigBanner() {
435
436
  });
436
437
  alertText.after(btn);
437
438
  }
439
+ /** Show the alert banner with a "Restart" button wired to the mailxapi
440
+ * restartDaemon action. Used when a watched config file whose changes
441
+ * don't apply live (accounts.jsonc) has been modified. */
442
+ function showRestartForConfigBanner() {
443
+ if (!alertBanner || !alertText)
444
+ return;
445
+ // Timestamp in the banner so repeated / spurious fires are visually
446
+ // distinguishable (and the user can see when the change actually
447
+ // happened, useful for debugging false triggers).
448
+ const ts = new Date().toLocaleTimeString([], { hour12: false });
449
+ alertText.textContent = `[${ts}] accounts.jsonc changed — restart to apply.`;
450
+ alertBanner.hidden = false;
451
+ alertBanner.dataset.key = "config-restart";
452
+ addRestartButton();
453
+ }
438
454
  // ── Wire up components ──
439
455
  const folderTree = document.getElementById("folder-tree");
440
456
  let currentFolderSpecialUse = "";
@@ -6156,7 +6172,20 @@ versionPromise.then((d) => {
6156
6172
  if (tip)
6157
6173
  el.title = tip;
6158
6174
  }
6159
- if (d.settingsError) {
6175
+ if (d.dbWriteDenied) {
6176
+ // The daemon's SQLite handle came up read-only (OS refused read-write
6177
+ // at open; SQLite downgrades silently). Reads all work, so the app
6178
+ // LOOKS fine — but nothing is ever saved: ★ does nothing, moves snap
6179
+ // back, no new mail lands. Say so once, stickily, with the one action
6180
+ // that fixes it, instead of letting it dribble out as a per-account
6181
+ // "attempt to write a readonly database" banner (Bob 2026-08-02).
6182
+ if (alertBanner)
6183
+ alertBanner.style.background = "oklch(0.45 0.18 25)";
6184
+ dbReadOnlyLatched = true;
6185
+ showAlert("Database is READ-ONLY — nothing you do is being saved (★, moves, new mail). Restart rmfmail.", "db-readonly", { sticky: true });
6186
+ addRestartButton();
6187
+ }
6188
+ else if (d.settingsError) {
6160
6189
  showAlert(d.settingsError, "settings-error");
6161
6190
  // Add repair button to the banner
6162
6191
  const banner = document.getElementById("alert-banner");