@bobfrankston/mailx 1.0.185 → 1.0.186
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/client/.msger-window.json +1 -1
- package/client/android.html +1 -1
- package/client/app.js +4 -8
- package/client/components/folder-tree.js +38 -2
- package/client/components/message-viewer.js +1 -6
- package/client/compose/compose.js +2 -7
- package/client/lib/api-client.js +5 -0
- package/package.json +1 -1
- package/packages/mailx-store-web/android-bootstrap.js +44 -2
- package/packages/mailx-store-web/sql-wasm-esm.js +10 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"height":1344,"width":
|
|
1
|
+
{"height":1344,"width":1586,"x":793,"y":81}
|
package/client/android.html
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"@bobfrankston/mailx-store-web": "../packages/mailx-store-web/index.js",
|
|
16
16
|
"@bobfrankston/mailx-store-web/": "../packages/mailx-store-web/",
|
|
17
17
|
"@bobfrankston/mailx-types": "../packages/mailx-types/index.js",
|
|
18
|
-
"sql.js": "../
|
|
18
|
+
"sql.js": "../packages/mailx-store-web/sql-wasm-esm.js"
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
</script>
|
package/client/app.js
CHANGED
|
@@ -305,14 +305,10 @@ async function openCompose(mode) {
|
|
|
305
305
|
}
|
|
306
306
|
// Store init data for compose window to pick up
|
|
307
307
|
sessionStorage.setItem("composeInit", JSON.stringify(init));
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
else {
|
|
314
|
-
window.open("compose/compose.html", "_blank", "width=800,height=600,menubar=no,toolbar=no,status=no");
|
|
315
|
-
}
|
|
308
|
+
// Open compose — always as popup via window.open().
|
|
309
|
+
// In IPC mode, msger's new_window_req_handler allows msger.localhost popups.
|
|
310
|
+
// DO NOT use window.location.href — msger exits on navigation.
|
|
311
|
+
window.open("compose/compose.html", "_blank", "width=800,height=600,menubar=no,toolbar=no,status=no");
|
|
316
312
|
}
|
|
317
313
|
function quoteBody(msg) {
|
|
318
314
|
const date = new Date(msg.date).toLocaleString();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Folder tree component -- renders account folders with hierarchy,
|
|
3
3
|
* expand/collapse, and optional unified inbox.
|
|
4
4
|
*/
|
|
5
|
-
import { getAccounts, getFolders, moveMessage, moveMessages, markFolderRead, createFolder, renameFolder, deleteFolder, emptyFolder, setupAccount, getVersion } from "../lib/api-client.js";
|
|
5
|
+
import { getAccounts, getFolders, moveMessage, moveMessages, markFolderRead, createFolder, renameFolder, deleteFolder, emptyFolder, setupAccount, getDeviceAccounts, getVersion } from "../lib/api-client.js";
|
|
6
6
|
import { showContextMenu } from "./context-menu.js";
|
|
7
7
|
let onFolderSelect;
|
|
8
8
|
let onUnifiedInbox = null;
|
|
@@ -357,8 +357,9 @@ async function loadFolderTree(container) {
|
|
|
357
357
|
if (mainBody) {
|
|
358
358
|
mainBody.innerHTML = `<div style="padding:2rem;line-height:1.8;max-width:500px">
|
|
359
359
|
<h2 style="margin-bottom:1rem">Welcome to mailx</h2>
|
|
360
|
+
<div id="setup-device-accounts"></div>
|
|
360
361
|
<div id="setup-cloud-status"></div>
|
|
361
|
-
<p>Add your email account to get started.</p>
|
|
362
|
+
<p id="setup-form-intro">Add your email account to get started.</p>
|
|
362
363
|
<form id="setup-form" style="margin-top:1rem">
|
|
363
364
|
<label style="display:block;margin-bottom:0.5rem">
|
|
364
365
|
Your name
|
|
@@ -462,6 +463,41 @@ async function loadFolderTree(container) {
|
|
|
462
463
|
</div>`;
|
|
463
464
|
}
|
|
464
465
|
}).catch(() => { });
|
|
466
|
+
// On Android, check for device Google accounts and show picker
|
|
467
|
+
getDeviceAccounts().then(async (deviceAccounts) => {
|
|
468
|
+
const pickerEl = document.getElementById("setup-device-accounts");
|
|
469
|
+
if (!pickerEl || deviceAccounts.length === 0)
|
|
470
|
+
return;
|
|
471
|
+
const formEl = document.getElementById("setup-form");
|
|
472
|
+
const introEl = document.getElementById("setup-form-intro");
|
|
473
|
+
if (introEl)
|
|
474
|
+
introEl.textContent = "Select an account:";
|
|
475
|
+
if (formEl)
|
|
476
|
+
formEl.style.display = "none";
|
|
477
|
+
pickerEl.innerHTML = deviceAccounts.map((a) => `<button class="device-account-btn" data-email="${a.email}" data-name="${a.name}" style="display:block;width:100%;padding:0.75rem 1rem;margin-bottom:0.5rem;background:var(--color-bg-surface);color:var(--color-text);border:1px solid var(--color-border);border-radius:6px;cursor:pointer;font-size:1rem;text-align:left">${a.email}</button>`).join("") + `<button id="setup-show-form" style="margin-top:0.5rem;padding:0.5rem 1rem;background:none;color:var(--color-text-muted);border:none;cursor:pointer;font-size:0.9rem">Use a different account...</button>`;
|
|
478
|
+
pickerEl.querySelectorAll(".device-account-btn").forEach((btn) => {
|
|
479
|
+
btn.addEventListener("click", async () => {
|
|
480
|
+
const email = btn.dataset.email || "";
|
|
481
|
+
const name = btn.dataset.name || "";
|
|
482
|
+
pickerEl.innerHTML = `<div style="padding:0.5rem;color:var(--color-text-muted)">Setting up ${email}...</div>`;
|
|
483
|
+
const result = await setupAccount(name, email, "");
|
|
484
|
+
if (result?.ok) {
|
|
485
|
+
pickerEl.innerHTML = `<div style="padding:0.5rem;color:var(--color-accent)">${result.message || "Account added!"}</div>`;
|
|
486
|
+
setTimeout(() => location.reload(), 2000);
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
pickerEl.innerHTML = `<div style="padding:0.5rem;color:#f55">${result?.error || "Setup failed"}</div>`;
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
document.getElementById("setup-show-form")?.addEventListener("click", () => {
|
|
494
|
+
pickerEl.style.display = "none";
|
|
495
|
+
if (formEl)
|
|
496
|
+
formEl.style.display = "block";
|
|
497
|
+
if (introEl)
|
|
498
|
+
introEl.textContent = "Add your email account to get started.";
|
|
499
|
+
});
|
|
500
|
+
}).catch(() => { });
|
|
465
501
|
}
|
|
466
502
|
// Dismiss startup overlay
|
|
467
503
|
const overlay = document.getElementById("startup-overlay");
|
|
@@ -144,12 +144,7 @@ export async function showMessage(accountId, uid, folderId, specialUse, isRetry
|
|
|
144
144
|
draftFolderId: msg.folderId,
|
|
145
145
|
};
|
|
146
146
|
sessionStorage.setItem("composeInit", JSON.stringify(init));
|
|
147
|
-
|
|
148
|
-
window.location.href = "compose/compose.html";
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
window.open("compose/compose.html", "_blank", "width=800,height=600,menubar=no,toolbar=no,status=no");
|
|
152
|
-
}
|
|
147
|
+
window.open("compose/compose.html", "_blank", "width=800,height=600,menubar=no,toolbar=no,status=no");
|
|
153
148
|
};
|
|
154
149
|
}
|
|
155
150
|
else {
|
|
@@ -5,14 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { createEditor } from "./editor.js";
|
|
7
7
|
import { getVersion, getSettings, getAccounts, searchContacts, sendMessage, saveDraft as apiSaveDraft, deleteDraft } from "../lib/api-client.js";
|
|
8
|
-
/** Close compose
|
|
8
|
+
/** Close compose window */
|
|
9
9
|
function closeCompose() {
|
|
10
|
-
|
|
11
|
-
window.location.href = "../index.html";
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
closeCompose();
|
|
15
|
-
}
|
|
10
|
+
window.close();
|
|
16
11
|
}
|
|
17
12
|
// ── Load editor scripts dynamically ──
|
|
18
13
|
function loadScript(src) {
|
package/client/lib/api-client.js
CHANGED
|
@@ -309,6 +309,11 @@ export function setupAccount(name, email, password) {
|
|
|
309
309
|
return getIpc().setupAccount?.(name, email, password);
|
|
310
310
|
return api("/setup", { method: "POST", body: JSON.stringify({ name, email, password }) });
|
|
311
311
|
}
|
|
312
|
+
export async function getDeviceAccounts() {
|
|
313
|
+
if (hasIPC())
|
|
314
|
+
return getIpc().getDeviceAccounts?.() ?? [];
|
|
315
|
+
return [];
|
|
316
|
+
}
|
|
312
317
|
// Legacy exports for backward compatibility
|
|
313
318
|
export const connectWebSocket = connectEvents;
|
|
314
319
|
export const onWsEvent = onEvent;
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import { WebMailxDB } from "./db.js";
|
|
15
15
|
import { WebMessageStore } from "./web-message-store.js";
|
|
16
16
|
import { WebMailxService } from "./web-service.js";
|
|
17
|
-
import { loadAccounts, clearSettings } from "./web-settings.js";
|
|
17
|
+
import { loadAccounts, saveAccounts, clearSettings } from "./web-settings.js";
|
|
18
18
|
import { GmailApiWebProvider } from "./gmail-api-web.js";
|
|
19
19
|
// ── State ──
|
|
20
20
|
let db;
|
|
@@ -327,7 +327,49 @@ function installBridge() {
|
|
|
327
327
|
},
|
|
328
328
|
getAutocompleteSettings: () => service.getAutocompleteSettings(),
|
|
329
329
|
saveAutocompleteSettings: async (settings) => { await service.saveAutocompleteSettings(settings); return { ok: true }; },
|
|
330
|
-
|
|
330
|
+
getDeviceAccounts: async () => {
|
|
331
|
+
const bridge = window._nativeBridge;
|
|
332
|
+
if (bridge?.app?.getDeviceAccounts) {
|
|
333
|
+
return bridge.app.getDeviceAccounts();
|
|
334
|
+
}
|
|
335
|
+
return [];
|
|
336
|
+
},
|
|
337
|
+
setupAccount: async (name, email, _password) => {
|
|
338
|
+
try {
|
|
339
|
+
if (!email || !email.includes("@")) {
|
|
340
|
+
return { ok: false, error: "Email address required" };
|
|
341
|
+
}
|
|
342
|
+
const domain = email.split("@")[1].toLowerCase();
|
|
343
|
+
const id = domain.split(".")[0] || "account";
|
|
344
|
+
const account = {
|
|
345
|
+
id,
|
|
346
|
+
name: name || email.split("@")[0],
|
|
347
|
+
email,
|
|
348
|
+
enabled: true,
|
|
349
|
+
imap: { host: `imap.${domain}`, port: 993, tls: true, auth: "oauth2", user: email },
|
|
350
|
+
smtp: { host: `smtp.${domain}`, port: 587, tls: true, auth: "oauth2", user: email },
|
|
351
|
+
};
|
|
352
|
+
// Apply known provider defaults
|
|
353
|
+
if (domain === "gmail.com" || domain === "googlemail.com") {
|
|
354
|
+
account.label = "Gmail";
|
|
355
|
+
account.imap = { host: "imap.gmail.com", port: 993, tls: true, auth: "oauth2", user: email };
|
|
356
|
+
account.smtp = { host: "smtp.gmail.com", port: 587, tls: true, auth: "oauth2", user: email };
|
|
357
|
+
}
|
|
358
|
+
const existing = await loadAccounts();
|
|
359
|
+
if (existing.some(a => a.email === email)) {
|
|
360
|
+
return { ok: true, message: "Account already exists" };
|
|
361
|
+
}
|
|
362
|
+
existing.push(account);
|
|
363
|
+
await saveAccounts(existing);
|
|
364
|
+
await syncManager.addAccount(account);
|
|
365
|
+
db.upsertAccount(account.id, account.name, account.email, JSON.stringify(account));
|
|
366
|
+
console.log(`[android] Account added: ${email}`);
|
|
367
|
+
return { ok: true, message: `Added ${email}. Syncing...` };
|
|
368
|
+
}
|
|
369
|
+
catch (e) {
|
|
370
|
+
return { ok: false, error: e.message };
|
|
371
|
+
}
|
|
372
|
+
},
|
|
331
373
|
repairAccounts: async () => ({ ok: false, error: "Use desktop for repair" }),
|
|
332
374
|
resetStore: () => resetStore(),
|
|
333
375
|
restart: () => { location.reload(); },
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// ESM wrapper for sql.js (UMD) — loads as classic script, re-exports the global
|
|
2
|
+
await new Promise((resolve, reject) => {
|
|
3
|
+
const s = document.createElement("script");
|
|
4
|
+
s.src = new URL("../../node_modules/sql.js/dist/sql-wasm.js", import.meta.url).href;
|
|
5
|
+
s.onload = resolve;
|
|
6
|
+
s.onerror = reject;
|
|
7
|
+
document.head.appendChild(s);
|
|
8
|
+
});
|
|
9
|
+
const initSqlJs = globalThis.initSqlJs;
|
|
10
|
+
export default initSqlJs;
|