@bobfrankston/rmfmail 1.2.222 → 1.2.224
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/bin/mailx.js +6 -9
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +6 -9
- package/client/android-bootstrap.bundle.js +38 -4
- package/client/android-bootstrap.bundle.js.map +2 -2
- package/client/app.bundle.js +149 -32
- package/client/app.bundle.js.map +2 -2
- package/client/components/address-book.js +93 -29
- package/client/components/address-book.js.map +1 -1
- package/client/components/address-book.ts +103 -24
- package/client/components/context-menu.js +76 -0
- package/client/components/context-menu.js.map +1 -1
- package/client/components/context-menu.ts +77 -0
- package/client/compose/compose.bundle.js +276 -116
- package/client/compose/compose.bundle.js.map +4 -4
- package/client/compose/compose.js +4 -149
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +4 -104
- package/client/compose/edit-commands.js +207 -0
- package/client/compose/edit-commands.js.map +1 -0
- package/client/compose/edit-commands.ts +181 -0
- package/client/compose/editor.js +6 -0
- package/client/compose/editor.js.map +1 -1
- package/client/compose/editor.ts +6 -0
- package/client/compose/spellcheck.js +19 -0
- package/client/compose/spellcheck.js.map +1 -1
- package/client/compose/spellcheck.ts +11 -0
- package/client/lib/api-client.js +2 -2
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +2 -2
- package/client/lib/mailxapi.js +2 -2
- package/client/styles/components.css +40 -0
- package/package.json +7 -7
- package/packages/mailx-imap/index.d.ts.map +1 -1
- package/packages/mailx-imap/index.js +32 -4
- package/packages/mailx-imap/index.js.map +1 -1
- package/packages/mailx-imap/index.ts +34 -5
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-service/index.d.ts +1 -1
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +33 -4
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +28 -4
- package/packages/mailx-service/jsonrpc.js +1 -1
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +1 -1
- package/packages/mailx-service/package.json +1 -1
- package/packages/mailx-store/db.d.ts +30 -10
- package/packages/mailx-store/db.d.ts.map +1 -1
- package/packages/mailx-store/db.js +108 -10
- package/packages/mailx-store/db.js.map +1 -1
- package/packages/mailx-store/db.ts +126 -13
- package/packages/mailx-store/package.json +1 -1
- package/packages/mailx-store-web/android-bootstrap.d.ts.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.js +17 -2
- package/packages/mailx-store-web/android-bootstrap.js.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.ts +17 -2
- package/packages/mailx-store-web/db.d.ts +1 -1
- package/packages/mailx-store-web/db.d.ts.map +1 -1
- package/packages/mailx-store-web/db.js +17 -2
- package/packages/mailx-store-web/db.js.map +1 -1
- package/packages/mailx-store-web/db.ts +18 -3
- package/packages/mailx-store-web/package.json +1 -1
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-104584 → node_modules.npmglobalize-stash-142920}/.package-lock.json +0 -0
package/client/app.bundle.js
CHANGED
|
@@ -306,8 +306,8 @@ function hasBccHistoryTo(email) {
|
|
|
306
306
|
function listContacts(query, page = 1, pageSize = 100) {
|
|
307
307
|
return ipc().listContacts(query, page, pageSize);
|
|
308
308
|
}
|
|
309
|
-
function upsertContact(name, email) {
|
|
310
|
-
return ipc().upsertContact(name, email);
|
|
309
|
+
function upsertContact(name, email, fields) {
|
|
310
|
+
return ipc().upsertContact(name, email, fields);
|
|
311
311
|
}
|
|
312
312
|
function deleteContact(email) {
|
|
313
313
|
return ipc().deleteContact(email);
|
|
@@ -723,7 +723,14 @@ __export(context_menu_exports, {
|
|
|
723
723
|
closeContextMenu: () => closeContextMenu,
|
|
724
724
|
showContextMenu: () => showContextMenu
|
|
725
725
|
});
|
|
726
|
+
function closeSubmenu() {
|
|
727
|
+
if (activeSubmenu) {
|
|
728
|
+
activeSubmenu.remove();
|
|
729
|
+
activeSubmenu = null;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
726
732
|
function closeContextMenu() {
|
|
733
|
+
closeSubmenu();
|
|
727
734
|
if (activeMenu) {
|
|
728
735
|
activeMenu.remove();
|
|
729
736
|
activeMenu = null;
|
|
@@ -737,6 +744,45 @@ function closeContextMenu() {
|
|
|
737
744
|
escapeListener = null;
|
|
738
745
|
}
|
|
739
746
|
}
|
|
747
|
+
function openSubmenu(parentRow, items) {
|
|
748
|
+
closeSubmenu();
|
|
749
|
+
const sub = document.createElement("div");
|
|
750
|
+
sub.className = "ctx-menu";
|
|
751
|
+
for (const item of items) {
|
|
752
|
+
if (item.separator) {
|
|
753
|
+
const sep = document.createElement("div");
|
|
754
|
+
sep.className = "ctx-sep";
|
|
755
|
+
sub.appendChild(sep);
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
const el = document.createElement("div");
|
|
759
|
+
el.className = "ctx-item" + (item.disabled ? " ctx-disabled" : "");
|
|
760
|
+
el.textContent = item.label;
|
|
761
|
+
if (item.tooltip)
|
|
762
|
+
el.title = item.tooltip;
|
|
763
|
+
if (!item.disabled) {
|
|
764
|
+
el.addEventListener("click", () => {
|
|
765
|
+
closeContextMenu();
|
|
766
|
+
item.action();
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
sub.appendChild(el);
|
|
770
|
+
}
|
|
771
|
+
document.body.appendChild(sub);
|
|
772
|
+
const anchor = parentRow.getBoundingClientRect();
|
|
773
|
+
const vw = window.visualViewport?.width ?? window.innerWidth;
|
|
774
|
+
const vh = window.visualViewport?.height ?? window.innerHeight;
|
|
775
|
+
const rect = sub.getBoundingClientRect();
|
|
776
|
+
let left = anchor.right - 2;
|
|
777
|
+
if (left + rect.width > vw)
|
|
778
|
+
left = anchor.left - rect.width + 2;
|
|
779
|
+
let top = anchor.top;
|
|
780
|
+
if (top + rect.height > vh)
|
|
781
|
+
top = vh - rect.height - 4;
|
|
782
|
+
sub.style.left = `${Math.max(4, left)}px`;
|
|
783
|
+
sub.style.top = `${Math.max(4, top)}px`;
|
|
784
|
+
activeSubmenu = sub;
|
|
785
|
+
}
|
|
740
786
|
function showContextMenu(x, y, items) {
|
|
741
787
|
closeContextMenu();
|
|
742
788
|
const menu = document.createElement("div");
|
|
@@ -755,6 +801,24 @@ function showContextMenu(x, y, items) {
|
|
|
755
801
|
el.textContent = item.label;
|
|
756
802
|
if (item.tooltip)
|
|
757
803
|
el.title = item.tooltip;
|
|
804
|
+
if (item.submenu && item.submenu.length > 0 && !item.disabled) {
|
|
805
|
+
el.style.display = "flex";
|
|
806
|
+
el.style.justifyContent = "space-between";
|
|
807
|
+
el.style.gap = "12px";
|
|
808
|
+
const caret = document.createElement("span");
|
|
809
|
+
caret.textContent = "\u25B8";
|
|
810
|
+
caret.style.opacity = "0.6";
|
|
811
|
+
el.appendChild(caret);
|
|
812
|
+
const open = () => openSubmenu(el, item.submenu);
|
|
813
|
+
el.addEventListener("mouseenter", open);
|
|
814
|
+
el.addEventListener("click", (e) => {
|
|
815
|
+
e.stopPropagation();
|
|
816
|
+
open();
|
|
817
|
+
});
|
|
818
|
+
menu.appendChild(el);
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
el.addEventListener("mouseenter", closeSubmenu);
|
|
758
822
|
if (!item.disabled) {
|
|
759
823
|
el.addEventListener("click", () => {
|
|
760
824
|
closeContextMenu();
|
|
@@ -786,6 +850,8 @@ function showContextMenu(x, y, items) {
|
|
|
786
850
|
activeMenu = menu;
|
|
787
851
|
requestAnimationFrame(() => {
|
|
788
852
|
dismissListener = (e) => {
|
|
853
|
+
if (activeSubmenu?.contains(e.target))
|
|
854
|
+
return;
|
|
789
855
|
if (activeMenu && !activeMenu.contains(e.target)) {
|
|
790
856
|
closeContextMenu();
|
|
791
857
|
}
|
|
@@ -801,13 +867,14 @@ function showContextMenu(x, y, items) {
|
|
|
801
867
|
document.addEventListener("keydown", escapeListener, true);
|
|
802
868
|
});
|
|
803
869
|
}
|
|
804
|
-
var activeMenu, dismissListener, escapeListener;
|
|
870
|
+
var activeMenu, dismissListener, escapeListener, activeSubmenu;
|
|
805
871
|
var init_context_menu = __esm({
|
|
806
872
|
"client/components/context-menu.js"() {
|
|
807
873
|
"use strict";
|
|
808
874
|
activeMenu = null;
|
|
809
875
|
dismissListener = null;
|
|
810
876
|
escapeListener = null;
|
|
877
|
+
activeSubmenu = null;
|
|
811
878
|
document.addEventListener("scroll", closeContextMenu, true);
|
|
812
879
|
document.addEventListener("contextmenu", () => {
|
|
813
880
|
});
|
|
@@ -1060,13 +1127,13 @@ async function openAddressBook(prefillSearch) {
|
|
|
1060
1127
|
<span class="ab-actions"></span>
|
|
1061
1128
|
</div>` + items.map((c) => `
|
|
1062
1129
|
<div class="ab-row" data-email="${escapeAttr(c.email)}">
|
|
1063
|
-
<span class="ab-name">${escapeHtml(c.name || "")}</span>
|
|
1130
|
+
<span class="ab-name" title="${escapeAttr(cardSummary(c))}">${escapeHtml(c.name || "")}${cardSummary(c) ? ' <span class="ab-hascard" aria-hidden="true">\u2022</span>' : ""}</span>
|
|
1064
1131
|
<span class="ab-email">${escapeHtml(c.email)}</span>
|
|
1065
1132
|
<span class="ab-source">${escapeHtml(c.source)}</span>
|
|
1066
1133
|
<span class="ab-count-cell">${c.useCount || 0}</span>
|
|
1067
1134
|
<span class="ab-last">${fmtDate(c.lastUsed)}</span>
|
|
1068
1135
|
<span class="ab-actions">
|
|
1069
|
-
<button type="button" class="ab-edit" title="Edit
|
|
1136
|
+
<button type="button" class="ab-edit" title="Edit contact">\u270E</button>
|
|
1070
1137
|
<button type="button" class="ab-del" title="Delete">\u{1F5D1}</button>
|
|
1071
1138
|
<button type="button" class="ab-mail" title="Compose to">\u2709</button>
|
|
1072
1139
|
</span>
|
|
@@ -1079,38 +1146,76 @@ async function openAddressBook(prefillSearch) {
|
|
|
1079
1146
|
row.querySelector(".ab-mail")?.addEventListener("click", () => composeTo(c));
|
|
1080
1147
|
});
|
|
1081
1148
|
};
|
|
1082
|
-
const beginEdit = (
|
|
1149
|
+
const beginEdit = (_row, c) => {
|
|
1083
1150
|
if (editingEmail === c.email)
|
|
1084
1151
|
return;
|
|
1085
1152
|
editingEmail = c.email;
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1153
|
+
const back = document.createElement("div");
|
|
1154
|
+
back.className = "mailx-modal-backdrop";
|
|
1155
|
+
const card = document.createElement("div");
|
|
1156
|
+
card.className = "mailx-modal";
|
|
1157
|
+
card.innerHTML = `
|
|
1158
|
+
<div class="mailx-modal-title">
|
|
1159
|
+
<span class="mailx-modal-title-text">Edit contact</span>
|
|
1160
|
+
<button type="button" class="mailx-modal-close" data-role="x" title="Close (Esc)" aria-label="Close">×</button>
|
|
1161
|
+
</div>
|
|
1162
|
+
<div class="ab-card">
|
|
1163
|
+
<label class="ab-field"><span>Name</span>
|
|
1164
|
+
<input type="text" class="mailx-modal-input" data-field="name" value="${escapeAttr(c.name || "")}"></label>
|
|
1165
|
+
<label class="ab-field"><span>Email</span>
|
|
1166
|
+
<input type="email" class="mailx-modal-input" value="${escapeAttr(c.email)}" disabled
|
|
1167
|
+
title="The address identifies the contact \u2014 delete and re-add to change it"></label>
|
|
1168
|
+
${CONTACT_FIELDS.map((f) => `
|
|
1169
|
+
<label class="ab-field"><span>${escapeHtml(f.label)}</span>
|
|
1170
|
+
${f.multiline ? `<textarea class="mailx-modal-input" rows="2" data-field="${f.key}">${escapeHtml(c[f.key] || "")}</textarea>` : `<input type="${f.type || "text"}" class="mailx-modal-input" data-field="${f.key}"
|
|
1171
|
+
value="${escapeAttr(c[f.key] || "")}"
|
|
1172
|
+
${f.placeholder ? `placeholder="${escapeAttr(f.placeholder)}"` : ""}>`}
|
|
1173
|
+
</label>`).join("")}
|
|
1174
|
+
</div>
|
|
1175
|
+
<div class="mailx-modal-buttons">
|
|
1176
|
+
<span class="mailx-modal-spacer"></span>
|
|
1177
|
+
<button type="button" class="mailx-modal-btn" data-role="cancel">Cancel</button>
|
|
1178
|
+
<button type="button" class="mailx-modal-btn mailx-modal-btn-primary" data-role="save">Save</button>
|
|
1179
|
+
</div>`;
|
|
1180
|
+
back.appendChild(card);
|
|
1181
|
+
document.body.appendChild(back);
|
|
1182
|
+
card.querySelector('[data-field="name"]')?.focus();
|
|
1183
|
+
const shut = () => {
|
|
1184
|
+
back.remove();
|
|
1185
|
+
document.removeEventListener("keydown", onCardKey, true);
|
|
1103
1186
|
editingEmail = null;
|
|
1104
1187
|
};
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
e.preventDefault();
|
|
1109
|
-
inp.blur();
|
|
1110
|
-
} else if (e.key === "Escape") {
|
|
1188
|
+
const onCardKey = (e) => {
|
|
1189
|
+
if (e.key === "Escape") {
|
|
1190
|
+
e.stopPropagation();
|
|
1111
1191
|
e.preventDefault();
|
|
1112
|
-
|
|
1113
|
-
|
|
1192
|
+
shut();
|
|
1193
|
+
}
|
|
1194
|
+
};
|
|
1195
|
+
document.addEventListener("keydown", onCardKey, true);
|
|
1196
|
+
card.querySelector('[data-role="x"]')?.addEventListener("click", shut);
|
|
1197
|
+
card.querySelector('[data-role="cancel"]')?.addEventListener("click", shut);
|
|
1198
|
+
back.addEventListener("mousedown", (e) => {
|
|
1199
|
+
if (e.target === back)
|
|
1200
|
+
shut();
|
|
1201
|
+
});
|
|
1202
|
+
card.querySelector('[data-role="save"]')?.addEventListener("click", async () => {
|
|
1203
|
+
const val = (k) => card.querySelector(`[data-field="${k}"]`)?.value.trim() ?? "";
|
|
1204
|
+
const fields = {};
|
|
1205
|
+
for (const f of CONTACT_FIELDS)
|
|
1206
|
+
fields[f.key] = val(f.key);
|
|
1207
|
+
const newName = val("name");
|
|
1208
|
+
const bd = fields.birthday;
|
|
1209
|
+
if (bd && !/^(\d{4}|-)-\d{2}-\d{2}$/.test(bd)) {
|
|
1210
|
+
alert(`Birthday must be YYYY-MM-DD, or --MM-DD when the year is unknown. Got "${bd}".`);
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
try {
|
|
1214
|
+
await upsertContact(newName, c.email, fields);
|
|
1215
|
+
shut();
|
|
1216
|
+
await reload();
|
|
1217
|
+
} catch (e) {
|
|
1218
|
+
alert(`Update failed: ${e?.message || e}`);
|
|
1114
1219
|
}
|
|
1115
1220
|
});
|
|
1116
1221
|
};
|
|
@@ -1195,17 +1300,29 @@ async function openAddressBook(prefillSearch) {
|
|
|
1195
1300
|
});
|
|
1196
1301
|
await reload();
|
|
1197
1302
|
}
|
|
1303
|
+
function cardSummary(c) {
|
|
1304
|
+
return CONTACT_FIELDS.map((f) => ({ label: f.label, value: (c[f.key] || "").trim() })).filter((x) => x.value).map((x) => `${x.label}: ${x.value.replace(/\s+/g, " ")}`).join("\n");
|
|
1305
|
+
}
|
|
1198
1306
|
function escapeHtml(s) {
|
|
1199
1307
|
return s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
1200
1308
|
}
|
|
1201
1309
|
function escapeAttr(s) {
|
|
1202
1310
|
return escapeHtml(s);
|
|
1203
1311
|
}
|
|
1204
|
-
var isOpen;
|
|
1312
|
+
var CONTACT_FIELDS, isOpen;
|
|
1205
1313
|
var init_address_book = __esm({
|
|
1206
1314
|
"client/components/address-book.js"() {
|
|
1207
1315
|
"use strict";
|
|
1208
1316
|
init_api_client();
|
|
1317
|
+
CONTACT_FIELDS = [
|
|
1318
|
+
{ key: "organization", label: "Company" },
|
|
1319
|
+
{ key: "title", label: "Job title" },
|
|
1320
|
+
{ key: "phone", label: "Phone", type: "tel" },
|
|
1321
|
+
{ key: "website", label: "Website", type: "url", placeholder: "https://" },
|
|
1322
|
+
{ key: "birthday", label: "Birthday", placeholder: "YYYY-MM-DD or --MM-DD" },
|
|
1323
|
+
{ key: "address", label: "Address", multiline: true },
|
|
1324
|
+
{ key: "notes", label: "Notes", multiline: true }
|
|
1325
|
+
];
|
|
1209
1326
|
isOpen = false;
|
|
1210
1327
|
}
|
|
1211
1328
|
});
|