@bobfrankston/rmfmail 1.1.5 → 1.1.7

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.
@@ -2440,12 +2440,14 @@ async function refreshPriorityIndex() {
2440
2440
  function rowKey(accountId, uid) {
2441
2441
  return `${accountId}:${uid}`;
2442
2442
  }
2443
- function focusRow(row) {
2443
+ function focusRow(row, opts = {}) {
2444
2444
  if (focusedRow && focusedRow !== row)
2445
2445
  focusedRow.setSelected(false);
2446
2446
  row.setSelected(true);
2447
2447
  focusedRow = row;
2448
- row.el.scrollIntoView({ block: "nearest" });
2448
+ if (opts.scroll !== false) {
2449
+ row.el.scrollIntoView({ block: "nearest" });
2450
+ }
2449
2451
  showMessage(row.accountId, row.msg.uid, row.msg.folderId, currentSpecialUse || void 0, false, row.msg);
2450
2452
  onMessageSelect(row.accountId, row.msg.uid, row.msg.folderId);
2451
2453
  document.dispatchEvent(new CustomEvent("mailx-focus-changed", { detail: row.msg }));
@@ -2454,11 +2456,11 @@ function focusRow(row) {
2454
2456
  function getCurrentFocused() {
2455
2457
  return focusedRow ? focusedRow.msg : null;
2456
2458
  }
2457
- function focusByIdentity(accountId, uid) {
2459
+ function focusByIdentity(accountId, uid, opts = {}) {
2458
2460
  const row = rowByKey.get(rowKey(accountId, uid));
2459
2461
  if (!row)
2460
2462
  return false;
2461
- focusRow(row);
2463
+ focusRow(row, opts);
2462
2464
  return true;
2463
2465
  }
2464
2466
  function setRowFlagged(accountId, uid, yes) {
@@ -2668,18 +2670,29 @@ function removeMessagesAndReconcile(uids) {
2668
2670
  for (const row of Array.from(body.querySelectorAll(".ml-row"))) {
2669
2671
  const el = row;
2670
2672
  const key = `${el.dataset.accountId}:${el.dataset.uid}`;
2671
- if (!stateUids.has(key))
2672
- el.remove();
2673
+ if (!stateUids.has(key)) {
2674
+ const dead = rowByKey.get(key);
2675
+ if (dead)
2676
+ dead.detach();
2677
+ else
2678
+ el.remove();
2679
+ }
2673
2680
  }
2674
2681
  if (getMessages2().length === 0) {
2675
2682
  body.innerHTML = `<div class="ml-empty">No messages</div>`;
2676
2683
  }
2677
2684
  }
2678
2685
  if (outcome.focusedWasRemoved) {
2679
- if (outcome.nextSurvivor && focusByIdentity(outcome.nextSurvivor.accountId, outcome.nextSurvivor.uid)) {
2680
- } else {
2681
- releaseFocus();
2686
+ const survivor = outcome.nextSurvivor;
2687
+ if (survivor && focusByIdentity(survivor.accountId, survivor.uid)) {
2688
+ return;
2689
+ }
2690
+ const remaining = getMessages2();
2691
+ for (const m of remaining) {
2692
+ if (focusByIdentity(m.accountId, m.uid))
2693
+ return;
2682
2694
  }
2695
+ releaseFocus();
2683
2696
  }
2684
2697
  }
2685
2698
  function reloadCurrentFolder() {
@@ -2943,7 +2956,7 @@ function restoreSelection(body, savedUid) {
2943
2956
  return;
2944
2957
  const accountId = body.querySelector(`.ml-row[data-uid="${savedUid}"]`)?.dataset.accountId;
2945
2958
  if (accountId) {
2946
- focusByIdentity(accountId, Number(savedUid));
2959
+ focusByIdentity(accountId, Number(savedUid), { scroll: false });
2947
2960
  }
2948
2961
  }
2949
2962
  async function showThreadPopup(pillEl, headMsg) {
@@ -4655,7 +4668,8 @@ async function collectDueAlarms(now) {
4655
4668
  try {
4656
4669
  const events = await getCalendarEvents(now - LOOKBACK_MS, now + LOOKAHEAD_MS);
4657
4670
  for (const ev of events) {
4658
- if (!ev.uuid)
4671
+ const stableId = ev.providerId || ev.uuid;
4672
+ if (!stableId)
4659
4673
  continue;
4660
4674
  const startMs = ev.startMs || 0;
4661
4675
  if (!startMs)
@@ -4663,7 +4677,7 @@ async function collectDueAlarms(now) {
4663
4677
  if (!Array.isArray(ev.reminderMinutes) || ev.reminderMinutes.length === 0)
4664
4678
  continue;
4665
4679
  const offsets = ev.reminderMinutes.map((m) => m * 6e4);
4666
- const occBaseKey = occKey(ev.uuid, startMs);
4680
+ const occBaseKey = occKey(stableId, startMs);
4667
4681
  let pick = null;
4668
4682
  const eligibleOffsets = [];
4669
4683
  for (const offsetMs of offsets) {
@@ -4937,6 +4951,17 @@ async function firePopupForItem(item) {
4937
4951
  snoozeItem(item, 60);
4938
4952
  break;
4939
4953
  case "Dismiss":
4954
+ case "dismissed":
4955
+ // msger reports window-close as r.dismissed=true → daemon
4956
+ // wrapper lowercases to "dismissed". Treat the same as
4957
+ // explicit Dismiss-button: the popup HAD a clear button
4958
+ // for that action, so any close that lands here is the
4959
+ // user's "I'm done with this reminder" — not a 15-min
4960
+ // snooze. Bob 2026-05-14: "I keep dismissing the
4961
+ // reminder but it keeps coming back" — the lowercase
4962
+ // path was hitting the snooze-15-min default, so the
4963
+ // popup re-fired every 15 min indefinitely.
4964
+ case "closed":
4940
4965
  {
4941
4966
  const m = loadDismissed();
4942
4967
  m[item.uuid] = true;
@@ -4953,7 +4978,12 @@ async function firePopupForItem(item) {
4953
4978
  deleteCalendarEvent(item.uuid).catch((e) => console.error(` [alarm] delete failed for ${item.uuid}: ${e?.message || e}`));
4954
4979
  break;
4955
4980
  default:
4956
- snoozeItem(item, 15);
4981
+ console.warn(`[alarm] unknown popup result: button=${JSON.stringify(r.button)}, form=${JSON.stringify(r.form)}`);
4982
+ {
4983
+ const m = loadDismissed();
4984
+ m[item.uuid] = true;
4985
+ saveDismissed(m);
4986
+ }
4957
4987
  break;
4958
4988
  }
4959
4989
  }
@@ -7988,6 +8018,10 @@ subscribeStore("*", (ev) => {
7988
8018
  document.dispatchEvent(new CustomEvent("mailx-remove-stale", {
7989
8019
  detail: { accountId: ev.accountId, uid: ev.uid }
7990
8020
  }));
8021
+ } else if (ev.kind === "messageMoved" && ev.accountId && ev.uid) {
8022
+ document.dispatchEvent(new CustomEvent("mailx-remove-stale", {
8023
+ detail: { accountId: ev.accountId, uid: ev.uid }
8024
+ }));
7991
8025
  }
7992
8026
  });
7993
8027
  async function openComposeFromMailto(m) {