@bobfrankston/rmfmail 1.1.248 → 1.1.250
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/app.bundle.js +149 -14
- package/client/app.bundle.js.map +3 -3
- package/client/app.js +155 -0
- package/client/app.js.map +1 -1
- package/client/app.ts +139 -0
- package/client/components/context-menu.js +3 -1
- package/client/components/context-menu.js.map +1 -1
- package/client/components/context-menu.ts +5 -1
- package/client/components/message-list.js +13 -5
- package/client/components/message-list.js.map +1 -1
- package/client/components/message-list.ts +14 -6
- package/client/compose/compose.bundle.js +7 -1
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/lib/api-client.js +6 -0
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +7 -0
- package/client/lib/message-state.js +23 -8
- package/client/lib/message-state.js.map +1 -1
- package/client/lib/message-state.ts +24 -10
- package/package.json +3 -3
- package/packages/mailx-imap/index.d.ts.map +1 -1
- package/packages/mailx-imap/index.js +24 -0
- package/packages/mailx-imap/index.js.map +1 -1
- package/packages/mailx-imap/index.ts +25 -0
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-service/index.d.ts +7 -0
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +15 -0
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +15 -0
- package/packages/mailx-service/jsonrpc.js +3 -0
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +3 -0
- package/packages/mailx-service/sync-queue.d.ts +5 -0
- package/packages/mailx-service/sync-queue.d.ts.map +1 -1
- package/packages/mailx-service/sync-queue.js +8 -0
- package/packages/mailx-service/sync-queue.js.map +1 -1
- package/packages/mailx-service/sync-queue.ts +9 -0
package/client/app.bundle.js
CHANGED
|
@@ -26,6 +26,7 @@ __export(api_client_exports, {
|
|
|
26
26
|
connectEvents: () => connectEvents,
|
|
27
27
|
connectWebSocket: () => connectWebSocket,
|
|
28
28
|
consumePendingMailto: () => consumePendingMailto,
|
|
29
|
+
copyMessages: () => copyMessages,
|
|
29
30
|
createCalendarEvent: () => createCalendarEvent,
|
|
30
31
|
createFolder: () => createFolder,
|
|
31
32
|
createTask: () => createTask,
|
|
@@ -345,6 +346,9 @@ function moveMessages(accountId, uids, targetFolderId, targetAccountId) {
|
|
|
345
346
|
function markAsSpamMessages(accountId, uids) {
|
|
346
347
|
return ipc().markAsSpamMessages?.(accountId, uids);
|
|
347
348
|
}
|
|
349
|
+
function copyMessages(accountId, uids, folderIds, targetFolderId) {
|
|
350
|
+
return ipc().copyMessages?.(accountId, uids, folderIds, targetFolderId);
|
|
351
|
+
}
|
|
348
352
|
function undeleteMessage(accountId, uid, folderId) {
|
|
349
353
|
return ipc().undeleteMessage?.(accountId, uid, folderId);
|
|
350
354
|
}
|
|
@@ -652,7 +656,9 @@ function showContextMenu(x, y, items) {
|
|
|
652
656
|
continue;
|
|
653
657
|
}
|
|
654
658
|
const el = document.createElement("div");
|
|
655
|
-
el.className = "ctx-item" + (item.disabled ? " ctx-disabled" : "");
|
|
659
|
+
el.className = "ctx-item" + (item.disabled ? " ctx-disabled" : "") + (item.emphasized ? " ctx-emphasized" : "");
|
|
660
|
+
if (item.emphasized)
|
|
661
|
+
el.style.fontWeight = "600";
|
|
656
662
|
el.textContent = item.label;
|
|
657
663
|
if (item.tooltip)
|
|
658
664
|
el.title = item.tooltip;
|
|
@@ -740,25 +746,32 @@ function getMessages2() {
|
|
|
740
746
|
return messages;
|
|
741
747
|
}
|
|
742
748
|
function removeMessages(uids, currentlyFocused) {
|
|
743
|
-
const
|
|
744
|
-
const
|
|
745
|
-
const
|
|
749
|
+
const fullSet = /* @__PURE__ */ new Set();
|
|
750
|
+
const looseSet = /* @__PURE__ */ new Set();
|
|
751
|
+
for (const u of uids) {
|
|
752
|
+
if (u.folderId != null)
|
|
753
|
+
fullSet.add(`${u.accountId}:${u.folderId}:${u.uid}`);
|
|
754
|
+
else
|
|
755
|
+
looseSet.add(`${u.accountId}:${u.uid}`);
|
|
756
|
+
}
|
|
757
|
+
const isRemoved = (m) => m.folderId != null && fullSet.has(`${m.accountId}:${m.folderId}:${m.uid}`) || looseSet.has(`${m.accountId}:${m.uid}`);
|
|
758
|
+
const focusedWasRemoved = currentlyFocused !== null && isRemoved(currentlyFocused);
|
|
746
759
|
let nextSurvivor = null;
|
|
747
760
|
if (focusedWasRemoved) {
|
|
748
761
|
let lastRemovedIdx = -1;
|
|
749
762
|
for (let i = 0; i < messages.length; i++) {
|
|
750
|
-
if (
|
|
763
|
+
if (isRemoved(messages[i]))
|
|
751
764
|
lastRemovedIdx = i;
|
|
752
765
|
}
|
|
753
766
|
for (let i = lastRemovedIdx + 1; i < messages.length; i++) {
|
|
754
|
-
if (!
|
|
767
|
+
if (!isRemoved(messages[i])) {
|
|
755
768
|
nextSurvivor = messages[i];
|
|
756
769
|
break;
|
|
757
770
|
}
|
|
758
771
|
}
|
|
759
772
|
if (!nextSurvivor) {
|
|
760
773
|
for (let i = lastRemovedIdx - 1; i >= 0; i--) {
|
|
761
|
-
if (!
|
|
774
|
+
if (!isRemoved(messages[i])) {
|
|
762
775
|
nextSurvivor = messages[i];
|
|
763
776
|
break;
|
|
764
777
|
}
|
|
@@ -767,11 +780,11 @@ function removeMessages(uids, currentlyFocused) {
|
|
|
767
780
|
}
|
|
768
781
|
const removedIds = /* @__PURE__ */ new Set();
|
|
769
782
|
for (const m of messages) {
|
|
770
|
-
if (
|
|
783
|
+
if (isRemoved(m) && m.messageId) {
|
|
771
784
|
removedIds.add(m.messageId);
|
|
772
785
|
}
|
|
773
786
|
}
|
|
774
|
-
messages = messages.filter((m) => !
|
|
787
|
+
messages = messages.filter((m) => !isRemoved(m));
|
|
775
788
|
if (removedIds.size > 0) {
|
|
776
789
|
for (const m of messages) {
|
|
777
790
|
if (m.messageId && removedIds.has(m.messageId) && typeof m.dupeCount === "number") {
|
|
@@ -3155,18 +3168,18 @@ function updateSortIndicators() {
|
|
|
3155
3168
|
});
|
|
3156
3169
|
}
|
|
3157
3170
|
function removeMessagesAndReconcile(uids) {
|
|
3158
|
-
const focusedIdent = focusedRow ? { accountId: focusedRow.accountId, uid: focusedRow.msg.uid } : null;
|
|
3171
|
+
const focusedIdent = focusedRow ? { accountId: focusedRow.accountId, uid: focusedRow.msg.uid, folderId: focusedRow.msg.folderId } : null;
|
|
3159
3172
|
const outcome = removeMessages(uids, focusedIdent);
|
|
3160
3173
|
listCache.clear();
|
|
3161
3174
|
const body = document.getElementById("ml-body");
|
|
3162
3175
|
if (body) {
|
|
3163
|
-
const stateUids = new Set(getMessages2().map((m) => `${m.accountId}:${m.uid}`));
|
|
3176
|
+
const stateUids = new Set(getMessages2().map((m) => `${m.accountId}:${m.folderId}:${m.uid}`));
|
|
3164
3177
|
for (const row of Array.from(body.querySelectorAll(".ml-row"))) {
|
|
3165
3178
|
const el = row;
|
|
3166
|
-
const key = `${el.dataset.accountId}:${el.dataset.uid}`;
|
|
3179
|
+
const key = `${el.dataset.accountId}:${el.dataset.folderId}:${el.dataset.uid}`;
|
|
3167
3180
|
if (!stateUids.has(key)) {
|
|
3168
|
-
const dead = rowByKey.get(
|
|
3169
|
-
if (dead)
|
|
3181
|
+
const dead = rowByKey.get(rowKey(el.dataset.accountId || "", Number(el.dataset.uid)));
|
|
3182
|
+
if (dead && dead.el === el)
|
|
3170
3183
|
dead.detach();
|
|
3171
3184
|
else
|
|
3172
3185
|
el.remove();
|
|
@@ -8106,6 +8119,128 @@ async function performUndo() {
|
|
|
8106
8119
|
document.addEventListener("mailx-moved", (e) => {
|
|
8107
8120
|
pushUndo({ kind: "move", at: Date.now(), payload: e.detail });
|
|
8108
8121
|
});
|
|
8122
|
+
(() => {
|
|
8123
|
+
const SLOP = 6;
|
|
8124
|
+
let cand = null;
|
|
8125
|
+
let active = false;
|
|
8126
|
+
let ghost = null;
|
|
8127
|
+
let suppressContext = false;
|
|
8128
|
+
const style = document.createElement("style");
|
|
8129
|
+
style.textContent = "body.rmf-rdrag, body.rmf-rdrag * { cursor: grabbing !important; }.rmf-rdrag-ghost { position: fixed; z-index: 100000; pointer-events: none; background: var(--color-accent, #1a6dd4); color: #fff; padding: 2px 9px; border-radius: 4px; font: 12px system-ui; box-shadow: 0 2px 8px rgba(0,0,0,.3); }.ft-folder.rmf-rdrag-over { outline: 2px solid var(--color-accent, #1a6dd4); outline-offset: -2px; border-radius: 4px; }";
|
|
8130
|
+
document.head.appendChild(style);
|
|
8131
|
+
const folderAt = (x, y) => document.elementFromPoint(x, y)?.closest?.(".ft-folder") || null;
|
|
8132
|
+
const clearOver = () => document.querySelectorAll(".ft-folder.rmf-rdrag-over").forEach((el) => el.classList.remove("rmf-rdrag-over"));
|
|
8133
|
+
const cleanup = () => {
|
|
8134
|
+
active = false;
|
|
8135
|
+
cand = null;
|
|
8136
|
+
document.body.classList.remove("rmf-rdrag");
|
|
8137
|
+
ghost?.remove();
|
|
8138
|
+
ghost = null;
|
|
8139
|
+
clearOver();
|
|
8140
|
+
};
|
|
8141
|
+
document.addEventListener("mousedown", (e) => {
|
|
8142
|
+
if (e.button !== 2) return;
|
|
8143
|
+
const row = e.target?.closest?.(".ml-row");
|
|
8144
|
+
if (!row) return;
|
|
8145
|
+
const pressed = {
|
|
8146
|
+
accountId: row.dataset.accountId || "",
|
|
8147
|
+
uid: Number(row.dataset.uid),
|
|
8148
|
+
folderId: Number(row.dataset.folderId)
|
|
8149
|
+
};
|
|
8150
|
+
if (!pressed.uid) return;
|
|
8151
|
+
let msgs = getSelectedMessages();
|
|
8152
|
+
if (!msgs.some((m) => m.accountId === pressed.accountId && m.uid === pressed.uid)) msgs = [pressed];
|
|
8153
|
+
cand = { msgs, x: e.clientX, y: e.clientY };
|
|
8154
|
+
active = false;
|
|
8155
|
+
}, true);
|
|
8156
|
+
document.addEventListener("mousemove", (e) => {
|
|
8157
|
+
if (!cand) return;
|
|
8158
|
+
if (!active) {
|
|
8159
|
+
if (Math.abs(e.clientX - cand.x) < SLOP && Math.abs(e.clientY - cand.y) < SLOP) return;
|
|
8160
|
+
active = true;
|
|
8161
|
+
document.body.classList.add("rmf-rdrag");
|
|
8162
|
+
ghost = document.createElement("div");
|
|
8163
|
+
ghost.className = "rmf-rdrag-ghost";
|
|
8164
|
+
ghost.textContent = cand.msgs.length === 1 ? "1 message" : `${cand.msgs.length} messages`;
|
|
8165
|
+
document.body.appendChild(ghost);
|
|
8166
|
+
}
|
|
8167
|
+
if (ghost) {
|
|
8168
|
+
ghost.style.left = `${e.clientX + 14}px`;
|
|
8169
|
+
ghost.style.top = `${e.clientY + 8}px`;
|
|
8170
|
+
}
|
|
8171
|
+
clearOver();
|
|
8172
|
+
folderAt(e.clientX, e.clientY)?.classList.add("rmf-rdrag-over");
|
|
8173
|
+
}, true);
|
|
8174
|
+
document.addEventListener("mouseup", (e) => {
|
|
8175
|
+
if (!cand) return;
|
|
8176
|
+
const wasActive = active;
|
|
8177
|
+
const msgs = cand.msgs;
|
|
8178
|
+
const folder = wasActive ? folderAt(e.clientX, e.clientY) : null;
|
|
8179
|
+
const mx = e.clientX, my = e.clientY;
|
|
8180
|
+
cleanup();
|
|
8181
|
+
if (!wasActive) return;
|
|
8182
|
+
suppressContext = true;
|
|
8183
|
+
if (!folder) return;
|
|
8184
|
+
e.preventDefault();
|
|
8185
|
+
e.stopPropagation();
|
|
8186
|
+
const targetAccount = folder.dataset.accountId || "";
|
|
8187
|
+
const targetFolderId = Number(folder.dataset.folderId);
|
|
8188
|
+
const targetName = folder.querySelector(".ft-folder-name")?.textContent?.trim() || "folder";
|
|
8189
|
+
void showRightDragMenu(mx, my, msgs, targetAccount, targetFolderId, targetName);
|
|
8190
|
+
}, true);
|
|
8191
|
+
document.addEventListener("contextmenu", (e) => {
|
|
8192
|
+
if (suppressContext) {
|
|
8193
|
+
e.preventDefault();
|
|
8194
|
+
e.stopPropagation();
|
|
8195
|
+
suppressContext = false;
|
|
8196
|
+
}
|
|
8197
|
+
}, true);
|
|
8198
|
+
async function showRightDragMenu(x, y, msgs, targetAccount, targetFolderId, targetName) {
|
|
8199
|
+
const { showContextMenu: showContextMenu2 } = await Promise.resolve().then(() => (init_context_menu(), context_menu_exports));
|
|
8200
|
+
const n = msgs.length;
|
|
8201
|
+
const label = n === 1 ? "message" : `${n} messages`;
|
|
8202
|
+
showContextMenu2(x, y, [
|
|
8203
|
+
{ label: `Move ${label} here`, emphasized: true, action: () => void runRightDragOp(msgs, targetAccount, targetFolderId, targetName, "move") },
|
|
8204
|
+
{ label: `Copy ${label} here`, action: () => void runRightDragOp(msgs, targetAccount, targetFolderId, targetName, "copy") }
|
|
8205
|
+
]);
|
|
8206
|
+
}
|
|
8207
|
+
async function runRightDragOp(msgs, targetAccount, targetFolderId, targetName, op) {
|
|
8208
|
+
const status = document.getElementById("status-sync");
|
|
8209
|
+
const api = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
8210
|
+
const byAccount = /* @__PURE__ */ new Map();
|
|
8211
|
+
for (const m of msgs) {
|
|
8212
|
+
const g = byAccount.get(m.accountId) || { uids: [], folderIds: [] };
|
|
8213
|
+
g.uids.push(m.uid);
|
|
8214
|
+
g.folderIds.push(m.folderId);
|
|
8215
|
+
byAccount.set(m.accountId, g);
|
|
8216
|
+
}
|
|
8217
|
+
if (op === "move") {
|
|
8218
|
+
removeMessagesAndReconcile(msgs);
|
|
8219
|
+
document.dispatchEvent(new CustomEvent("mailx-moved", {
|
|
8220
|
+
detail: { messages: msgs.map((m) => ({ accountId: m.accountId, uid: m.uid, sourceFolderId: m.folderId })) }
|
|
8221
|
+
}));
|
|
8222
|
+
for (const [src, g] of byAccount) {
|
|
8223
|
+
const targetAcct = src === targetAccount ? void 0 : targetAccount;
|
|
8224
|
+
api.moveMessages(src, g.uids, targetFolderId, targetAcct)?.catch?.((err) => {
|
|
8225
|
+
if (status) status.textContent = `Move failed: ${err?.message || err}`;
|
|
8226
|
+
});
|
|
8227
|
+
}
|
|
8228
|
+
if (status) status.textContent = `Moved ${msgs.length} to ${targetName} \u2014 Ctrl+Z to undo`;
|
|
8229
|
+
} else {
|
|
8230
|
+
let skippedXacct = false;
|
|
8231
|
+
for (const [src, g] of byAccount) {
|
|
8232
|
+
if (src !== targetAccount) {
|
|
8233
|
+
skippedXacct = true;
|
|
8234
|
+
continue;
|
|
8235
|
+
}
|
|
8236
|
+
api.copyMessages(src, g.uids, g.folderIds, targetFolderId)?.catch?.((err) => {
|
|
8237
|
+
if (status) status.textContent = `Copy failed: ${err?.message || err}`;
|
|
8238
|
+
});
|
|
8239
|
+
}
|
|
8240
|
+
if (status) status.textContent = skippedXacct ? `Copy across accounts isn't supported yet` : `Copying ${msgs.length} to ${targetName}\u2026`;
|
|
8241
|
+
}
|
|
8242
|
+
}
|
|
8243
|
+
})();
|
|
8109
8244
|
document.getElementById("btn-delete")?.addEventListener("click", deleteSelection);
|
|
8110
8245
|
document.getElementById("btn-tb-delete")?.addEventListener("click", deleteSelection);
|
|
8111
8246
|
document.getElementById("btn-tb-spam")?.addEventListener("click", spamSelectedMessages);
|