Wire mail action bar buttons

This commit is contained in:
DonVoo 2026-07-13 12:06:41 +02:00
parent 0e47f8c61f
commit e8029a177c
3 changed files with 113 additions and 25 deletions

View file

@ -73,8 +73,8 @@ func renderStartPage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260713-9">
<script src="/static/app.js?v=20260713-9" defer></script></head>
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
<script src="/static/app.js?v=20260713-10" defer></script></head>
<body class="ol2013">
<header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
@ -121,9 +121,9 @@ func renderShell(w http.ResponseWriter, r *http.Request, active, readingPane str
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260713-9">
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
<script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260713-9" defer></script></head>
<script src="/static/app.js?v=20260713-10" defer></script></head>
<body class="ol2013">
<header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
@ -697,9 +697,9 @@ func renderAccountsPage(w http.ResponseWriter, r *http.Request, accounts []Accou
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Konten-Verwaltung - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260713-9">
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
<script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260713-9" defer></script></head>
<script src="/static/app.js?v=20260713-10" defer></script></head>
<body class="ol2013">
<header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
@ -743,9 +743,9 @@ func renderArchivesPage(w http.ResponseWriter, r *http.Request, archives []strin
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Archiv-mbox Verwaltung - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260713-9">
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
<script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260713-9" defer></script></head>
<script src="/static/app.js?v=20260713-10" defer></script></head>
<body class="ol2013">
<header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
@ -791,9 +791,9 @@ func renderUsersPage(w http.ResponseWriter, r *http.Request, users []AppUser, ed
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Benutzerverwaltung - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260713-9">
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
<script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260713-9" defer></script></head>
<script src="/static/app.js?v=20260713-10" defer></script></head>
<body class="ol2013">
<header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
@ -865,9 +865,9 @@ func renderTransferPage(w http.ResponseWriter, r *http.Request, mode transferMod
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>%s - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260713-9">
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
<script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260713-9" defer></script></head>
<script src="/static/app.js?v=20260713-10" defer></script></head>
<body class="ol2013">
<header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
@ -1317,9 +1317,9 @@ func renderSourceEmailBoxPage(w http.ResponseWriter, r *http.Request, accounts [
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Quell-Postfach - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260713-9">
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
<script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260713-9" defer></script></head>
<script src="/static/app.js?v=20260713-10" defer></script></head>
<body class="ol2013">
<header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
@ -1361,9 +1361,9 @@ func renderTargetEmailBoxPage(w http.ResponseWriter, r *http.Request, accounts [
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Ziel-Postfach - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260713-9">
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
<script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260713-9" defer></script></head>
<script src="/static/app.js?v=20260713-10" defer></script></head>
<body class="ol2013">
<header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>

View file

@ -8,26 +8,106 @@
bar.innerHTML = `
<label class="action-search" aria-label="Suchen">
<span aria-hidden="true">&#x2315;</span>
<input type="search" placeholder="Suchen">
<input type="search" placeholder="Suchen" data-action-search>
</label>
<button class="action-btn primary" type="button"><span aria-hidden="true">&#x2709;</span> Neu</button>
<button class="action-btn primary" type="button" data-action-new><span aria-hidden="true">&#x2709;</span> Neu</button>
<button class="action-btn" type="button" data-action-refresh><span aria-hidden="true">&#x21BB;</span> Aktualisieren</button>
<span class="action-separator"></span>
<button class="action-btn" type="button"><span aria-hidden="true">&#x21A9;</span> Antworten</button>
<button class="action-btn" type="button"><span aria-hidden="true">&#x21AA;</span> Weiterleiten</button>
<button class="action-btn" type="button" data-action-reply><span aria-hidden="true">&#x21A9;</span> Antworten</button>
<button class="action-btn" type="button" data-action-forward><span aria-hidden="true">&#x21AA;</span> Weiterleiten</button>
<span class="action-separator"></span>
<button class="action-btn muted" type="button"><span aria-hidden="true">&#x2691;</span> Markieren</button>
<button class="action-btn muted" type="button"><span aria-hidden="true">&#x25A3;</span> Archivieren</button>
<button class="action-btn muted" type="button"><span aria-hidden="true">&#x23F2;</span> Zurueckstellen</button>
<button class="action-btn muted" type="button"><span aria-hidden="true">&#x26A1;</span> Schnelle Aktionen</button>
<button class="action-btn muted" type="button"><span aria-hidden="true">&#x232B;</span> Loeschen</button>
<button class="action-btn muted" type="button" disabled><span aria-hidden="true">&#x2691;</span> Markieren</button>
<button class="action-btn muted" type="button" disabled><span aria-hidden="true">&#x25A3;</span> Archivieren</button>
<button class="action-btn muted" type="button" disabled><span aria-hidden="true">&#x23F2;</span> Zurueckstellen</button>
<button class="action-btn muted" type="button" disabled><span aria-hidden="true">&#x26A1;</span> Schnelle Aktionen</button>
<button class="action-btn muted" type="button" disabled><span aria-hidden="true">&#x232B;</span> Loeschen</button>
`;
ribbon.insertAdjacentElement("afterend", bar);
}
function selectedPreview() {
const read = document.querySelector("#read .read-body, #transfer-preview .read-body, .pane-read .read-body");
if (!read || read.querySelector(".ef-empty")) return null;
const subject = read.querySelector(".read-subject")?.textContent?.trim() || "";
const meta = read.querySelector(".read-meta")?.innerText || "";
const body = read.querySelector(".read-pre")?.innerText || "";
const from = (meta.match(/Von:\s*(.+)/i) || [])[1]?.trim() || document.querySelector(".msg-row.active .msg-from")?.textContent?.trim() || "";
const date = (meta.match(/Datum:\s*(.+)/i) || [])[1]?.trim() || document.querySelector(".msg-row.active .msg-date")?.textContent?.trim() || "";
return { subject, from, date, body };
}
function prefixedSubject(prefix, subject) {
const clean = subject || "";
if (new RegExp(`^${prefix}:`, "i").test(clean)) return clean;
return `${prefix}: ${clean || "(ohne Betreff)"}`;
}
function openMailto(mail) {
const params = new URLSearchParams();
if (mail.subject) params.set("subject", mail.subject);
if (mail.body) params.set("body", mail.body);
window.location.href = `mailto:${encodeURIComponent(mail.to || "")}?${params.toString()}`;
}
function quoteBody(message) {
const lines = (message.body || "").split(/\r?\n/).map((line) => `> ${line}`).join("\n");
return `\n\nAm ${message.date || "unbekanntes Datum"} schrieb ${message.from || "unbekannter Absender"}:\n${lines}`;
}
function forwardBody(message) {
return `\n\n---------- Weitergeleitete Nachricht ----------\nVon: ${message.from || ""}\nDatum: ${message.date || ""}\nBetreff: ${message.subject || ""}\n\n${message.body || ""}`;
}
function applyListSearch(term) {
const q = term.trim().toLowerCase();
document.querySelectorAll(".msg-row").forEach((row) => {
row.hidden = q !== "" && !row.textContent.toLowerCase().includes(q);
});
}
function runSearch(input) {
const existing = document.querySelector(".searchbar .search-input");
const form = existing?.closest("form");
if (existing && form && input.value.trim()) {
existing.value = input.value;
form.requestSubmit();
return;
}
applyListSearch(input.value);
}
document.addEventListener("DOMContentLoaded", ensureActionBar);
document.addEventListener("click", (event) => {
const row = event.target.closest(".msg-row");
if (!row) return;
row.closest(".pane")?.querySelectorAll(".msg-row.active").forEach((item) => item.classList.remove("active"));
row.classList.add("active");
});
document.addEventListener("click", (event) => {
if (event.target.closest("[data-action-new]")) {
openMailto({ to: "", subject: "", body: "" });
}
if (event.target.closest("[data-action-refresh]")) window.location.reload();
if (event.target.closest("[data-action-reply]")) {
const message = selectedPreview();
if (!message) {
window.alert("Bitte zuerst eine Nachricht waehlen.");
return;
}
openMailto({ to: message.from, subject: prefixedSubject("Re", message.subject), body: quoteBody(message) });
}
if (event.target.closest("[data-action-forward]")) {
const message = selectedPreview();
if (!message) {
window.alert("Bitte zuerst eine Nachricht waehlen.");
return;
}
openMailto({ to: "", subject: prefixedSubject("Fw", message.subject), body: forwardBody(message) });
}
});
document.addEventListener("input", (event) => {
const input = event.target.closest("[data-action-search]");
if (input) runSearch(input);
});
})();

View file

@ -231,6 +231,14 @@ body.ol2013 {
border-color: #ff9d34;
}
.action-btn.muted { color: #d7e8f5; }
.action-btn:disabled {
cursor: default;
opacity: .58;
}
.action-btn:disabled:hover {
background: transparent;
border-color: transparent;
}
.action-separator {
width: 1px;
height: 26px;