feat: add click-to-copy on timestamps in all transaction list views
All checks were successful
check / check (push) Successful in 9s

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.
This commit is contained in:
user
2026-02-28 14:40:11 -08:00
parent 5565e76796
commit 3005813f2c
4 changed files with 27 additions and 5 deletions

View File

@@ -216,12 +216,19 @@ function renderTransactions(txs) {
const ago = escapeHtml(timeAgo(tx.timestamp));
const iso = escapeHtml(isoDate(tx.timestamp));
html += `<div class="tx-row py-2 border-b border-border-light text-xs cursor-pointer hover:bg-hover" data-tx="${i}" style="${opacity}">`;
html += `<div class="flex justify-between"><span class="text-muted" title="${iso}">${ago}</span><span>${dirLabel}${err}</span></div>`;
html += `<div class="flex justify-between"><span class="text-muted underline decoration-dashed cursor-pointer" title="${iso}" data-copy="${iso}">${ago}</span><span>${dirLabel}${err}</span></div>`;
html += `<div class="flex justify-between"><span class="flex items-center">${dot}${addrStr}</span><span>${amountStr}</span></div>`;
html += `</div>`;
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);

View File

@@ -293,12 +293,19 @@ function renderTransactions(txs) {
const ago = escapeHtml(timeAgo(tx.timestamp));
const iso = escapeHtml(isoDate(tx.timestamp));
html += `<div class="tx-row py-2 border-b border-border-light text-xs cursor-pointer hover:bg-hover" data-tx="${i}" style="${opacity}">`;
html += `<div class="flex justify-between"><span class="text-muted" title="${iso}">${ago}</span><span>${dirLabel}${err}</span></div>`;
html += `<div class="flex justify-between"><span class="text-muted underline decoration-dashed cursor-pointer" title="${iso}" data-copy="${iso}">${ago}</span><span>${dirLabel}${err}</span></div>`;
html += `<div class="flex justify-between"><span class="flex items-center">${dot}${addrStr}</span><span>${amountStr}</span></div>`;
html += `</div>`;
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);

View File

@@ -130,12 +130,19 @@ function renderHomeTxList(ctx) {
const ago = escapeHtml(timeAgo(tx.timestamp));
const iso = escapeHtml(isoDate(tx.timestamp));
html += `<div class="home-tx-row py-2 border-b border-border-light text-xs cursor-pointer hover:bg-hover" data-tx="${i}" style="${opacity}">`;
html += `<div class="flex justify-between"><span class="text-muted" title="${iso}">${ago}</span><span>${dirLabel}${err}</span></div>`;
html += `<div class="flex justify-between"><span class="text-muted underline decoration-dashed cursor-pointer" title="${iso}" data-copy="${iso}">${ago}</span><span>${dirLabel}${err}</span></div>`;
html += `<div class="flex justify-between"><span class="flex items-center">${dot}${addrStr}</span><span>${amountStr}</span></div>`;
html += `</div>`;
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);

View File

@@ -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");