@bobfrankston/rmfmail 1.1.214 → 1.1.216

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.
@@ -1814,6 +1814,33 @@ async function showMessage(accountId, uid, folderId, specialUse, isRetry = false
1814
1814
  attEl.hidden = true;
1815
1815
  if (msg.attachments?.length) {
1816
1816
  attEl.hidden = false;
1817
+ const saveAttachmentAt = async (idx) => {
1818
+ const a0 = msg.attachments[idx];
1819
+ try {
1820
+ const data = await getAttachment(accountId, uid, idx, msg.folderId);
1821
+ const bytes = Uint8Array.from(atob(data.content), (c) => c.charCodeAt(0));
1822
+ const blob = new Blob([bytes], { type: data.contentType || "application/octet-stream" });
1823
+ const url = URL.createObjectURL(blob);
1824
+ const a = document.createElement("a");
1825
+ a.href = url;
1826
+ a.download = a0.filename || "attachment";
1827
+ a.style.display = "none";
1828
+ document.body.appendChild(a);
1829
+ a.click();
1830
+ setTimeout(() => {
1831
+ a.remove();
1832
+ URL.revokeObjectURL(url);
1833
+ }, 5e3);
1834
+ } catch (err) {
1835
+ window.dispatchEvent(new CustomEvent("mailx-alert", { detail: { message: `Couldn't save "${a0.filename}": ${err?.message || err}`, key: "attachment-save" } }));
1836
+ }
1837
+ };
1838
+ const saveAllAttachments = async () => {
1839
+ for (let k = 0; k < msg.attachments.length; k++) {
1840
+ await saveAttachmentAt(k);
1841
+ await new Promise((r) => setTimeout(r, 300));
1842
+ }
1843
+ };
1817
1844
  for (let i = 0; i < msg.attachments.length; i++) {
1818
1845
  const att = msg.attachments[i];
1819
1846
  const chip = document.createElement("button");
@@ -1885,6 +1912,22 @@ async function showMessage(accountId, uid, folderId, specialUse, isRetry = false
1885
1912
  console.error(`Attachment drag-out failed: ${err.message || err}`);
1886
1913
  }
1887
1914
  });
1915
+ chip.addEventListener("contextmenu", (e) => {
1916
+ e.preventDefault();
1917
+ e.stopPropagation();
1918
+ const items = [
1919
+ { label: "Open", action: () => chip.click() },
1920
+ { label: `Save "${att.filename}"\u2026`, action: () => {
1921
+ void saveAttachmentAt(i);
1922
+ } }
1923
+ ];
1924
+ if (msg.attachments.length > 1) {
1925
+ items.push({ label: `Save all (${msg.attachments.length})\u2026`, action: () => {
1926
+ void saveAllAttachments();
1927
+ } });
1928
+ }
1929
+ showContextMenu(e.clientX, e.clientY, items);
1930
+ });
1888
1931
  attEl.appendChild(chip);
1889
1932
  }
1890
1933
  }
@@ -1960,6 +2003,18 @@ async function showAddContactDialog(nameIn, emailIn) {
1960
2003
  dup = match;
1961
2004
  } catch {
1962
2005
  }
2006
+ let dupInfo = "";
2007
+ if (dup) {
2008
+ const nm = `<strong>${escapeText(dup.name || "(no name)")}</strong>`;
2009
+ if (dup.source === "discovered") {
2010
+ dupInfo = `<div class="mailx-modal-info">Found in <em>local discovered contacts</em> (auto-collected from your mail) as ${nm} \u2014 <strong>not yet in Google Contacts</strong>. Saving adds it to Google Contacts.</div>`;
2011
+ } else if (dup.source === "google") {
2012
+ dupInfo = `<div class="mailx-modal-info">Already in <strong>Google Contacts</strong> as ${nm}. Saving updates the name.</div>`;
2013
+ } else {
2014
+ dupInfo = `<div class="mailx-modal-info">Already saved as ${nm} (${escapeText(dup.source)}). Saving updates the name and syncs to Google Contacts.</div>`;
2015
+ }
2016
+ }
2017
+ const saveLabel = dup ? dup.source === "discovered" ? "Add to Google Contacts" : "Update" : "Save";
1963
2018
  const backdrop = document.createElement("div");
1964
2019
  backdrop.className = "mailx-modal-backdrop";
1965
2020
  const panel = document.createElement("div");
@@ -1969,7 +2024,7 @@ async function showAddContactDialog(nameIn, emailIn) {
1969
2024
  <span class="mailx-modal-title-text">${dup ? "Update contact" : "Add contact"}</span>
1970
2025
  <button type="button" class="mailx-modal-close" id="ac-close" aria-label="Close">&times;</button>
1971
2026
  </div>
1972
- ${dup ? `<div class="mailx-modal-info">Already in address book as <strong>${escapeText(dup.name || "(no name)")}</strong> (${escapeText(dup.source)}). Saving will update the name.</div>` : ""}
2027
+ ${dupInfo}
1973
2028
  <label class="mailx-modal-label">Name
1974
2029
  <input class="mailx-modal-input" id="ac-name" type="text" value="${escapeText(dup?.name || nameIn || "")}" autofocus>
1975
2030
  </label>
@@ -1982,7 +2037,7 @@ async function showAddContactDialog(nameIn, emailIn) {
1982
2037
  <div class="mailx-modal-buttons">
1983
2038
  <span class="mailx-modal-spacer"></span>
1984
2039
  <button type="button" class="mailx-modal-btn" data-action="cancel">Cancel</button>
1985
- <button type="button" class="mailx-modal-btn mailx-modal-btn-primary" data-action="save">${dup ? "Update" : "Save"}</button>
2040
+ <button type="button" class="mailx-modal-btn mailx-modal-btn-primary" data-action="save">${escapeText(saveLabel)}</button>
1986
2041
  </div>`;
1987
2042
  backdrop.appendChild(panel);
1988
2043
  document.body.appendChild(backdrop);
@@ -2003,7 +2058,7 @@ async function showAddContactDialog(nameIn, emailIn) {
2003
2058
  close();
2004
2059
  } catch (e) {
2005
2060
  btn.disabled = false;
2006
- btn.textContent = dup ? "Update" : "Save";
2061
+ btn.textContent = saveLabel;
2007
2062
  alert(`Couldn't save: ${e?.message || e}`);
2008
2063
  }
2009
2064
  });