@bobfrankston/rmfmail 1.2.166 → 1.2.167
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 +3 -0
- package/client/android-bootstrap.bundle.js +23 -4
- package/client/android-bootstrap.bundle.js.map +2 -2
- package/client/app.bundle.js +17 -0
- package/client/app.bundle.js.map +2 -2
- package/client/components/folder-tree.js +25 -1
- package/client/components/folder-tree.js.map +1 -1
- package/client/components/folder-tree.ts +21 -1
- package/package.json +5 -5
- package/packages/mailx-store-web/android-bootstrap.d.ts.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.js +31 -4
- package/packages/mailx-store-web/android-bootstrap.js.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.ts +31 -4
- package/packages/mailx-store-web/package.json +1 -1
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-61300 → node_modules.npmglobalize-stash-28172}/.package-lock.json +0 -0
package/TODO.md
CHANGED
|
@@ -18,6 +18,9 @@ These items don't need user input — source-only changes, compile-verified befo
|
|
|
18
18
|
| ~~4~~ | ~~**C122 — Recent-unread count overlay on taskbar icon**~~ — **DONE** (shipped ~v1.1.x 2026-05-29; TODO entry was stale). `updateBadge` in `client/app.ts` renders the red count pill on a 32×32 canvas (badge-only, transparent field) and pushes it via `mailxapi.setTaskbarOverlay`; hooked to `folderCountsChanged` + startup via `updateNewMessageCount`. Badge shows NEW-since-last-seen, matching the "recent-unread" spec. S65 (badge while not running) remains open/blocked. | S | |
|
|
19
19
|
| ~~5~~ | ~~**C126 — Cold-start latency (~28s observed)**~~ — **MOSTLY DONE** (2026-05-11). Root cause IDed from boot log: 43 s of cascading ES module imports through msger's custom protocol IPC (one roundtrip per file). Fixes shipped: (a) bundling via esbuild (`bin/build-bundles.mjs`) collapses the cascade to one fetch per entry point — `client/app.bundle.js` 345 kb, `client/compose/compose.bundle.js` 99 kb; (b) boot-snapshot hydration — bundled app saves `folder-tree` + `ml-body` innerHTML to localStorage every 30 s; inline script in `index.html` restores at cold start before the bundle has fetched. Sub-bullet (c) [500 ms placeholder pause] not in current code at cited line — skipped as stale. Expected cold start now 3-5 s (full WebView2 + bundle parse), with near-instant *feel* via hydrated snapshot after the first run. | M | |
|
|
20
20
|
| **6** | **C125 — Unify desktop + Android IMAP code paths** | M | Today's split (`mailx-imap` Node-only + `mailx-store-web` browser-only) duplicates orchestration that's protocol-identical. Differences boil down to (a) transport factory choice (b) whether clients are persistent — both parameters, not architectural divides. Plan: extract `mailx-imap-core` with ImapManager + ops queue + fast-lane + sync orchestration over a `Transport` and a `Storage` interface, no Node-specific imports. `mailx-imap` and `mailx-store-web` become thin shims that wire engine-specific Storage + Transport. `{persistent: true\|false}` flag on ImapManager covers the lifetime split (no fast-lane queue when ephemeral). Pre-req for Android non-Gmail IMAP without forking sync code. |
|
|
21
|
+
| **7** | **C156 — Android first-boot/setup progress narration** [M] | M | New-device install looked frozen (Bob 2026-07-22): no indication of "Signing into <gmail>…", "Fetching settings from Google Drive…", "Syncing INBOX (120/2000)…". Surface the android-bootstrap stage breadcrumbs (already vlog'd since v1.2.167) in the startup status area / folder-tree loading slot. |
|
|
22
|
+
| **8** | **C157 — Android fresh install: paint folder list before message sync** [S/M] | S/M | First sync ran minutes with an empty tree. Prioritize per-account folder-LIST fetch + paint ahead of message sync so the skeleton appears in seconds; message counts stream in behind. |
|
|
23
|
+
| **9** | **C158 — Android double-init after fold-triggered reload** [M] | M | 2026-07-22 13:07:49 logit trail: ONE page reload after unfolding produced TWO module loads, duplicate "bridge installed", duplicate GDrive lookups, and THREE "Starting OAuth flow" launches. Activity did NOT recreate (ConfigChanges already declared; no OnCreate in trail) — find what reloads the WebView on fold and make android-bootstrap init idempotent (single-flight guard on the module-level init, dedupe OAuth intent launches). |
|
|
21
24
|
| **5** | **C124 — mailto handler on macOS** (Linux ✅ 2026-07-08 v1.2.115) | S | Linux half shipped: `--register-mailto` on linux writes `~/.local/share/applications/rmfmail.desktop` (`MimeType=x-scheme-handler/mailto;`, quoted node+mailx.js Exec, bare `%u`), runs `xdg-mime default` + `update-desktop-database`; `--unregister-mailto` removes it. **Remaining: macOS** — real `.app` bundle (LaunchServices won't register a bare script) with `Info.plist` `CFBundleURLTypes` declaring `mailto` + a tiny launcher binary; `LSSetDefaultHandlerForURLScheme` programmatically sets the default. Rust `rmfmailto-src/` crate already portable — drop the Win32 imports and `cargo build --target x86_64-apple-darwin` produces the macOS launcher. Needs a Mac to build/verify. |
|
|
22
25
|
| ~~7~~ | ~~**C154 — Audit Android bridge vs MailxApi contract**~~ — **DONE** (2026-07-08, v1.2.115). Bridge now has a generic contract-backstop: every service method not explicitly adapted gets a positional pass-through, so a missing method can never again fail as `parent bridge has no method "X"`. Wire-shape mismatches adapted explicitly (`readJsoncFile`/`writeJsoncFile`/`formatJsonc`/`readConfigHelp` → `{content}`/`{ok}`, `drainStoreSync`/`syncFolderNow` → `{ok}`). Contract gained the missing entries (`getMessageSource`, `copyMessages`, `syncFolderNow`, `cancelServerSearch`, `getReminderSound`, `getCalendars`, optional `popoutWindow`) with web-service stubs per the policy (reads → graceful default, writes/OS → notImpl). Bonus fixes: bridge `getMessages` was dropping sort/sortDir/search and `searchMessages` was dropping scope/accountId/folderId — Android sort toggles and scoped search silently didn't work. | S | |
|
|
23
26
|
~~done — C120 read transport diagnostics~~ shipped v1.0.583 (per-folder timeout error now includes `[conn#X r=YB w=ZB writes=N sinceLastRead=Tms]` snapshot for the doomed socket).
|
|
@@ -11022,10 +11022,20 @@ async function initAndroid() {
|
|
|
11022
11022
|
}, 1e3);
|
|
11023
11023
|
if (gmailTokenProvider) {
|
|
11024
11024
|
const tp = gmailTokenProvider;
|
|
11025
|
-
|
|
11025
|
+
const RECONCILE_RETRY_MS = 3e4;
|
|
11026
|
+
const RECONCILE_MAX_TRIES = 5;
|
|
11027
|
+
let reconcileTry = 0;
|
|
11028
|
+
const reconcile = async () => {
|
|
11029
|
+
reconcileTry++;
|
|
11026
11030
|
setGDriveTokenProvider(tp);
|
|
11027
11031
|
try {
|
|
11028
|
-
console.log(
|
|
11032
|
+
console.log(`[android] Looking up GDrive .rmfmail folder\u2026 (try ${reconcileTry}/${RECONCILE_MAX_TRIES})`);
|
|
11033
|
+
vlog(`gdrive-reconcile try ${reconcileTry}: token fetch`);
|
|
11034
|
+
await Promise.race([
|
|
11035
|
+
tp(),
|
|
11036
|
+
new Promise((_, rej) => setTimeout(() => rej(new Error("token fetch timed out (20s)")), 2e4))
|
|
11037
|
+
]);
|
|
11038
|
+
vlog(`gdrive-reconcile try ${reconcileTry}: folder lookup`);
|
|
11029
11039
|
const folder = await findGDriveMailxFolder(tp);
|
|
11030
11040
|
if (!folder) {
|
|
11031
11041
|
emitEvent({
|
|
@@ -11075,13 +11085,22 @@ async function initAndroid() {
|
|
|
11075
11085
|
await registerDeviceInGDrive(tp, folderId, accounts.map((a) => a.id));
|
|
11076
11086
|
}
|
|
11077
11087
|
} catch (e) {
|
|
11088
|
+
vlog(`gdrive-reconcile try ${reconcileTry} FAILED: ${e.message}`);
|
|
11089
|
+
if (reconcileTry < RECONCILE_MAX_TRIES) {
|
|
11090
|
+
console.warn(`[android] GDrive reconcile failed (${e.message}) \u2014 retrying in ${RECONCILE_RETRY_MS / 1e3}s`);
|
|
11091
|
+
setTimeout(() => {
|
|
11092
|
+
void reconcile();
|
|
11093
|
+
}, RECONCILE_RETRY_MS);
|
|
11094
|
+
return;
|
|
11095
|
+
}
|
|
11078
11096
|
emitEvent({
|
|
11079
11097
|
type: "fatal",
|
|
11080
11098
|
key: "gdrive-access-failed",
|
|
11081
|
-
message: `GDrive access failed
|
|
11099
|
+
message: `GDrive access failed after ${RECONCILE_MAX_TRIES} attempts: ${e.message}. Mail from this device's local accounts still works; restart the app to retry cloud settings.`
|
|
11082
11100
|
});
|
|
11083
11101
|
}
|
|
11084
|
-
}
|
|
11102
|
+
};
|
|
11103
|
+
void reconcile();
|
|
11085
11104
|
}
|
|
11086
11105
|
service.loadContactsConfig().catch((e) => console.error(`[android] startup contacts.jsonc load: ${e?.message || e}`));
|
|
11087
11106
|
for (const account of db.getAccounts()) {
|