@bobfrankston/mailx 1.0.154 → 1.0.155
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 +27 -8
- package/client/components/folder-tree.js +10 -5
- package/client/index.html +2 -2
- package/package.json +2 -2
- package/packages/mailx-imap/index.js +7 -3
package/bin/mailx.js
CHANGED
|
@@ -657,13 +657,10 @@ async function main() {
|
|
|
657
657
|
// Pass server version to dispatch so getVersion returns it
|
|
658
658
|
const rootPkg = JSON.parse(fs.readFileSync(path.join(import.meta.dirname, "..", "package.json"), "utf-8"));
|
|
659
659
|
handle.onRequest(async (req) => {
|
|
660
|
-
|
|
661
|
-
console.error(`[ipc] ← ${req._action} (${req._cbid})`);
|
|
662
|
-
req._version = rootPkg.version;
|
|
660
|
+
console.log(`[ipc] ← ${req._action} (${req._cbid})`);
|
|
663
661
|
try {
|
|
664
662
|
const response = await dispatch(svc, req);
|
|
665
|
-
|
|
666
|
-
console.error(`[ipc] → ${req._action} (${req._cbid}) ok`);
|
|
663
|
+
console.log(`[ipc] → ${req._action} (${req._cbid}) ok`);
|
|
667
664
|
handle.send(response);
|
|
668
665
|
}
|
|
669
666
|
catch (e) {
|
|
@@ -671,12 +668,34 @@ async function main() {
|
|
|
671
668
|
handle.send({ _cbid: req._cbid, error: e.message });
|
|
672
669
|
}
|
|
673
670
|
});
|
|
674
|
-
// Wire IMAP events → push to WebView
|
|
671
|
+
// Wire IMAP events → push to WebView (throttled to avoid flooding stdin)
|
|
672
|
+
let pendingSyncProgress = {};
|
|
673
|
+
let syncProgressTimer = null;
|
|
675
674
|
imapManager.on("syncProgress", (accountId, phase, progress) => {
|
|
676
|
-
|
|
675
|
+
pendingSyncProgress[accountId] = { phase, progress };
|
|
676
|
+
if (!syncProgressTimer) {
|
|
677
|
+
syncProgressTimer = setTimeout(() => {
|
|
678
|
+
syncProgressTimer = null;
|
|
679
|
+
for (const [id, p] of Object.entries(pendingSyncProgress)) {
|
|
680
|
+
handle.send({ _event: "syncProgress", type: "syncProgress", accountId: id, phase: p.phase, progress: p.progress });
|
|
681
|
+
}
|
|
682
|
+
pendingSyncProgress = {};
|
|
683
|
+
}, 500); // batch sync events every 500ms
|
|
684
|
+
}
|
|
677
685
|
});
|
|
686
|
+
let pendingCounts = {};
|
|
687
|
+
let countsTimer = null;
|
|
678
688
|
imapManager.on("folderCountsChanged", (accountId, counts) => {
|
|
679
|
-
|
|
689
|
+
pendingCounts[accountId] = counts;
|
|
690
|
+
if (!countsTimer) {
|
|
691
|
+
countsTimer = setTimeout(() => {
|
|
692
|
+
countsTimer = null;
|
|
693
|
+
for (const [id, c] of Object.entries(pendingCounts)) {
|
|
694
|
+
handle.send({ _event: "folderCountsChanged", type: "folderCountsChanged", accountId: id, ...c });
|
|
695
|
+
}
|
|
696
|
+
pendingCounts = {};
|
|
697
|
+
}, 1000); // batch count updates every 1s
|
|
698
|
+
}
|
|
680
699
|
});
|
|
681
700
|
imapManager.on("syncError", (accountId, error) => {
|
|
682
701
|
handle.send({ _event: "error", type: "error", message: `${accountId}: ${error}` });
|
|
@@ -603,11 +603,16 @@ async function loadFolderTree(container) {
|
|
|
603
603
|
setTimeout(() => overlay?.remove(), 400);
|
|
604
604
|
}
|
|
605
605
|
catch (e) {
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
container.
|
|
610
|
-
|
|
606
|
+
// Don't destroy existing folder tree on error — just log it
|
|
607
|
+
console.error(`Folder tree error: ${e.message}`);
|
|
608
|
+
// Only show error if tree is completely empty (first load failure)
|
|
609
|
+
if (container.children.length === 0 || container.querySelector(".folder-loading")) {
|
|
610
|
+
const errEl = document.createElement("div");
|
|
611
|
+
errEl.className = "folder-loading";
|
|
612
|
+
errEl.textContent = `Error loading folders: ${e.message}`;
|
|
613
|
+
container.replaceChildren(errEl);
|
|
614
|
+
}
|
|
615
|
+
// Dismiss overlay on error too
|
|
611
616
|
const overlay = document.getElementById("startup-overlay");
|
|
612
617
|
if (overlay) {
|
|
613
618
|
const status = document.getElementById("startup-status");
|
package/client/index.html
CHANGED
|
@@ -128,9 +128,9 @@
|
|
|
128
128
|
|
|
129
129
|
<footer class="status-bar" id="status-bar">
|
|
130
130
|
<span id="status-accounts"></span>
|
|
131
|
-
<span id="status-sync"
|
|
131
|
+
<span id="status-sync">Syncing...</span>
|
|
132
132
|
<span id="status-pending"></span>
|
|
133
|
-
<span id="status-queue"
|
|
133
|
+
<span id="status-queue"></span>
|
|
134
134
|
</footer>
|
|
135
135
|
|
|
136
136
|
<div id="startup-overlay" class="startup-overlay">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.155",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"@bobfrankston/iflow": "^1.0.53",
|
|
24
24
|
"@bobfrankston/miscinfo": "^1.0.7",
|
|
25
25
|
"@bobfrankston/oauthsupport": "^1.0.20",
|
|
26
|
-
"@bobfrankston/msger": "^0.1.
|
|
26
|
+
"@bobfrankston/msger": "^0.1.205",
|
|
27
27
|
"@capacitor/android": "^8.3.0",
|
|
28
28
|
"@capacitor/cli": "^8.3.0",
|
|
29
29
|
"@capacitor/core": "^8.3.0",
|
|
@@ -636,7 +636,8 @@ export class ImapManager extends EventEmitter {
|
|
|
636
636
|
if (inbox) {
|
|
637
637
|
try {
|
|
638
638
|
client = await this.createClientWithLimit(accountId);
|
|
639
|
-
|
|
639
|
+
const inboxTimeout = this.db.getHighestUid(accountId, inbox.id) === 0 ? 300000 : 60000;
|
|
640
|
+
await withTimeout(this.syncFolder(accountId, inbox.id, client), inboxTimeout, client, "Inbox sync");
|
|
640
641
|
await client.logout();
|
|
641
642
|
client = null;
|
|
642
643
|
}
|
|
@@ -885,8 +886,10 @@ export class ImapManager extends EventEmitter {
|
|
|
885
886
|
this.syncIntervals.set(`quick:${accountId}`, timer);
|
|
886
887
|
console.log(` [periodic] ${accountId}: STATUS check every ${interval / 1000}s (${this.isOAuthAccount(accountId) ? "OAuth" : "password"})`);
|
|
887
888
|
}
|
|
888
|
-
// Sync actions (sends + flags/deletes/moves) every 30 seconds
|
|
889
|
+
// Sync actions (sends + flags/deletes/moves) every 30 seconds — skip during active sync
|
|
889
890
|
const actionsInterval = setInterval(async () => {
|
|
891
|
+
if (this.syncing)
|
|
892
|
+
return;
|
|
890
893
|
for (const [accountId] of this.configs) {
|
|
891
894
|
this.processSendActions(accountId).catch(() => { });
|
|
892
895
|
this.processSyncActions(accountId).catch(() => { });
|
|
@@ -1136,7 +1139,8 @@ export class ImapManager extends EventEmitter {
|
|
|
1136
1139
|
async updateFlagsLocal(accountId, uid, folderId, flags) {
|
|
1137
1140
|
this.db.updateMessageFlags(accountId, uid, flags);
|
|
1138
1141
|
this.db.queueSyncAction(accountId, "flags", uid, folderId, { flags });
|
|
1139
|
-
|
|
1142
|
+
// Don't process immediately — let the 30s timer batch actions
|
|
1143
|
+
// (immediate processing during sync causes connection churn)
|
|
1140
1144
|
}
|
|
1141
1145
|
/** Process pending sync actions for an account */
|
|
1142
1146
|
async processSyncActions(accountId) {
|