From f637d5be579be903effb4a4ba5f285a00e163924 Mon Sep 17 00:00:00 2001 From: randogoth Date: Sat, 26 Sep 2026 17:45:20 +0300 Subject: [PATCH] fix: contact rows, narrow sender labels and stale reader pane --- web/js/app.js | 21 ++++++++++++++++++--- web/style.css | 14 +++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/web/js/app.js b/web/js/app.js index 7b2bde2..ae3d5f0 100644 --- a/web/js/app.js +++ b/web/js/app.js @@ -115,9 +115,17 @@ for (const button of $("folders").querySelectorAll("button")) folder = button.dataset.folder; for (const b of $("folders").querySelectorAll("button")) b.classList.toggle("active", b === button); selectedId = null; + resetView(); renderFolder(); }; +// The reader empties when a folder changes, so a stale message cannot linger +// under a different list. +function resetView() { + $("view").replaceChildren(Object.assign(document.createElement("p"), + { className: "placeholder", textContent: "select a message" })); +} + async function renderFolder() { const list = $("list"); if (folder === "contacts") return renderContacts(list); @@ -132,7 +140,7 @@ async function renderFolder() { item.className = `item${row.id === selectedId ? " selected" : ""}${isUnread ? " unread" : ""}`; const who = document.createElement("div"); who.className = "who"; - who.textContent = folder === "sent" ? row.recipient : (opened.from ?? "?"); + who.textContent = folder === "sent" ? row.recipient : (opened.fromShort ?? "?"); const mid = document.createElement("div"); mid.className = "mid"; const subject = document.createElement("span"); @@ -188,6 +196,8 @@ function describe(row) { subject: fields.Subject || "", snippet: body.replace(/\s+/g, " ").trim().slice(0, 140), from: known ?? `unknown · ${fingerprint(opened.sender)}`, + // rows stay narrow: the full fingerprint belongs to the reader + fromShort: known ?? `unknown · ${b32encode(opened.sender).slice(0, 4)}`, knownSender: Boolean(known), replyAddress, }; @@ -471,13 +481,18 @@ function renderContacts(list) { const contacts = store.allContacts(); for (const { address, key, verified } of contacts) { const item = document.createElement("div"); - item.className = "item"; + item.className = "item contact"; const who = document.createElement("div"); who.className = "who"; who.textContent = address; const mid = document.createElement("div"); mid.className = "mid"; - mid.textContent = `${fingerprint(b32decode(key))} · ${verified ? "verified key" : "tofu"}`; + const fp = document.createElement("div"); + fp.textContent = fingerprint(b32decode(key)); + const badge = document.createElement("span"); + badge.className = verified ? "badge ok" : "badge"; + badge.textContent = verified ? "verified key" : "tofu"; + mid.append(fp, badge); const refresh = document.createElement("button"); refresh.textContent = "refresh"; refresh.onclick = () => refreshContact(address); diff --git a/web/style.css b/web/style.css index 36a2159..321100e 100644 --- a/web/style.css +++ b/web/style.css @@ -152,6 +152,18 @@ main { flex: 1; min-height: 0; display: flex; background: var(--bg); } .item.unread .snippet { color: var(--fg); } .item .when { flex: none; font-size: 12px; color: var(--muted); } .item button { padding: 2px 12px; font-size: 12px; } +.item.contact .mid { white-space: normal; line-height: 1.45; font-family: var(--mono); font-size: 12px; color: var(--muted); } +.badge { + display: inline-block; + margin-top: 3px; + font-family: inherit; + font-size: 11px; + padding: 1px 9px; + border-radius: 10px; + background: var(--hover); + color: var(--muted); +} +.badge.ok { background: #e6f4ea; color: #137333; } .view { flex: 1; min-width: 0; overflow-y: auto; padding: 24px 28px; } .view h2 { margin: 0 0 10px; font-size: 20px; font-weight: 400; } @@ -211,7 +223,7 @@ dialog::backdrop { background: rgba(32, 33, 36, 0.3); } #compose input, #compose textarea { width: 100%; } #compose textarea { font-family: inherit; resize: vertical; } #compose footer { display: flex; gap: 8px; align-items: center; justify-content: flex-end; padding-top: 4px; } -#compose .anon { font-size: 13px; color: var(--muted); margin-right: auto; display: flex; align-items: center; gap: 6px; } +#compose .anon { font-size: 13px; color: var(--muted); margin-right: auto; display: flex; align-items: center; gap: 6px; white-space: nowrap; } .error { color: var(--warn); font-size: 13px; } #settings section { margin-bottom: 22px; }