@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.
Files changed (37) hide show
  1. package/TODO.md +5 -5
  2. package/bin/mailx.js +72 -3
  3. package/bin/mailx.js.map +1 -1
  4. package/bin/mailx.ts +62 -3
  5. package/client/android-bootstrap.bundle.js +60 -3
  6. package/client/android-bootstrap.bundle.js.map +2 -2
  7. package/client/app.bundle.js +46 -0
  8. package/client/app.bundle.js.map +2 -2
  9. package/client/app.js +66 -0
  10. package/client/app.js.map +1 -1
  11. package/client/app.ts +60 -0
  12. package/client/compose/compose.bundle.js +7 -2
  13. package/client/compose/compose.bundle.js.map +2 -2
  14. package/client/compose/compose.js +16 -5
  15. package/client/compose/compose.js.map +1 -1
  16. package/client/compose/compose.ts +13 -5
  17. package/package.json +5 -5
  18. package/packages/mailx-imap/index.d.ts +4 -1
  19. package/packages/mailx-imap/index.d.ts.map +1 -1
  20. package/packages/mailx-imap/index.js +59 -5
  21. package/packages/mailx-imap/index.js.map +1 -1
  22. package/packages/mailx-imap/index.ts +55 -5
  23. package/packages/mailx-imap/package-lock.json +2 -2
  24. package/packages/mailx-imap/package.json +1 -1
  25. package/packages/mailx-store-web/android-bootstrap.js +48 -3
  26. package/packages/mailx-store-web/android-bootstrap.js.map +1 -1
  27. package/packages/mailx-store-web/android-bootstrap.ts +47 -4
  28. package/packages/mailx-store-web/package.json +1 -1
  29. package/packages/mailx-store-web/web-service.d.ts +21 -0
  30. package/packages/mailx-store-web/web-service.d.ts.map +1 -1
  31. package/packages/mailx-store-web/web-service.js +11 -0
  32. package/packages/mailx-store-web/web-service.js.map +1 -1
  33. package/packages/mailx-store-web/web-service.ts +11 -0
  34. package/packages/mailx-types/mailx-api.d.ts +22 -0
  35. package/packages/mailx-types/mailx-api.d.ts.map +1 -1
  36. package/packages/mailx-types/mailx-api.ts +7 -0
  37. 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
- const isAndroid = window.mailxapi?.platform === "android" || window.parent?.mailxapi?.platform === "android";
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.hidden = true;
5094
+ if (btn) btn.style.display = "none";
5090
5095
  }
5091
5096
  }
5092
5097
  document.getElementById("btn-edit-in-word")?.addEventListener("click", async () => {