@bobfrankston/rmfmail 1.2.223 → 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.
Files changed (47) hide show
  1. package/client/android-bootstrap.bundle.js +38 -4
  2. package/client/android-bootstrap.bundle.js.map +2 -2
  3. package/client/app.bundle.js +81 -31
  4. package/client/app.bundle.js.map +2 -2
  5. package/client/components/address-book.js +93 -29
  6. package/client/components/address-book.js.map +1 -1
  7. package/client/components/address-book.ts +103 -24
  8. package/client/compose/compose.bundle.js +2 -2
  9. package/client/compose/compose.bundle.js.map +2 -2
  10. package/client/lib/api-client.js +2 -2
  11. package/client/lib/api-client.js.map +1 -1
  12. package/client/lib/api-client.ts +2 -2
  13. package/client/lib/mailxapi.js +2 -2
  14. package/client/styles/components.css +40 -0
  15. package/package.json +5 -5
  16. package/packages/mailx-imap/index.d.ts.map +1 -1
  17. package/packages/mailx-imap/index.js +32 -4
  18. package/packages/mailx-imap/index.js.map +1 -1
  19. package/packages/mailx-imap/index.ts +34 -5
  20. package/packages/mailx-imap/package-lock.json +2 -2
  21. package/packages/mailx-imap/package.json +1 -1
  22. package/packages/mailx-service/index.d.ts +1 -1
  23. package/packages/mailx-service/index.d.ts.map +1 -1
  24. package/packages/mailx-service/index.js +33 -4
  25. package/packages/mailx-service/index.js.map +1 -1
  26. package/packages/mailx-service/index.ts +28 -4
  27. package/packages/mailx-service/jsonrpc.js +1 -1
  28. package/packages/mailx-service/jsonrpc.js.map +1 -1
  29. package/packages/mailx-service/jsonrpc.ts +1 -1
  30. package/packages/mailx-service/package.json +1 -1
  31. package/packages/mailx-store/db.d.ts +30 -10
  32. package/packages/mailx-store/db.d.ts.map +1 -1
  33. package/packages/mailx-store/db.js +108 -10
  34. package/packages/mailx-store/db.js.map +1 -1
  35. package/packages/mailx-store/db.ts +126 -13
  36. package/packages/mailx-store/package.json +1 -1
  37. package/packages/mailx-store-web/android-bootstrap.d.ts.map +1 -1
  38. package/packages/mailx-store-web/android-bootstrap.js +17 -2
  39. package/packages/mailx-store-web/android-bootstrap.js.map +1 -1
  40. package/packages/mailx-store-web/android-bootstrap.ts +17 -2
  41. package/packages/mailx-store-web/db.d.ts +1 -1
  42. package/packages/mailx-store-web/db.d.ts.map +1 -1
  43. package/packages/mailx-store-web/db.js +17 -2
  44. package/packages/mailx-store-web/db.js.map +1 -1
  45. package/packages/mailx-store-web/db.ts +18 -3
  46. package/packages/mailx-store-web/package.json +1 -1
  47. /package/packages/mailx-imap/{node_modules.npmglobalize-stash-65000 → node_modules.npmglobalize-stash-142920}/.package-lock.json +0 -0
@@ -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);
@@ -1127,13 +1127,13 @@ async function openAddressBook(prefillSearch) {
1127
1127
  <span class="ab-actions"></span>
1128
1128
  </div>` + items.map((c) => `
1129
1129
  <div class="ab-row" data-email="${escapeAttr(c.email)}">
1130
- <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>
1131
1131
  <span class="ab-email">${escapeHtml(c.email)}</span>
1132
1132
  <span class="ab-source">${escapeHtml(c.source)}</span>
1133
1133
  <span class="ab-count-cell">${c.useCount || 0}</span>
1134
1134
  <span class="ab-last">${fmtDate(c.lastUsed)}</span>
1135
1135
  <span class="ab-actions">
1136
- <button type="button" class="ab-edit" title="Edit name">\u270E</button>
1136
+ <button type="button" class="ab-edit" title="Edit contact">\u270E</button>
1137
1137
  <button type="button" class="ab-del" title="Delete">\u{1F5D1}</button>
1138
1138
  <button type="button" class="ab-mail" title="Compose to">\u2709</button>
1139
1139
  </span>
@@ -1146,38 +1146,76 @@ async function openAddressBook(prefillSearch) {
1146
1146
  row.querySelector(".ab-mail")?.addEventListener("click", () => composeTo(c));
1147
1147
  });
1148
1148
  };
1149
- const beginEdit = (row, c) => {
1149
+ const beginEdit = (_row, c) => {
1150
1150
  if (editingEmail === c.email)
1151
1151
  return;
1152
1152
  editingEmail = c.email;
1153
- const nameSpan = row.querySelector(".ab-name");
1154
- const original = c.name || "";
1155
- nameSpan.innerHTML = `<input type="text" value="${escapeAttr(original)}" class="ab-name-input">`;
1156
- const inp = nameSpan.querySelector("input");
1157
- inp.focus();
1158
- inp.select();
1159
- const commit = async () => {
1160
- const newName = inp.value.trim();
1161
- if (newName !== original) {
1162
- try {
1163
- await upsertContact(newName, c.email);
1164
- c.name = newName;
1165
- } catch (e) {
1166
- alert(`Update failed: ${e?.message || e}`);
1167
- }
1168
- }
1169
- nameSpan.textContent = c.name || "";
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">&times;</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);
1170
1186
  editingEmail = null;
1171
1187
  };
1172
- inp.addEventListener("blur", commit);
1173
- inp.addEventListener("keydown", (e) => {
1174
- if (e.key === "Enter") {
1175
- e.preventDefault();
1176
- inp.blur();
1177
- } else if (e.key === "Escape") {
1188
+ const onCardKey = (e) => {
1189
+ if (e.key === "Escape") {
1190
+ e.stopPropagation();
1178
1191
  e.preventDefault();
1179
- inp.value = original;
1180
- inp.blur();
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}`);
1181
1219
  }
1182
1220
  });
1183
1221
  };
@@ -1262,17 +1300,29 @@ async function openAddressBook(prefillSearch) {
1262
1300
  });
1263
1301
  await reload();
1264
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
+ }
1265
1306
  function escapeHtml(s) {
1266
1307
  return s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
1267
1308
  }
1268
1309
  function escapeAttr(s) {
1269
1310
  return escapeHtml(s);
1270
1311
  }
1271
- var isOpen;
1312
+ var CONTACT_FIELDS, isOpen;
1272
1313
  var init_address_book = __esm({
1273
1314
  "client/components/address-book.js"() {
1274
1315
  "use strict";
1275
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
+ ];
1276
1326
  isOpen = false;
1277
1327
  }
1278
1328
  });