Fix display consistency across all views
All checks were successful
check / check (push) Successful in 18s

Receive view: address now shows color dot and etherscan link,
matching every other address display in the app.

Send view "From": address now includes etherscan link alongside
the existing color dot.

Send view "What to send" (ERC-20 from token view): shows token
symbol as bold heading, then full contract address below with
color dot, copy-on-click, and etherscan link.

Approval views: tx approval From/To addresses now show color
dots and etherscan links instead of bare text. Site approval
address adds etherscan link. Tx approval value uses 4 decimal
places consistent with all other amount displays.

Home tx list: row padding changed from py-1 to py-2, matching
addressDetail and addressToken transaction lists.
This commit is contained in:
2026-02-27 12:01:34 +07:00
parent a43e8f20ea
commit e58f113cda
6 changed files with 85 additions and 21 deletions

View File

@@ -552,10 +552,11 @@
<div class="flex justify-center mb-3"> <div class="flex justify-center mb-3">
<canvas id="receive-qr"></canvas> <canvas id="receive-qr"></canvas>
</div> </div>
<div <div class="border border-border p-2 break-all mb-3 text-xs">
id="receive-address" <span id="receive-dot"></span>
class="border border-border p-2 break-all select-all mb-3" <span id="receive-address" class="select-all"></span>
></div> <span id="receive-etherscan-link"></span>
</div>
<button <button
id="btn-receive-copy" id="btn-receive-copy"
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer" class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"

View File

@@ -318,7 +318,12 @@ function init(_ctx) {
$("btn-receive").addEventListener("click", () => { $("btn-receive").addEventListener("click", () => {
const addr = currentAddress(); const addr = currentAddress();
const address = addr ? addr.address : ""; const address = addr ? addr.address : "";
$("receive-dot").innerHTML = address ? addressDotHtml(address) : "";
$("receive-address").textContent = address; $("receive-address").textContent = address;
const link = address ? etherscanAddressLink(address) : "";
$("receive-etherscan-link").innerHTML = link
? `<a href="${link}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`
: "";
if (address) { if (address) {
QRCode.toCanvas($("receive-qr"), address, { QRCode.toCanvas($("receive-qr"), address, {
width: 200, width: 200,

View File

@@ -340,13 +340,27 @@ function init(ctx) {
} }
// Hide dropdown, show static token display // Hide dropdown, show static token display
$("send-token").classList.add("hidden"); $("send-token").classList.add("hidden");
let staticHtml = escapeHtml(currentSymbol); let staticHtml = `<div class="font-bold">${escapeHtml(currentSymbol)}</div>`;
if (tokenId !== "ETH") { if (tokenId !== "ETH") {
const dot = addressDotHtml(tokenId);
const link = `https://etherscan.io/token/${tokenId}`; const link = `https://etherscan.io/token/${tokenId}`;
staticHtml += ` <a href="${link}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`; const extLink = `<a href="${link}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`;
staticHtml +=
`<div class="flex items-center text-xs">${dot}` +
`<span class="break-all underline decoration-dashed cursor-pointer" data-copy="${escapeHtml(tokenId)}">${escapeHtml(tokenId)}</span>` +
extLink +
`</div>`;
} }
$("send-token-static").innerHTML = staticHtml; $("send-token-static").innerHTML = staticHtml;
$("send-token-static").classList.remove("hidden"); $("send-token-static").classList.remove("hidden");
// Attach copy handler for the contract address
const copyEl = $("send-token-static").querySelector("[data-copy]");
if (copyEl) {
copyEl.addEventListener("click", () => {
navigator.clipboard.writeText(copyEl.dataset.copy);
showFlash("Copied!");
});
}
updateSendBalance(); updateSendBalance();
showView("send"); showView("send");
}); });
@@ -354,7 +368,12 @@ function init(ctx) {
$("btn-address-token-receive").addEventListener("click", () => { $("btn-address-token-receive").addEventListener("click", () => {
const addr = currentAddress(); const addr = currentAddress();
const address = addr ? addr.address : ""; const address = addr ? addr.address : "";
$("receive-dot").innerHTML = address ? addressDotHtml(address) : "";
$("receive-address").textContent = address; $("receive-address").textContent = address;
const addrLink = address ? etherscanAddressLink(address) : "";
$("receive-etherscan-link").innerHTML = addrLink
? `<a href="${addrLink}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`
: "";
if (address) { if (address) {
QRCode.toCanvas($("receive-qr"), address, { QRCode.toCanvas($("receive-qr"), address, {
width: 200, width: 200,

View File

@@ -1,19 +1,42 @@
const { $, formatAddressHtml, showView } = require("./helpers"); const { $, addressDotHtml, escapeHtml, showView } = require("./helpers");
const { state, saveState } = require("../../shared/state"); const { state, saveState } = require("../../shared/state");
const { formatEther } = require("ethers"); const { formatEther } = require("ethers");
const runtime = const runtime =
typeof browser !== "undefined" ? browser.runtime : chrome.runtime; typeof browser !== "undefined" ? browser.runtime : chrome.runtime;
const EXT_ICON =
`<span style="display:inline-block;width:10px;height:10px;margin-left:4px;vertical-align:middle">` +
`<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5">` +
`<path d="M4.5 1.5H2a.5.5 0 00-.5.5v8a.5.5 0 00.5.5h8a.5.5 0 00.5-.5V7.5"/>` +
`<path d="M7 1.5h3.5V5M7 5.5L10.5 1.5"/>` +
`</svg></span>`;
function approvalAddressHtml(address) {
const dot = addressDotHtml(address);
const link = `https://etherscan.io/address/${address}`;
const extLink = `<a href="${link}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`;
return `<div class="flex items-center">${dot}<span class="break-all">${escapeHtml(address)}</span>${extLink}</div>`;
}
function formatTxValue(val) {
const parts = val.split(".");
if (parts.length === 1) return val + ".0000";
const dec = (parts[1] + "0000").slice(0, 4);
return parts[0] + "." + dec;
}
let approvalId = null; let approvalId = null;
function showTxApproval(details) { function showTxApproval(details) {
$("approve-tx-hostname").textContent = details.hostname; $("approve-tx-hostname").textContent = details.hostname;
$("approve-tx-from").textContent = state.activeAddress; $("approve-tx-from").innerHTML = approvalAddressHtml(state.activeAddress);
$("approve-tx-to").textContent = const toAddr = details.txParams.to;
details.txParams.to || "(contract creation)"; $("approve-tx-to").innerHTML = toAddr
? approvalAddressHtml(toAddr)
: escapeHtml("(contract creation)");
$("approve-tx-value").textContent = $("approve-tx-value").textContent =
formatEther(details.txParams.value || "0") + " ETH"; formatTxValue(formatEther(details.txParams.value || "0")) + " ETH";
if (details.txParams.data && details.txParams.data !== "0x") { if (details.txParams.data && details.txParams.data !== "0x") {
$("approve-tx-data").textContent = details.txParams.data; $("approve-tx-data").textContent = details.txParams.data;
$("approve-tx-data-section").classList.remove("hidden"); $("approve-tx-data-section").classList.remove("hidden");
@@ -36,10 +59,8 @@ function show(id) {
return; return;
} }
$("approve-hostname").textContent = details.hostname; $("approve-hostname").textContent = details.hostname;
$("approve-address").innerHTML = formatAddressHtml( $("approve-address").innerHTML = approvalAddressHtml(
state.activeAddress, state.activeAddress,
null,
null,
); );
$("approve-remember").checked = state.rememberSiteChoice; $("approve-remember").checked = state.rememberSiteChoice;
}); });

View File

@@ -145,7 +145,7 @@ function renderHomeTxList(ctx) {
const opacity = tx.isError ? " opacity:0.5;" : ""; const opacity = tx.isError ? " opacity:0.5;" : "";
const ago = escapeHtml(timeAgo(tx.timestamp)); const ago = escapeHtml(timeAgo(tx.timestamp));
const iso = escapeHtml(isoDate(tx.timestamp)); const iso = escapeHtml(isoDate(tx.timestamp));
html += `<div class="home-tx-row py-1 border-b border-border-light text-xs cursor-pointer hover:bg-hover" data-tx="${i}" style="${opacity}">`; 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" title="${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 class="flex justify-between"><span class="flex items-center">${dot}${addrStr}</span><span>${amountStr}</span></div>`;
html += `</div>`; html += `</div>`;
@@ -417,7 +417,12 @@ function init(ctx) {
} }
const addr = currentAddress(); const addr = currentAddress();
const address = addr ? addr.address : ""; const address = addr ? addr.address : "";
$("receive-dot").innerHTML = address ? addressDotHtml(address) : "";
$("receive-address").textContent = address; $("receive-address").textContent = address;
const link = address ? `https://etherscan.io/address/${address}` : "";
$("receive-etherscan-link").innerHTML = link
? `<a href="${link}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`
: "";
if (address) { if (address) {
QRCode.toCanvas($("receive-qr"), address, { QRCode.toCanvas($("receive-qr"), address, {
width: 200, width: 200,

View File

@@ -1,11 +1,18 @@
// Send view: collect To, Amount, Token. Then go to confirmation. // Send view: collect To, Amount, Token. Then go to confirmation.
const { $, showFlash, formatAddressHtml } = require("./helpers"); const { $, showFlash, addressDotHtml, escapeHtml } = require("./helpers");
const { state, currentAddress } = require("../../shared/state"); const { state, currentAddress } = require("../../shared/state");
let ctx; let ctx;
const { getProvider } = require("../../shared/balances"); const { getProvider } = require("../../shared/balances");
const { KNOWN_SYMBOLS } = require("../../shared/tokens"); const { KNOWN_SYMBOLS } = require("../../shared/tokens");
const EXT_ICON =
`<span style="display:inline-block;width:10px;height:10px;margin-left:4px;vertical-align:middle">` +
`<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5">` +
`<path d="M4.5 1.5H2a.5.5 0 00-.5.5v8a.5.5 0 00.5.5h8a.5.5 0 00.5-.5V7.5"/>` +
`<path d="M7 1.5h3.5V5M7 5.5L10.5 1.5"/>` +
`</svg></span>`;
function isSpoofedToken(t) { function isSpoofedToken(t) {
const upper = (t.symbol || "").toUpperCase(); const upper = (t.symbol || "").toUpperCase();
if (!KNOWN_SYMBOLS.has(upper)) return false; if (!KNOWN_SYMBOLS.has(upper)) return false;
@@ -34,11 +41,17 @@ function renderSendTokenSelect(addr) {
function updateSendBalance() { function updateSendBalance() {
const addr = currentAddress(); const addr = currentAddress();
if (!addr) return; if (!addr) return;
$("send-from").innerHTML = formatAddressHtml( const dot = addressDotHtml(addr.address);
addr.address, const link = `https://etherscan.io/address/${addr.address}`;
addr.ensName || null, const extLink = `<a href="${link}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`;
null, let fromHtml = "";
); if (addr.ensName) {
fromHtml += `<div class="flex items-center font-bold">${dot}${escapeHtml(addr.ensName)}</div>`;
fromHtml += `<div class="break-all">${escapeHtml(addr.address)}${extLink}</div>`;
} else {
fromHtml += `<div class="flex items-center">${dot}<span class="break-all">${escapeHtml(addr.address)}</span>${extLink}</div>`;
}
$("send-from").innerHTML = fromHtml;
const token = state.selectedToken || $("send-token").value; const token = state.selectedToken || $("send-token").value;
if (token === "ETH") { if (token === "ETH") {
$("send-balance").textContent = $("send-balance").textContent =