@bobfrankston/rmfmail 1.2.113 → 1.2.115
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/TODO.md +5 -5
- package/bin/mailx.js +72 -3
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +62 -3
- package/client/android-bootstrap.bundle.js +60 -3
- package/client/android-bootstrap.bundle.js.map +2 -2
- package/client/app.bundle.js +46 -0
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +66 -0
- package/client/app.js.map +1 -1
- package/client/app.ts +60 -0
- package/client/compose/compose.bundle.js +7 -2
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/compose/compose.js +16 -5
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +13 -5
- package/package.json +5 -5
- package/packages/mailx-imap/index.d.ts +4 -1
- package/packages/mailx-imap/index.d.ts.map +1 -1
- package/packages/mailx-imap/index.js +59 -5
- package/packages/mailx-imap/index.js.map +1 -1
- package/packages/mailx-imap/index.ts +55 -5
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-store-web/android-bootstrap.js +48 -3
- package/packages/mailx-store-web/android-bootstrap.js.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.ts +47 -4
- package/packages/mailx-store-web/package.json +1 -1
- package/packages/mailx-store-web/web-service.d.ts +21 -0
- package/packages/mailx-store-web/web-service.d.ts.map +1 -1
- package/packages/mailx-store-web/web-service.js +11 -0
- package/packages/mailx-store-web/web-service.js.map +1 -1
- package/packages/mailx-store-web/web-service.ts +11 -0
- package/packages/mailx-types/mailx-api.d.ts +22 -0
- package/packages/mailx-types/mailx-api.d.ts.map +1 -1
- package/packages/mailx-types/mailx-api.ts +7 -0
- package/packages/mailx-types/package.json +1 -1
package/client/app.ts
CHANGED
|
@@ -634,6 +634,63 @@ function scheduleSyncedStamp(): void {
|
|
|
634
634
|
}, SYNC_SETTLE_MS);
|
|
635
635
|
}
|
|
636
636
|
|
|
637
|
+
// C121: connect-phase ticker. The daemon emits connectProgress per IMAP
|
|
638
|
+
// connection attempt (socket → handshake → done/failed). While any attempt
|
|
639
|
+
// has been in flight longer than CONNECT_SHOW_AFTER_MS, the status bar shows
|
|
640
|
+
// "Connecting to <host> (<phase> <purpose>, 3.2s)" with a live elapsed
|
|
641
|
+
// counter — so a stuck DNS/TLS/greeting/LOGIN is distinguishable at a glance
|
|
642
|
+
// instead of an opaque frozen "Syncing…". Healthy sub-second connects never
|
|
643
|
+
// surface (the delay gate kills the flicker).
|
|
644
|
+
const activeConnects = new Map<string, { host: string; purpose: string; phase: string; startedAt: number }>();
|
|
645
|
+
const CONNECT_SHOW_AFTER_MS = 1500;
|
|
646
|
+
let connectTickTimer: ReturnType<typeof setInterval> | null = null;
|
|
647
|
+
function handleConnectProgress(event: any): void {
|
|
648
|
+
const key = `${event.accountId}|${event.purpose}`;
|
|
649
|
+
if (event.phase === "done" || event.phase === "failed") {
|
|
650
|
+
activeConnects.delete(key);
|
|
651
|
+
if (activeConnects.size === 0 && connectTickTimer) {
|
|
652
|
+
clearInterval(connectTickTimer);
|
|
653
|
+
connectTickTimer = null;
|
|
654
|
+
// Leave the bar to the normal sync handlers; stamp settle so a
|
|
655
|
+
// lingering "Connecting…" flips to Synced when things go quiet.
|
|
656
|
+
scheduleSyncedStamp();
|
|
657
|
+
}
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
const existing = activeConnects.get(key);
|
|
661
|
+
if (existing) {
|
|
662
|
+
existing.phase = event.phase;
|
|
663
|
+
} else {
|
|
664
|
+
activeConnects.set(key, {
|
|
665
|
+
host: event.host || event.accountId,
|
|
666
|
+
purpose: event.purpose || "",
|
|
667
|
+
phase: event.phase,
|
|
668
|
+
startedAt: Date.now() - (event.elapsedMs || 0),
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
if (!connectTickTimer) connectTickTimer = setInterval(renderConnectTick, 100);
|
|
672
|
+
}
|
|
673
|
+
function renderConnectTick(): void {
|
|
674
|
+
const el = document.getElementById("status-sync");
|
|
675
|
+
if (!el || activeConnects.size === 0) return;
|
|
676
|
+
// Staleness guard: if a done/failed event got lost (daemon restart,
|
|
677
|
+
// dropped IPC line) an entry would tick forever — expire at 10 min,
|
|
678
|
+
// well past any transport timeout.
|
|
679
|
+
const now = Date.now();
|
|
680
|
+
for (const [k, c] of activeConnects) {
|
|
681
|
+
if (now - c.startedAt > 600_000) activeConnects.delete(k);
|
|
682
|
+
}
|
|
683
|
+
// Show the LONGEST-waiting attempt — that's the one worth staring at.
|
|
684
|
+
let worst: { host: string; purpose: string; phase: string; startedAt: number } | null = null;
|
|
685
|
+
for (const c of activeConnects.values()) {
|
|
686
|
+
if (!worst || c.startedAt < worst.startedAt) worst = c;
|
|
687
|
+
}
|
|
688
|
+
if (!worst) return;
|
|
689
|
+
const elapsed = Date.now() - worst.startedAt;
|
|
690
|
+
if (elapsed < CONNECT_SHOW_AFTER_MS) return;
|
|
691
|
+
el.textContent = `Connecting to ${worst.host} (${worst.phase}${worst.purpose ? " " + worst.purpose : ""}, ${(elapsed / 1000).toFixed(1)}s)`;
|
|
692
|
+
}
|
|
693
|
+
|
|
637
694
|
// ── Auto two-line when message list is narrow ──
|
|
638
695
|
const messageList = document.getElementById("message-list");
|
|
639
696
|
if (messageList) {
|
|
@@ -3011,6 +3068,9 @@ onWsEvent((event) => {
|
|
|
3011
3068
|
if (startupStatus) startupStatus.textContent = "Loading accounts...";
|
|
3012
3069
|
// Don't refresh folder tree on connect — it's already loaded by initFolderTree
|
|
3013
3070
|
break;
|
|
3071
|
+
case "connectProgress":
|
|
3072
|
+
handleConnectProgress(event);
|
|
3073
|
+
break;
|
|
3014
3074
|
case "syncProgress": {
|
|
3015
3075
|
// Aggregate folders phases ("folders:<path>" when starting a folder,
|
|
3016
3076
|
// "folders-done" between folders) print as a proportion so the user
|
|
@@ -5083,10 +5083,15 @@ document.getElementById("btn-attach")?.addEventListener("click", () => {
|
|
|
5083
5083
|
var wordEditId = null;
|
|
5084
5084
|
var extEditHintClose = null;
|
|
5085
5085
|
{
|
|
5086
|
-
|
|
5086
|
+
let parentPlatform;
|
|
5087
|
+
try {
|
|
5088
|
+
parentPlatform = window.parent?.mailxapi?.platform;
|
|
5089
|
+
} catch {
|
|
5090
|
+
}
|
|
5091
|
+
const isAndroid = /Android/i.test(navigator.userAgent) || window.mailxapi?.platform === "android" || parentPlatform === "android";
|
|
5087
5092
|
if (isAndroid) {
|
|
5088
5093
|
const btn = document.getElementById("btn-edit-in-word");
|
|
5089
|
-
if (btn) btn.
|
|
5094
|
+
if (btn) btn.style.display = "none";
|
|
5090
5095
|
}
|
|
5091
5096
|
}
|
|
5092
5097
|
document.getElementById("btn-edit-in-word")?.addEventListener("click", async () => {
|