@bobfrankston/rmfmail 1.1.228 → 1.1.230
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.bundle.js +29 -11
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +42 -7
- package/client/app.js.map +1 -1
- package/client/app.ts +43 -7
- package/client/compose/compose.bundle.js +5 -5
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/lib/api-client.js +5 -5
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +5 -5
- package/client/lib/mailxapi.js +4 -4
- package/package.json +1 -1
- package/packages/mailx-service/index.d.ts +2 -2
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +56 -24
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +52 -18
- package/packages/mailx-service/jsonrpc.js +2 -2
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +2 -2
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-126048 → node_modules.npmglobalize-stash-86824}/.package-lock.json +0 -0
package/client/app.ts
CHANGED
|
@@ -577,6 +577,29 @@ function refreshSyncTooltip(): void {
|
|
|
577
577
|
// no new sync events.
|
|
578
578
|
setInterval(refreshSyncTooltip, 30_000);
|
|
579
579
|
|
|
580
|
+
// Honest "Synced" indicator. "Synced HH:MM" must mean sync is IDLE, not fire
|
|
581
|
+
// on every tick — folderCountsChanged/syncProgress fire repeatedly during a
|
|
582
|
+
// catch-up backfill (e.g. after the daemon was offline) while messages are
|
|
583
|
+
// STILL arriving, so stamping "Synced" each time misled (Bob 2026-06-06: "it
|
|
584
|
+
// says synced but I don't see this afternoon's mail" — they were mid-backfill).
|
|
585
|
+
// Any sync activity (re)arms this settle timer; only when activity goes quiet
|
|
586
|
+
// for SYNC_SETTLE_MS do we stamp the time. While the storm runs, the handlers
|
|
587
|
+
// keep the bar on "Syncing…".
|
|
588
|
+
let syncSettleTimer: ReturnType<typeof setTimeout> | null = null;
|
|
589
|
+
const SYNC_SETTLE_MS = 4000;
|
|
590
|
+
function scheduleSyncedStamp(): void {
|
|
591
|
+
if (syncSettleTimer) clearTimeout(syncSettleTimer);
|
|
592
|
+
syncSettleTimer = setTimeout(() => {
|
|
593
|
+
syncSettleTimer = null;
|
|
594
|
+
const el = document.getElementById("status-sync");
|
|
595
|
+
// Only flip a "Syncing…" display to "Synced" — never clobber an error
|
|
596
|
+
// ("Sync error: …") or a transient action message ("Trashed 1 …").
|
|
597
|
+
if (el && (el.textContent || "").startsWith("Syncing")) {
|
|
598
|
+
el.textContent = `Synced ${new Date().toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit", hour12: false })}`;
|
|
599
|
+
}
|
|
600
|
+
}, SYNC_SETTLE_MS);
|
|
601
|
+
}
|
|
602
|
+
|
|
580
603
|
// ── Auto two-line when message list is narrow ──
|
|
581
604
|
const messageList = document.getElementById("message-list");
|
|
582
605
|
if (messageList) {
|
|
@@ -1471,14 +1494,19 @@ async function deleteSelectedMessages(): Promise<void> {
|
|
|
1471
1494
|
// An IPC 120s timeout doesn't mean the trash failed — surfacing it
|
|
1472
1495
|
// as a status-bar error would only mislead. Real errors are still
|
|
1473
1496
|
// reported by next sync's diagnostics.
|
|
1474
|
-
|
|
1497
|
+
// Carry folderId per message so the daemon trashes the exact (folder, uid),
|
|
1498
|
+
// not whatever folder a bare (account, uid) lookup happens to resolve to
|
|
1499
|
+
// (Bob 2026-06-06: Del trashed the wrong message). uids/folderIds stay
|
|
1500
|
+
// index-aligned per account.
|
|
1501
|
+
const byAccount = new Map<string, { uids: number[]; folderIds: number[] }>();
|
|
1475
1502
|
for (const msg of snapshot) {
|
|
1476
|
-
const
|
|
1477
|
-
uids.push(msg.uid);
|
|
1478
|
-
|
|
1503
|
+
const g = byAccount.get(msg.accountId) || { uids: [], folderIds: [] };
|
|
1504
|
+
g.uids.push(msg.uid);
|
|
1505
|
+
g.folderIds.push(msg.folderId);
|
|
1506
|
+
byAccount.set(msg.accountId, g);
|
|
1479
1507
|
}
|
|
1480
|
-
for (const [accountId,
|
|
1481
|
-
deleteMessages(accountId, uids).catch((e: any) => {
|
|
1508
|
+
for (const [accountId, g] of byAccount) {
|
|
1509
|
+
deleteMessages(accountId, g.uids, g.folderIds).catch((e: any) => {
|
|
1482
1510
|
console.error(`Delete failed for ${accountId}: ${e?.message || e}`);
|
|
1483
1511
|
if (statusSync) statusSync.textContent = `Delete sync issue (${accountId}): ${e?.message || e}`;
|
|
1484
1512
|
});
|
|
@@ -2646,6 +2674,7 @@ onWsEvent((event) => {
|
|
|
2646
2674
|
}
|
|
2647
2675
|
if (statusSync) statusSync.textContent = `Syncing ${event.accountId}: ${label}`;
|
|
2648
2676
|
if (startupStatus) startupStatus.textContent = `Syncing ${event.accountId}: ${label}`;
|
|
2677
|
+
scheduleSyncedStamp(); // re-arm the settle timer; stamp "Synced" when this quiets
|
|
2649
2678
|
// Mark syncing folder in tree — bubble up to visible parent if collapsed
|
|
2650
2679
|
const syncPath = event.phase?.startsWith("sync:") ? event.phase.slice(5) : null;
|
|
2651
2680
|
// Clear previous syncing markers for this account
|
|
@@ -2672,6 +2701,7 @@ onWsEvent((event) => {
|
|
|
2672
2701
|
refreshFolderTree();
|
|
2673
2702
|
// Q53: track per-account last-sync timestamp for the status-bar hover.
|
|
2674
2703
|
recordAccountSync(event.accountId);
|
|
2704
|
+
scheduleSyncedStamp(); // settle → "Synced HH:MM" once activity stops
|
|
2675
2705
|
// Earlier I added reloadCurrentFolder() here to fix the
|
|
2676
2706
|
// "phone INBOX stays on placeholder" report. That broke desktop
|
|
2677
2707
|
// because syncComplete fires repeatedly on the desktop sync
|
|
@@ -2712,7 +2742,13 @@ onWsEvent((event) => {
|
|
|
2712
2742
|
hideAlert();
|
|
2713
2743
|
const syncBtn = document.getElementById("btn-sync") as HTMLButtonElement;
|
|
2714
2744
|
if (syncBtn) { syncBtn.disabled = false; syncBtn.classList.remove("syncing"); }
|
|
2715
|
-
|
|
2745
|
+
// Honest indicator: a count change means activity, not necessarily
|
|
2746
|
+
// "done". Show "Syncing…" and let the settle timer stamp the time
|
|
2747
|
+
// once the backfill goes quiet (see scheduleSyncedStamp).
|
|
2748
|
+
if (statusSync && !(statusSync.textContent || "").startsWith("Sync error")) {
|
|
2749
|
+
statusSync.textContent = "Syncing…";
|
|
2750
|
+
}
|
|
2751
|
+
scheduleSyncedStamp();
|
|
2716
2752
|
break;
|
|
2717
2753
|
}
|
|
2718
2754
|
case "updateAvailable": {
|
|
@@ -1322,13 +1322,13 @@ function removeUserDictWord(word) {
|
|
|
1322
1322
|
function flagSenderOrDomain(type, value) {
|
|
1323
1323
|
return ipc().flagSenderOrDomain?.(type, value) ?? Promise.resolve({ flagged: false });
|
|
1324
1324
|
}
|
|
1325
|
-
function deleteMessage(accountId, uid) {
|
|
1326
|
-
return ipc().deleteMessage?.(accountId, uid);
|
|
1325
|
+
function deleteMessage(accountId, uid, folderId) {
|
|
1326
|
+
return ipc().deleteMessage?.(accountId, uid, folderId);
|
|
1327
1327
|
}
|
|
1328
|
-
function deleteMessages(accountId, uids) {
|
|
1328
|
+
function deleteMessages(accountId, uids, folderIds) {
|
|
1329
1329
|
if (uids.length === 1)
|
|
1330
|
-
return deleteMessage(accountId, uids[0]);
|
|
1331
|
-
return ipc().deleteMessages?.(accountId, uids);
|
|
1330
|
+
return deleteMessage(accountId, uids[0], folderIds?.[0]);
|
|
1331
|
+
return ipc().deleteMessages?.(accountId, uids, folderIds);
|
|
1332
1332
|
}
|
|
1333
1333
|
function moveMessages(accountId, uids, targetFolderId, targetAccountId) {
|
|
1334
1334
|
if (uids.length === 1)
|