@bobfrankston/rmfmail 1.1.97 → 1.1.99
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/.commitmsg +14 -13
- package/client/app.bundle.js +2 -0
- package/client/app.bundle.js.map +2 -2
- package/client/components/message-list.js +8 -0
- package/client/components/message-list.js.map +1 -1
- package/client/components/message-list.ts +7 -0
- package/client/compose/compose.bundle.js +11 -7
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/compose/compose.js +21 -13
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +20 -12
- package/npmchanges.md +28 -0
- package/package.json +3 -3
- package/packages/mailx-service/sync-queue.d.ts +8 -0
- package/packages/mailx-service/sync-queue.d.ts.map +1 -1
- package/packages/mailx-service/sync-queue.js +40 -4
- package/packages/mailx-service/sync-queue.js.map +1 -1
- package/packages/mailx-service/sync-queue.ts +39 -4
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-26080 → node_modules.npmglobalize-stash-75440}/.package-lock.json +0 -0
|
@@ -33,6 +33,17 @@ interface BodyFetchKey { accountId: string; folderId: number; uid: number; lane:
|
|
|
33
33
|
|
|
34
34
|
export class SyncQueue {
|
|
35
35
|
private bodyFetches = new Map<string, BodyFetchKey>();
|
|
36
|
+
/** Per-draft serialization slot. Autosave fires every ~2 s but an IMAP
|
|
37
|
+
* search-delete-append cycle takes longer; unserialized pushes
|
|
38
|
+
* overlapped, each one's "find the stale copy" running before a
|
|
39
|
+
* sibling's "append" — so neither saw the other's new copy and the
|
|
40
|
+
* Drafts folder filled with dozens of duplicates (Bob 2026-05-19).
|
|
41
|
+
* One push in flight per draft; while it runs, only the LATEST queued
|
|
42
|
+
* raw message is kept (intermediate autosaves are superseded). */
|
|
43
|
+
private draftPushes = new Map<string, {
|
|
44
|
+
inFlight: boolean;
|
|
45
|
+
pending: { rawMessage: string; previousDraftUid?: number } | null;
|
|
46
|
+
}>();
|
|
36
47
|
/** Per-account debounce timers. Multiple rapid enqueues (3 flag toggles,
|
|
37
48
|
* bulk move/delete) coalesce into one processSyncActions drain after
|
|
38
49
|
* DRAIN_DEBOUNCE_MS. Was previously in ImapManager.debounceSyncActions;
|
|
@@ -97,10 +108,34 @@ export class SyncQueue {
|
|
|
97
108
|
* bytes. The reconciler retries on transient IMAP failure via the
|
|
98
109
|
* existing sync_actions backoff after first failure. */
|
|
99
110
|
enqueueDraftPush(accountId: string, rawMessage: string, previousDraftUid?: number, draftId?: string): void {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
111
|
+
const key = `${accountId}:${draftId || ""}`;
|
|
112
|
+
let slot = this.draftPushes.get(key);
|
|
113
|
+
if (slot && slot.inFlight) {
|
|
114
|
+
// A push is already running for this draft — supersede: keep only
|
|
115
|
+
// the newest content. The in-flight push finishes, then this runs.
|
|
116
|
+
slot.pending = { rawMessage, previousDraftUid };
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (!slot) { slot = { inFlight: false, pending: null }; this.draftPushes.set(key, slot); }
|
|
120
|
+
slot.inFlight = true;
|
|
121
|
+
const run = (raw: string, prevUid?: number): void => {
|
|
122
|
+
this.imapManager.saveDraft(accountId, raw, prevUid, draftId)
|
|
123
|
+
.catch((e: any) => {
|
|
124
|
+
console.error(` [sync-queue] draft push deferred (${draftId}): ${e?.message || e}`);
|
|
125
|
+
this.imapManager.emit("draftSaveDeferred", { accountId, draftId, error: String(e?.message || e) });
|
|
126
|
+
})
|
|
127
|
+
.finally(() => {
|
|
128
|
+
const next = slot!.pending;
|
|
129
|
+
slot!.pending = null;
|
|
130
|
+
if (next) {
|
|
131
|
+
run(next.rawMessage, next.previousDraftUid); // drain the coalesced save
|
|
132
|
+
} else {
|
|
133
|
+
slot!.inFlight = false;
|
|
134
|
+
this.draftPushes.delete(key);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
run(rawMessage, previousDraftUid);
|
|
104
139
|
}
|
|
105
140
|
|
|
106
141
|
/** Queue an outbox send. Today the outbox/<acct>/*.ltr directory IS
|