@bobfrankston/mailx 1.0.242 → 1.0.244
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"height":1344,"width":2151,"x":
|
|
1
|
+
{"height":1344,"width":2151,"x":419,"y":152}
|
package/client/app.js
CHANGED
|
@@ -752,6 +752,20 @@ window.addEventListener("message", (e) => {
|
|
|
752
752
|
if (e.data?.type === "openLink" && e.data.url) {
|
|
753
753
|
window.open(e.data.url, "_blank", "noopener,noreferrer");
|
|
754
754
|
}
|
|
755
|
+
if (e.data?.type === "linkClick" && e.data.url) {
|
|
756
|
+
const url = e.data.url;
|
|
757
|
+
if (window.mailxapi?.platform === "android") {
|
|
758
|
+
// Android: use a hidden iframe to trigger OnNavigating which opens in Chrome
|
|
759
|
+
const f = document.createElement("iframe");
|
|
760
|
+
f.style.display = "none";
|
|
761
|
+
f.src = url;
|
|
762
|
+
document.body.appendChild(f);
|
|
763
|
+
setTimeout(() => f.remove(), 500);
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
767
|
+
}
|
|
768
|
+
}
|
|
755
769
|
if (e.data?.type === "linkHover") {
|
|
756
770
|
const statusEl = document.getElementById("status-sync");
|
|
757
771
|
if (statusEl) {
|
|
@@ -122,11 +122,16 @@ export function reloadCurrentFolder() {
|
|
|
122
122
|
/** Load unified inbox (all accounts) */
|
|
123
123
|
export async function loadUnifiedInbox(autoSelect = true) {
|
|
124
124
|
unifiedMode = true;
|
|
125
|
+
searchMode = false;
|
|
126
|
+
showToInsteadOfFrom = false; // Unified inbox always shows From, not To
|
|
125
127
|
currentPage = 1;
|
|
126
128
|
totalMessages = 0;
|
|
127
129
|
const body = document.getElementById("ml-body");
|
|
128
130
|
if (!body)
|
|
129
131
|
return;
|
|
132
|
+
const fromHeader = document.querySelector(".ml-col-from");
|
|
133
|
+
if (fromHeader)
|
|
134
|
+
fromHeader.textContent = "From";
|
|
130
135
|
const savedScroll = !autoSelect ? body.scrollTop : 0;
|
|
131
136
|
const savedUid = !autoSelect ? body.querySelector(".ml-row.selected")?.getAttribute("data-uid") : null;
|
|
132
137
|
if (autoSelect) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.244",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
"postinstall": "node bin/postinstall.js"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@bobfrankston/iflow-direct": "^0.1.
|
|
23
|
+
"@bobfrankston/iflow-direct": "^0.1.12",
|
|
24
24
|
"@bobfrankston/iflow-node": "^0.1.2",
|
|
25
25
|
"@bobfrankston/miscinfo": "^1.0.8",
|
|
26
26
|
"@bobfrankston/oauthsupport": "^1.0.22",
|
|
27
|
-
"@bobfrankston/msger": "^0.1.
|
|
27
|
+
"@bobfrankston/msger": "^0.1.306",
|
|
28
28
|
"@capacitor/android": "^8.3.0",
|
|
29
29
|
"@capacitor/cli": "^8.3.0",
|
|
30
30
|
"@capacitor/core": "^8.3.0",
|
|
@@ -74,11 +74,11 @@
|
|
|
74
74
|
},
|
|
75
75
|
".transformedSnapshot": {
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@bobfrankston/iflow-direct": "^0.1.
|
|
77
|
+
"@bobfrankston/iflow-direct": "^0.1.12",
|
|
78
78
|
"@bobfrankston/iflow-node": "^0.1.2",
|
|
79
79
|
"@bobfrankston/miscinfo": "^1.0.8",
|
|
80
80
|
"@bobfrankston/oauthsupport": "^1.0.22",
|
|
81
|
-
"@bobfrankston/msger": "^0.1.
|
|
81
|
+
"@bobfrankston/msger": "^0.1.306",
|
|
82
82
|
"@capacitor/android": "^8.3.0",
|
|
83
83
|
"@capacitor/cli": "^8.3.0",
|
|
84
84
|
"@capacitor/core": "^8.3.0",
|
|
@@ -1430,7 +1430,13 @@ export class ImapManager extends EventEmitter {
|
|
|
1430
1430
|
let totalFetched = 0;
|
|
1431
1431
|
let deleted = 0;
|
|
1432
1432
|
let errors = 0;
|
|
1433
|
+
let rateLimited = false;
|
|
1433
1434
|
const ERROR_BUDGET = 20;
|
|
1435
|
+
// Pace body fetches to avoid slamming Gmail's rate limit. Without a
|
|
1436
|
+
// delay, 500+ body-fetch API calls fire in a burst, every one hits 429,
|
|
1437
|
+
// and the error budget drains before any bodies land.
|
|
1438
|
+
const FETCH_DELAY_MS = this.isGmailAccount(accountId) ? 1000 : 200;
|
|
1439
|
+
const RATE_LIMIT_PAUSE_MS = 30000;
|
|
1434
1440
|
while (true) {
|
|
1435
1441
|
const missing = this.db.getMessagesWithoutBody(accountId, 100);
|
|
1436
1442
|
if (missing.length === 0)
|
|
@@ -1439,6 +1445,15 @@ export class ImapManager extends EventEmitter {
|
|
|
1439
1445
|
console.log(` [prefetch] ${accountId}: ${missing.length}+ bodies to fetch`);
|
|
1440
1446
|
let madeProgress = false;
|
|
1441
1447
|
for (const msg of missing) {
|
|
1448
|
+
// If we hit a rate limit, pause before the next fetch
|
|
1449
|
+
if (rateLimited) {
|
|
1450
|
+
console.log(` [prefetch] ${accountId}: rate-limited — pausing ${RATE_LIMIT_PAUSE_MS / 1000}s`);
|
|
1451
|
+
await new Promise(r => setTimeout(r, RATE_LIMIT_PAUSE_MS));
|
|
1452
|
+
rateLimited = false;
|
|
1453
|
+
}
|
|
1454
|
+
else if (FETCH_DELAY_MS > 0) {
|
|
1455
|
+
await new Promise(r => setTimeout(r, FETCH_DELAY_MS));
|
|
1456
|
+
}
|
|
1442
1457
|
try {
|
|
1443
1458
|
const result = await this.fetchMessageBody(accountId, msg.folderId, msg.uid);
|
|
1444
1459
|
if (result) {
|
|
@@ -1460,7 +1475,15 @@ export class ImapManager extends EventEmitter {
|
|
|
1460
1475
|
catch { /* ignore */ }
|
|
1461
1476
|
continue;
|
|
1462
1477
|
}
|
|
1463
|
-
|
|
1478
|
+
// If the error is a rate limit (429), don't count against
|
|
1479
|
+
// the budget — just slow down. The API will accept requests
|
|
1480
|
+
// again after a brief pause.
|
|
1481
|
+
if (/429|rate|too many/i.test(String(e?.message || ""))) {
|
|
1482
|
+
rateLimited = true;
|
|
1483
|
+
}
|
|
1484
|
+
else {
|
|
1485
|
+
errors++;
|
|
1486
|
+
}
|
|
1464
1487
|
if (errors >= ERROR_BUDGET) {
|
|
1465
1488
|
console.error(` [prefetch] ${accountId}: stopping after ${errors} errors (${totalFetched} cached, ${deleted} pruned)`);
|
|
1466
1489
|
return;
|