From 3005813f2c322aa03b310f2d72bdee69dd3c0e92 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 28 Feb 2026 14:40:11 -0800 Subject: [PATCH] feat: add click-to-copy on timestamps in all transaction list views Adds click-to-copy (copies ISO date string) to timestamp displays in: - home view (relative time ago) - addressDetail view (relative time ago) - addressToken view (relative time ago) - transactionDetail view (full ISO date) All timestamps now show dashed underline to indicate copyability, matching the existing UX pattern for addresses, tx hashes, and block numbers. --- src/popup/views/addressDetail.js | 9 ++++++++- src/popup/views/addressToken.js | 9 ++++++++- src/popup/views/home.js | 9 ++++++++- src/popup/views/transactionDetail.js | 5 +++-- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/popup/views/addressDetail.js b/src/popup/views/addressDetail.js index 2deb222..913f949 100644 --- a/src/popup/views/addressDetail.js +++ b/src/popup/views/addressDetail.js @@ -216,12 +216,19 @@ function renderTransactions(txs) { const ago = escapeHtml(timeAgo(tx.timestamp)); const iso = escapeHtml(isoDate(tx.timestamp)); html += `
`; - html += `
${ago}${dirLabel}${err}
`; + html += `
${ago}${dirLabel}${err}
`; html += `
${dot}${addrStr}${amountStr}
`; html += `
`; i++; } list.innerHTML = html; + list.querySelectorAll("[data-copy]").forEach((el) => { + el.addEventListener("click", (e) => { + e.stopPropagation(); + navigator.clipboard.writeText(el.dataset.copy); + showFlash("Copied!"); + }); + }); list.querySelectorAll(".tx-row").forEach((row) => { row.addEventListener("click", () => { const idx = parseInt(row.dataset.tx, 10); diff --git a/src/popup/views/addressToken.js b/src/popup/views/addressToken.js index 72c95c0..68703e1 100644 --- a/src/popup/views/addressToken.js +++ b/src/popup/views/addressToken.js @@ -293,12 +293,19 @@ function renderTransactions(txs) { const ago = escapeHtml(timeAgo(tx.timestamp)); const iso = escapeHtml(isoDate(tx.timestamp)); html += `
`; - html += `
${ago}${dirLabel}${err}
`; + html += `
${ago}${dirLabel}${err}
`; html += `
${dot}${addrStr}${amountStr}
`; html += `
`; i++; } list.innerHTML = html; + list.querySelectorAll("[data-copy]").forEach((el) => { + el.addEventListener("click", (e) => { + e.stopPropagation(); + navigator.clipboard.writeText(el.dataset.copy); + showFlash("Copied!"); + }); + }); list.querySelectorAll(".tx-row").forEach((row) => { row.addEventListener("click", () => { const idx = parseInt(row.dataset.tx, 10); diff --git a/src/popup/views/home.js b/src/popup/views/home.js index ea923e0..0ba2df3 100644 --- a/src/popup/views/home.js +++ b/src/popup/views/home.js @@ -130,12 +130,19 @@ function renderHomeTxList(ctx) { const ago = escapeHtml(timeAgo(tx.timestamp)); const iso = escapeHtml(isoDate(tx.timestamp)); html += `
`; - html += `
${ago}${dirLabel}${err}
`; + html += `
${ago}${dirLabel}${err}
`; html += `
${dot}${addrStr}${amountStr}
`; html += `
`; i++; } list.innerHTML = html; + list.querySelectorAll("[data-copy]").forEach((el) => { + el.addEventListener("click", (e) => { + e.stopPropagation(); + navigator.clipboard.writeText(el.dataset.copy); + showFlash("Copied!"); + }); + }); list.querySelectorAll(".home-tx-row").forEach((row) => { row.addEventListener("click", () => { const idx = parseInt(row.dataset.tx, 10); diff --git a/src/popup/views/transactionDetail.js b/src/popup/views/transactionDetail.js index d24e040..bbfb0d9 100644 --- a/src/popup/views/transactionDetail.js +++ b/src/popup/views/transactionDetail.js @@ -158,8 +158,9 @@ function render() { loadCalldata(tx.hash, tx.to); } - $("tx-detail-time").textContent = - isoDate(tx.timestamp) + " (" + timeAgo(tx.timestamp) + ")"; + const isoStr = isoDate(tx.timestamp); + $("tx-detail-time").innerHTML = + copyableHtml(isoStr) + " (" + escapeHtml(timeAgo(tx.timestamp)) + ")"; $("tx-detail-status").textContent = tx.isError ? "Failed" : "Success"; showView("transaction");