Add mail context menu
This commit is contained in:
parent
a64a6d187b
commit
4f8a441d51
3 changed files with 208 additions and 16 deletions
149
frontend-js/dist/app.js
vendored
149
frontend-js/dist/app.js
vendored
|
|
@ -129,6 +129,98 @@
|
|||
applyListSearch(input.value);
|
||||
}
|
||||
|
||||
function ensureContextMenu() {
|
||||
let menu = document.getElementById("mailContextMenu");
|
||||
if (menu) return menu;
|
||||
menu = document.createElement("div");
|
||||
menu.id = "mailContextMenu";
|
||||
menu.className = "mail-context-menu";
|
||||
menu.setAttribute("role", "menu");
|
||||
menu.setAttribute("aria-label", "Nachrichten-Aktionen");
|
||||
menu.innerHTML = `
|
||||
<button type="button" role="menuitem" data-context-action="open"><span aria-hidden="true">⛶</span> Oeffnen</button>
|
||||
<button type="button" role="menuitem" data-context-action="reply"><span aria-hidden="true">↩</span> Antworten</button>
|
||||
<button type="button" role="menuitem" data-context-action="forward"><span aria-hidden="true">↪</span> Weiterleiten</button>
|
||||
<div class="sep"></div>
|
||||
<button type="button" role="menuitem" data-context-action="copy-subject"><span aria-hidden="true">⎘</span> Betreff kopieren</button>
|
||||
<button type="button" role="menuitem" data-context-action="copy-from"><span aria-hidden="true">✉</span> Absender kopieren</button>
|
||||
<button type="button" role="menuitem" data-context-action="copy-body"><span aria-hidden="true">☰</span> Text kopieren</button>
|
||||
`;
|
||||
document.body.appendChild(menu);
|
||||
return menu;
|
||||
}
|
||||
|
||||
function closeContextMenu() {
|
||||
const menu = document.getElementById("mailContextMenu");
|
||||
if (menu) menu.classList.remove("open");
|
||||
}
|
||||
|
||||
function openContextMenu(x, y, row) {
|
||||
const menu = ensureContextMenu();
|
||||
menu.dataset.rowContext = row ? "1" : "0";
|
||||
const pad = 8;
|
||||
menu.classList.add("open");
|
||||
menu.style.left = "0px";
|
||||
menu.style.top = "0px";
|
||||
const rect = menu.getBoundingClientRect();
|
||||
menu.style.left = `${Math.max(pad, Math.min(x, window.innerWidth - rect.width - pad))}px`;
|
||||
menu.style.top = `${Math.max(pad, Math.min(y, window.innerHeight - rect.height - pad))}px`;
|
||||
}
|
||||
|
||||
function rowMessage(row) {
|
||||
if (!row) return null;
|
||||
return {
|
||||
subject: row.querySelector(".msg-subject")?.textContent?.trim() || "",
|
||||
from: row.querySelector(".msg-from")?.textContent?.trim() || "",
|
||||
date: row.querySelector(".msg-date")?.textContent?.trim() || "",
|
||||
body: ""
|
||||
};
|
||||
}
|
||||
|
||||
function activeMessageFallback() {
|
||||
return rowMessage(document.querySelector(".msg-row.active")) || selectedPreview();
|
||||
}
|
||||
|
||||
function waitForPreview() {
|
||||
return new Promise((resolve) => {
|
||||
let done = false;
|
||||
const finish = () => {
|
||||
if (done) return;
|
||||
done = true;
|
||||
document.removeEventListener("htmx:afterSwap", finish);
|
||||
resolve();
|
||||
};
|
||||
document.addEventListener("htmx:afterSwap", finish);
|
||||
setTimeout(finish, 350);
|
||||
});
|
||||
}
|
||||
|
||||
async function activateContextRow() {
|
||||
const row = document.querySelector(".msg-row.context-active");
|
||||
if (!row) return null;
|
||||
row.click();
|
||||
await waitForPreview();
|
||||
return row;
|
||||
}
|
||||
|
||||
async function copyText(text) {
|
||||
const value = text || "";
|
||||
if (!value) return;
|
||||
if (navigator.clipboard?.writeText) {
|
||||
await navigator.clipboard.writeText(value);
|
||||
return;
|
||||
}
|
||||
const area = document.createElement("textarea");
|
||||
area.value = value;
|
||||
area.style.position = "fixed";
|
||||
area.style.left = "-9999px";
|
||||
document.body.appendChild(area);
|
||||
area.focus();
|
||||
area.select();
|
||||
document.execCommand("copy");
|
||||
area.remove();
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", ensureActionBar);
|
||||
function closeActionMenus() {
|
||||
document.querySelectorAll(".action-menu.open").forEach((menu) => {
|
||||
|
|
@ -216,6 +308,63 @@
|
|||
const input = event.target.closest("[data-action-search]");
|
||||
if (input) runSearch(input);
|
||||
});
|
||||
document.addEventListener("contextmenu", (event) => {
|
||||
const row = event.target.closest(".msg-row");
|
||||
const readBody = event.target.closest(".read-body");
|
||||
if (!row && !readBody) return;
|
||||
event.preventDefault();
|
||||
document.querySelectorAll(".msg-row.context-active").forEach((item) => item.classList.remove("context-active"));
|
||||
if (row) {
|
||||
row.classList.add("context-active");
|
||||
row.closest(".pane")?.querySelectorAll(".msg-row.active").forEach((item) => item.classList.remove("active"));
|
||||
row.classList.add("active");
|
||||
}
|
||||
openContextMenu(event.clientX, event.clientY, row);
|
||||
});
|
||||
document.addEventListener("click", async (event) => {
|
||||
const menu = event.target.closest("#mailContextMenu");
|
||||
if (!menu) {
|
||||
closeContextMenu();
|
||||
return;
|
||||
}
|
||||
const item = event.target.closest("[data-context-action]");
|
||||
if (!item) return;
|
||||
event.preventDefault();
|
||||
const action = item.dataset.contextAction;
|
||||
const contextRow = await activateContextRow();
|
||||
closeContextMenu();
|
||||
const rowMessageData = rowMessage(contextRow);
|
||||
const previewMessage = selectedPreview();
|
||||
const message = previewMessage || rowMessageData || activeMessageFallback();
|
||||
if (!message && action !== "open") {
|
||||
window.alert("Bitte zuerst eine Nachricht waehlen.");
|
||||
return;
|
||||
}
|
||||
if (action === "open") {
|
||||
const button = document.querySelector("#read [data-open-mail-modal], #transfer-preview [data-open-mail-modal], .pane-read [data-open-mail-modal]");
|
||||
if (button) button.click();
|
||||
return;
|
||||
}
|
||||
if (action === "reply") {
|
||||
openCompose({ to: message.from, subject: prefixedSubject("Re", message.subject), body: quoteBody(message) });
|
||||
return;
|
||||
}
|
||||
if (action === "forward") {
|
||||
openCompose({ to: "", subject: prefixedSubject("Fw", message.subject), body: forwardBody(message) });
|
||||
return;
|
||||
}
|
||||
if (action === "copy-subject") {
|
||||
await copyText((rowMessageData || message).subject);
|
||||
return;
|
||||
}
|
||||
if (action === "copy-from") {
|
||||
await copyText((rowMessageData || message).from);
|
||||
return;
|
||||
}
|
||||
if (action === "copy-body") {
|
||||
await copyText(message.body);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function () {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue