@bobfrankston/rmfmail 1.2.182 → 1.2.184
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/bin/mailx.js +21 -1
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +15 -1
- package/client/app.bundle.js +102 -9
- package/client/app.bundle.js.map +2 -2
- package/client/components/alarms.js +60 -13
- package/client/components/alarms.js.map +1 -1
- package/client/components/alarms.ts +69 -11
- package/client/components/message-list.js +54 -7
- package/client/components/message-list.js.map +1 -1
- package/client/components/message-list.ts +41 -7
- package/client/components/message-viewer.js +38 -0
- package/client/components/message-viewer.js.map +1 -1
- package/client/components/message-viewer.ts +29 -0
- package/client/compose/compose.bundle.js +4 -0
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/lib/api-client.js +5 -0
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +8 -0
- package/client/lib/mailxapi.js +5 -0
- package/package.json +3 -3
- package/packages/mailx-service/index.d.ts +11 -0
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +15 -0
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +14 -0
- package/packages/mailx-service/jsonrpc.js +2 -0
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +2 -0
- package/packages/mailx-service/package.json +1 -1
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-54716 → node_modules.npmglobalize-stash-62132}/.package-lock.json +0 -0
package/bin/mailx.ts
CHANGED
|
@@ -2131,19 +2131,33 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
2131
2131
|
}
|
|
2132
2132
|
}, 5000);
|
|
2133
2133
|
}
|
|
2134
|
+
// Open reminder popups by occurrence key, so a dismiss/snooze made on
|
|
2135
|
+
// ANOTHER machine (seen via the reminders.jsonc state sync) can retract
|
|
2136
|
+
// a popup that's already on this machine's screen. `popupKey` is
|
|
2137
|
+
// supplied by alarms.ts and stripped before the opts reach msger.
|
|
2138
|
+
const reminderPopups = new Map<string, { close: () => void }>();
|
|
2134
2139
|
svc.setPopupFn(async (opts: any) => {
|
|
2135
2140
|
// Own WebView2 profile so a reminder popup can never share (and
|
|
2136
2141
|
// crash) the main window's browser process — see S67 note on the
|
|
2137
2142
|
// popout windows below. Service-supplied opts may override.
|
|
2138
|
-
const
|
|
2143
|
+
const { popupKey, ...boxOpts } = opts || {};
|
|
2144
|
+
const h = showMessageBoxEx({ profile: "popout", ...boxOpts });
|
|
2139
2145
|
if (typeof h.pid === "number") addChildPid(h.pid);
|
|
2146
|
+
if (popupKey) reminderPopups.set(popupKey, { close: () => { try { h.close(); } catch { /* already gone */ } } });
|
|
2140
2147
|
try {
|
|
2141
2148
|
const r = await h.result;
|
|
2142
2149
|
return { button: r.button, closed: r.closed, dismissed: r.dismissed, form: (r as any).form };
|
|
2143
2150
|
} finally {
|
|
2144
2151
|
if (typeof h.pid === "number") removeChildPid(h.pid);
|
|
2152
|
+
if (popupKey) reminderPopups.delete(popupKey);
|
|
2145
2153
|
}
|
|
2146
2154
|
});
|
|
2155
|
+
svc.setPopupCloserFn((key: string) => {
|
|
2156
|
+
const p = reminderPopups.get(key);
|
|
2157
|
+
if (!p) return false;
|
|
2158
|
+
p.close();
|
|
2159
|
+
return true;
|
|
2160
|
+
});
|
|
2147
2161
|
|
|
2148
2162
|
// Optional debug HTTP server ALONGSIDE the GUI. `--server` mode replaces
|
|
2149
2163
|
// the WebView with Express; `--debug-server` instead runs a loopback
|
package/client/app.bundle.js
CHANGED
|
@@ -29,6 +29,7 @@ __export(api_client_exports, {
|
|
|
29
29
|
cancelServerSearch: () => cancelServerSearch,
|
|
30
30
|
checkDraftNewer: () => checkDraftNewer,
|
|
31
31
|
closeComposePopout: () => closeComposePopout,
|
|
32
|
+
closeReminderPopup: () => closeReminderPopup,
|
|
32
33
|
closeWordEdit: () => closeWordEdit,
|
|
33
34
|
connectEvents: () => connectEvents,
|
|
34
35
|
connectWebSocket: () => connectWebSocket,
|
|
@@ -621,6 +622,9 @@ function closeWordEdit(editId) {
|
|
|
621
622
|
function showReminderPopup(opts) {
|
|
622
623
|
return ipc().showReminderPopup?.(opts) ?? Promise.resolve({ button: "", reason: "no host" });
|
|
623
624
|
}
|
|
625
|
+
function closeReminderPopup(key) {
|
|
626
|
+
return ipc().closeReminderPopup?.(key) ?? Promise.resolve({ ok: false });
|
|
627
|
+
}
|
|
624
628
|
function mergeReminderState(patch) {
|
|
625
629
|
return ipc().mergeReminderState?.(patch) ?? Promise.resolve(null);
|
|
626
630
|
}
|
|
@@ -1595,6 +1599,19 @@ function clearViewer() {
|
|
|
1595
1599
|
headerEl.hidden = true;
|
|
1596
1600
|
if (attEl)
|
|
1597
1601
|
attEl.hidden = true;
|
|
1602
|
+
setTimeout(() => {
|
|
1603
|
+
try {
|
|
1604
|
+
const stillPlaceholder = !!document.querySelector("#mv-body .mv-empty");
|
|
1605
|
+
if (!stillPlaceholder)
|
|
1606
|
+
return;
|
|
1607
|
+
const sel = document.querySelectorAll(".ml-row.selected");
|
|
1608
|
+
if (sel.length === 0)
|
|
1609
|
+
return;
|
|
1610
|
+
const uids = Array.from(sel).slice(0, 5).map((r) => `${r.dataset.accountId}/${r.dataset.uid}`);
|
|
1611
|
+
window.mailxapi?.logClientEvent?.("[fate-share] viewer empty but rows still highlighted", { rows: uids });
|
|
1612
|
+
} catch {
|
|
1613
|
+
}
|
|
1614
|
+
}, 1e3);
|
|
1598
1615
|
}
|
|
1599
1616
|
function confirmDeleteAttachments(msg, accountId, uid, ids, question) {
|
|
1600
1617
|
const backdrop = document.createElement("div");
|
|
@@ -3109,8 +3126,23 @@ var init_message_viewer = __esm({
|
|
|
3109
3126
|
return;
|
|
3110
3127
|
if (!currentMessage)
|
|
3111
3128
|
return;
|
|
3129
|
+
const evUuid = ev.msgUuid || ev.uuid;
|
|
3130
|
+
if (evUuid && currentMessage.uuid) {
|
|
3131
|
+
if (ev.accountId === currentAccountId && evUuid === currentMessage.uuid) {
|
|
3132
|
+
try {
|
|
3133
|
+
window.mailxapi?.logClientEvent?.("[fate-share] viewed message removed (uuid) \u2014 clearing viewer", { uuid: evUuid });
|
|
3134
|
+
} catch {
|
|
3135
|
+
}
|
|
3136
|
+
clearViewer();
|
|
3137
|
+
}
|
|
3138
|
+
return;
|
|
3139
|
+
}
|
|
3112
3140
|
const folderMatches = ev.folderId == null || currentMessage.folderId == null || ev.folderId === currentMessage.folderId;
|
|
3113
3141
|
if (ev.accountId === currentAccountId && ev.uid === currentMessage.uid && folderMatches) {
|
|
3142
|
+
try {
|
|
3143
|
+
window.mailxapi?.logClientEvent?.("[fate-share] viewed message removed (uid) \u2014 clearing viewer", { uid: ev.uid, folderId: ev.folderId ?? null });
|
|
3144
|
+
} catch {
|
|
3145
|
+
}
|
|
3114
3146
|
clearViewer();
|
|
3115
3147
|
}
|
|
3116
3148
|
});
|
|
@@ -3499,10 +3531,17 @@ function focusRow(row, opts = {}) {
|
|
|
3499
3531
|
if (opts.scroll !== false) {
|
|
3500
3532
|
row.el.scrollIntoView({ block: "nearest" });
|
|
3501
3533
|
}
|
|
3502
|
-
showMessage(row.accountId, row.msg.uid, row.msg.folderId, currentSpecialUse || void 0, false, row.msg);
|
|
3503
|
-
onMessageSelect(row.accountId, row.msg.uid, row.msg.folderId);
|
|
3504
|
-
document.dispatchEvent(new CustomEvent("mailx-focus-changed", { detail: row.msg }));
|
|
3505
3534
|
rememberPosition();
|
|
3535
|
+
try {
|
|
3536
|
+
showMessage(row.accountId, row.msg.uid, row.msg.folderId, currentSpecialUse || void 0, false, row.msg);
|
|
3537
|
+
onMessageSelect(row.accountId, row.msg.uid, row.msg.folderId);
|
|
3538
|
+
document.dispatchEvent(new CustomEvent("mailx-focus-changed", { detail: row.msg }));
|
|
3539
|
+
} catch (e) {
|
|
3540
|
+
try {
|
|
3541
|
+
logClientEvent("[fate-share] focusRow viewer drive threw", { uid: row.msg?.uid, accountId: row.accountId, err: e?.message || String(e), stack: e?.stack || null });
|
|
3542
|
+
} catch {
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3506
3545
|
}
|
|
3507
3546
|
function getCurrentFocused() {
|
|
3508
3547
|
return focusedRow ? focusedRow.msg : null;
|
|
@@ -4212,8 +4251,13 @@ async function loadMessages(accountId, folderId, page = 1, specialUse = "", auto
|
|
|
4212
4251
|
setMessages(result.items);
|
|
4213
4252
|
withScrollAnchor(body, () => renderMessages(body, accountId, result.items, { background: !autoSelect }));
|
|
4214
4253
|
if (autoSelect && focusedRow) {
|
|
4215
|
-
const
|
|
4254
|
+
const fUuid = focusedRow.msg?.uuid;
|
|
4255
|
+
const stillThere = result.items.some((m) => fUuid && m.uuid ? m.uuid === fUuid : m.accountId === focusedRow.accountId && m.uid === focusedRow.msg.uid);
|
|
4216
4256
|
if (!stillThere) {
|
|
4257
|
+
try {
|
|
4258
|
+
logClientEvent("[fate-share] focused row gone after foreground load \u2014 clearing viewer", { uid: focusedRow.msg?.uid, uuid: fUuid || null, accountId: focusedRow.accountId });
|
|
4259
|
+
} catch {
|
|
4260
|
+
}
|
|
4217
4261
|
clearViewer();
|
|
4218
4262
|
focusedRow = null;
|
|
4219
4263
|
}
|
|
@@ -4309,8 +4353,13 @@ function renderMessages(body, accountId, items, opts = {}) {
|
|
|
4309
4353
|
if (focusedRow && !opts.background) {
|
|
4310
4354
|
const fAcct = focusedRow.accountId;
|
|
4311
4355
|
const fUid = focusedRow.msg?.uid;
|
|
4312
|
-
const
|
|
4356
|
+
const fUuid = focusedRow.msg?.uuid;
|
|
4357
|
+
const stillThere = items.some((m) => fUuid && m.uuid ? m.uuid === fUuid : (m.accountId || accountId) === fAcct && m.uid === fUid);
|
|
4313
4358
|
if (!stillThere) {
|
|
4359
|
+
try {
|
|
4360
|
+
logClientEvent("[fate-share] focused row gone after foreground render \u2014 clearing viewer", { uid: fUid, uuid: fUuid || null, accountId: fAcct });
|
|
4361
|
+
} catch {
|
|
4362
|
+
}
|
|
4314
4363
|
clearViewer();
|
|
4315
4364
|
focusedRow = null;
|
|
4316
4365
|
}
|
|
@@ -4335,6 +4384,12 @@ function restoreSelection(body, savedUuid) {
|
|
|
4335
4384
|
const uid = Number(row.dataset.uid);
|
|
4336
4385
|
if (accountId && Number.isFinite(uid)) {
|
|
4337
4386
|
const switching = focusedRow?.msg?.uuid !== savedUuid;
|
|
4387
|
+
if (switching) {
|
|
4388
|
+
try {
|
|
4389
|
+
logClientEvent("[fate-share] restoreSelection switching viewer", { toUuid: savedUuid, toUid: uid, fromUuid: focusedRow?.msg?.uuid || null });
|
|
4390
|
+
} catch {
|
|
4391
|
+
}
|
|
4392
|
+
}
|
|
4338
4393
|
focusByIdentity(accountId, uid, { scroll: switching });
|
|
4339
4394
|
}
|
|
4340
4395
|
}
|
|
@@ -6153,6 +6208,7 @@ async function syncReminderState(force = false) {
|
|
|
6153
6208
|
sn[k] = v;
|
|
6154
6209
|
}
|
|
6155
6210
|
saveSnoozed(sn);
|
|
6211
|
+
retractSuppressedPopups();
|
|
6156
6212
|
return true;
|
|
6157
6213
|
} catch (e) {
|
|
6158
6214
|
alog("state sync failed", { err: e?.message || String(e) });
|
|
@@ -6249,10 +6305,28 @@ function formatWhen(ms) {
|
|
|
6249
6305
|
const d = new Date(ms);
|
|
6250
6306
|
return d.toLocaleString(void 0, { weekday: "short", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
|
|
6251
6307
|
}
|
|
6308
|
+
function retractSuppressedPopups() {
|
|
6309
|
+
if (openPopups.size === 0)
|
|
6310
|
+
return;
|
|
6311
|
+
const now = Date.now();
|
|
6312
|
+
const dismissed = loadDismissed();
|
|
6313
|
+
const snoozed = loadSnoozed();
|
|
6314
|
+
for (const [key, entry] of openPopups) {
|
|
6315
|
+
if (entry.retracted)
|
|
6316
|
+
continue;
|
|
6317
|
+
if (dismissed[key] || (snoozed[key] || 0) > now) {
|
|
6318
|
+
alog("retracting popup \u2014 resolved on another machine", { key, dismissed: !!dismissed[key], snoozedUntil: snoozed[key] || 0 });
|
|
6319
|
+
try {
|
|
6320
|
+
entry.retract();
|
|
6321
|
+
} catch {
|
|
6322
|
+
}
|
|
6323
|
+
}
|
|
6324
|
+
}
|
|
6325
|
+
}
|
|
6252
6326
|
function escapeHtml6(s) {
|
|
6253
6327
|
return s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
6254
6328
|
}
|
|
6255
|
-
function showInWebViewPopup(opts) {
|
|
6329
|
+
function showInWebViewPopup(opts, onRegisterClose) {
|
|
6256
6330
|
return new Promise((resolve) => {
|
|
6257
6331
|
const overlay = document.createElement("div");
|
|
6258
6332
|
overlay.className = "alarm-overlay";
|
|
@@ -6294,12 +6368,15 @@ function showInWebViewPopup(opts) {
|
|
|
6294
6368
|
panel.querySelectorAll("[data-button]").forEach((btn) => {
|
|
6295
6369
|
btn.addEventListener("click", () => cleanup(btn.dataset.button || ""));
|
|
6296
6370
|
});
|
|
6371
|
+
onRegisterClose?.(cleanup);
|
|
6297
6372
|
});
|
|
6298
6373
|
}
|
|
6299
6374
|
async function firePopupForItem(item) {
|
|
6300
6375
|
if (openPopups.has(item.uuid))
|
|
6301
6376
|
return;
|
|
6302
|
-
|
|
6377
|
+
const popupEntry = { retracted: false, retract: () => {
|
|
6378
|
+
} };
|
|
6379
|
+
openPopups.set(item.uuid, popupEntry);
|
|
6303
6380
|
const openUrl = item.kind === "calendar" ? item.htmlLink || "https://calendar.google.com/" : "https://tasks.google.com/";
|
|
6304
6381
|
const buttons = ["snooze", "Dismiss", "Open"];
|
|
6305
6382
|
if (item.kind === "calendar")
|
|
@@ -6401,17 +6478,28 @@ async function firePopupForItem(item) {
|
|
|
6401
6478
|
let r;
|
|
6402
6479
|
try {
|
|
6403
6480
|
if (hasHost) {
|
|
6481
|
+
popupEntry.retract = () => {
|
|
6482
|
+
popupEntry.retracted = true;
|
|
6483
|
+
closeReminderPopup(item.uuid).catch(() => {
|
|
6484
|
+
});
|
|
6485
|
+
};
|
|
6404
6486
|
r = await showReminderPopup({
|
|
6405
6487
|
title,
|
|
6406
6488
|
html,
|
|
6407
6489
|
buttons,
|
|
6490
|
+
popupKey: item.uuid,
|
|
6408
6491
|
// Sized for the new snooze form (chips row + custom input
|
|
6409
6492
|
// row + action buttons) plus title/when/kind. Width fits
|
|
6410
6493
|
// "5m 15m 30m 1h 2h 1d" on one line in Segoe UI.
|
|
6411
6494
|
size: { width: 560, height: 300 }
|
|
6412
6495
|
});
|
|
6413
6496
|
} else {
|
|
6414
|
-
r = await showInWebViewPopup({ title, html, buttons })
|
|
6497
|
+
r = await showInWebViewPopup({ title, html, buttons }, (close) => {
|
|
6498
|
+
popupEntry.retract = () => {
|
|
6499
|
+
popupEntry.retracted = true;
|
|
6500
|
+
close("retracted");
|
|
6501
|
+
};
|
|
6502
|
+
});
|
|
6415
6503
|
}
|
|
6416
6504
|
} catch (e) {
|
|
6417
6505
|
alog("popup IPC failed \u2014 treating as Dismiss", { key: item.uuid, err: e?.message || String(e) });
|
|
@@ -6419,6 +6507,11 @@ async function firePopupForItem(item) {
|
|
|
6419
6507
|
} finally {
|
|
6420
6508
|
openPopups.delete(item.uuid);
|
|
6421
6509
|
}
|
|
6510
|
+
if (popupEntry.retracted) {
|
|
6511
|
+
alog("popup closed by retraction \u2014 skipping result handling", { key: item.uuid });
|
|
6512
|
+
firedThisSession.delete(item.uuid);
|
|
6513
|
+
return;
|
|
6514
|
+
}
|
|
6422
6515
|
if (!r || typeof r !== "object" || typeof r.button !== "string") {
|
|
6423
6516
|
alog("popup returned empty / malformed \u2014 treating as Dismiss", { key: item.uuid, raw: r });
|
|
6424
6517
|
r = { button: "Dismiss" };
|
|
@@ -6570,7 +6663,7 @@ var init_alarms = __esm({
|
|
|
6570
6663
|
_lastStateSync = 0;
|
|
6571
6664
|
STATE_SYNC_MIN_MS = 6e4;
|
|
6572
6665
|
firedThisSession = /* @__PURE__ */ new Set();
|
|
6573
|
-
openPopups = /* @__PURE__ */ new
|
|
6666
|
+
openPopups = /* @__PURE__ */ new Map();
|
|
6574
6667
|
}
|
|
6575
6668
|
});
|
|
6576
6669
|
|