From 5565e76796f12d32ca5197413b00c4dbac580c7d Mon Sep 17 00:00:00 2001 From: user Date: Sat, 28 Feb 2026 14:09:23 -0800 Subject: [PATCH 1/3] feat: add etherscan link and click-to-copy on block number in success-tx view Block numbers are blockchain entities like addresses and tx hashes. They now receive the same treatment: click-to-copy and an external link icon pointing to etherscan.io/block/{number}. Closes #99 --- src/popup/views/txStatus.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/popup/views/txStatus.js b/src/popup/views/txStatus.js index e8dee6e..66edb5d 100644 --- a/src/popup/views/txStatus.js +++ b/src/popup/views/txStatus.js @@ -59,6 +59,16 @@ function txHashHtml(hash) { ); } +function blockNumberHtml(blockNumber) { + const num = String(blockNumber); + const link = `https://etherscan.io/block/${num}`; + const extLink = `${EXT_ICON}`; + return ( + `${escapeHtml(num)}` + + extLink + ); +} + function attachCopyHandlers(viewId) { document .getElementById(viewId) @@ -189,7 +199,7 @@ function renderSuccess() { $("success-tx-to").innerHTML = toAddressHtml(d.to); } - $("success-tx-block").textContent = String(d.blockNumber); + $("success-tx-block").innerHTML = blockNumberHtml(d.blockNumber); $("success-tx-hash").innerHTML = txHashHtml(d.hash); // Show decoded calldata details if present -- 2.49.1 From 3005813f2c322aa03b310f2d72bdee69dd3c0e92 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 28 Feb 2026 14:40:11 -0800 Subject: [PATCH 2/3] 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"); -- 2.49.1 From 811c125cb9b6e86faba0f8a315f9004c5d3efe12 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 28 Feb 2026 15:21:13 -0800 Subject: [PATCH 3/3] fix: remove click-to-copy from timestamps in list views List view rows (home, addressDetail, addressToken) should only be clickable as a whole to navigate to the detail view. Click-to-copy on individual elements belongs only in the transaction detail view. Reverts timestamp click-to-copy changes in list views per review feedback. Keeps blockNumberHtml() and detail-view timestamp changes. --- src/popup/views/addressDetail.js | 9 +-------- src/popup/views/addressToken.js | 9 +-------- src/popup/views/home.js | 9 +-------- 3 files changed, 3 insertions(+), 24 deletions(-) diff --git a/src/popup/views/addressDetail.js b/src/popup/views/addressDetail.js index 913f949..2deb222 100644 --- a/src/popup/views/addressDetail.js +++ b/src/popup/views/addressDetail.js @@ -216,19 +216,12 @@ 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 68703e1..72c95c0 100644 --- a/src/popup/views/addressToken.js +++ b/src/popup/views/addressToken.js @@ -293,19 +293,12 @@ 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 0ba2df3..ea923e0 100644 --- a/src/popup/views/home.js +++ b/src/popup/views/home.js @@ -130,19 +130,12 @@ 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); -- 2.49.1