fix: contact rows, narrow sender labels and stale reader pane

This commit is contained in:
randogoth 2026-09-26 17:45:20 +03:00
parent 9842302c50
commit f637d5be57
2 changed files with 31 additions and 4 deletions

View file

@ -115,9 +115,17 @@ for (const button of $("folders").querySelectorAll("button"))
folder = button.dataset.folder; folder = button.dataset.folder;
for (const b of $("folders").querySelectorAll("button")) b.classList.toggle("active", b === button); for (const b of $("folders").querySelectorAll("button")) b.classList.toggle("active", b === button);
selectedId = null; selectedId = null;
resetView();
renderFolder(); 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() { async function renderFolder() {
const list = $("list"); const list = $("list");
if (folder === "contacts") return renderContacts(list); if (folder === "contacts") return renderContacts(list);
@ -132,7 +140,7 @@ async function renderFolder() {
item.className = `item${row.id === selectedId ? " selected" : ""}${isUnread ? " unread" : ""}`; item.className = `item${row.id === selectedId ? " selected" : ""}${isUnread ? " unread" : ""}`;
const who = document.createElement("div"); const who = document.createElement("div");
who.className = "who"; who.className = "who";
who.textContent = folder === "sent" ? row.recipient : (opened.from ?? "?"); who.textContent = folder === "sent" ? row.recipient : (opened.fromShort ?? "?");
const mid = document.createElement("div"); const mid = document.createElement("div");
mid.className = "mid"; mid.className = "mid";
const subject = document.createElement("span"); const subject = document.createElement("span");
@ -188,6 +196,8 @@ function describe(row) {
subject: fields.Subject || "", subject: fields.Subject || "",
snippet: body.replace(/\s+/g, " ").trim().slice(0, 140), snippet: body.replace(/\s+/g, " ").trim().slice(0, 140),
from: known ?? `unknown · ${fingerprint(opened.sender)}`, 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), knownSender: Boolean(known),
replyAddress, replyAddress,
}; };
@ -471,13 +481,18 @@ function renderContacts(list) {
const contacts = store.allContacts(); const contacts = store.allContacts();
for (const { address, key, verified } of contacts) { for (const { address, key, verified } of contacts) {
const item = document.createElement("div"); const item = document.createElement("div");
item.className = "item"; item.className = "item contact";
const who = document.createElement("div"); const who = document.createElement("div");
who.className = "who"; who.className = "who";
who.textContent = address; who.textContent = address;
const mid = document.createElement("div"); const mid = document.createElement("div");
mid.className = "mid"; 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"); const refresh = document.createElement("button");
refresh.textContent = "refresh"; refresh.textContent = "refresh";
refresh.onclick = () => refreshContact(address); refresh.onclick = () => refreshContact(address);

View file

@ -152,6 +152,18 @@ main { flex: 1; min-height: 0; display: flex; background: var(--bg); }
.item.unread .snippet { color: var(--fg); } .item.unread .snippet { color: var(--fg); }
.item .when { flex: none; font-size: 12px; color: var(--muted); } .item .when { flex: none; font-size: 12px; color: var(--muted); }
.item button { padding: 2px 12px; font-size: 12px; } .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 { flex: 1; min-width: 0; overflow-y: auto; padding: 24px 28px; }
.view h2 { margin: 0 0 10px; font-size: 20px; font-weight: 400; } .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 input, #compose textarea { width: 100%; }
#compose textarea { font-family: inherit; resize: vertical; } #compose textarea { font-family: inherit; resize: vertical; }
#compose footer { display: flex; gap: 8px; align-items: center; justify-content: flex-end; padding-top: 4px; } #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; } .error { color: var(--warn); font-size: 13px; }
#settings section { margin-bottom: 22px; } #settings section { margin-bottom: 22px; }