From af757eb552cab0b2883a7de1bcaadc6ee6b0e6de Mon Sep 17 00:00:00 2001 From: sneak Date: Mon, 5 Oct 2026 05:51:46 +0000 Subject: [PATCH] chore: remove dead exports and share copied view helpers (closes #168) AddressDetail and AddressToken defined their own isoDate() and timeAgo(), hiding the shared pair in helpers.js, so a fix there would not have reached them. The copies were identical and are deleted; blockieHtml() and tokenLabel(), each defined twice, move to helpers.js. A new test shows the history rows and the transaction detail view write the time with the shared pair. Deleted as never called: explorerLink(), ETHEREUM_SEPOLIA_CHAIN_ID, getWalletValue() and getTotalValue() with their tests. Home's "Total:" is the active address's total, as README.md already says. addressColor() and etherscanAddressUrl() are no longer exported. Model: opus-5-5 --- README.md | 10 +- TODO.md | 13 +++ src/popup/views/addressDetail.js | 58 +--------- src/popup/views/addressToken.js | 58 +--------- src/popup/views/approval.js | 12 +- src/popup/views/confirmTx.js | 7 +- src/popup/views/helpers.js | 25 ++++- src/popup/views/settingsAddToken.js | 4 +- src/popup/views/transactionDetail.js | 7 +- src/popup/views/txStatus.js | 13 +-- src/shared/constants.js | 2 - src/shared/networks.js | 7 -- src/shared/prices.js | 23 ---- tests/addressValue.test.js | 13 --- tests/support/popupBoot.js | 2 - tests/timestampDisplay.test.js | 159 +++++++++++++++++++++++++++ 16 files changed, 211 insertions(+), 202 deletions(-) create mode 100644 tests/timestampDisplay.test.js diff --git a/README.md b/README.md index 034d06b..806476b 100644 --- a/README.md +++ b/README.md @@ -840,10 +840,12 @@ something when you click it. The same data must be formatted identically everywhere it appears. Token and ETH amounts are displayed with exactly 4 decimal places (e.g. "1.0500 ETH", "17.1900 USDT") in balance lists, transaction lists, send confirmations, and approval -screens. Timestamps include both an ISO datetime and a humanized relative age -wherever shown. If a formatting rule applies in one place, it applies in every -place. Users should never see the same value rendered differently on two -screens. +screens. A transaction's time includes both an ISO datetime and a humanized +relative age, written by `isoDate()` and `timeAgo()` in +`src/popup/views/helpers.js` on every screen that shows one; the ISO datetime is +in UTC when the UTC Timestamps setting is on. If a formatting rule applies in +one place, it applies in every place. Users should never see the same value +rendered differently on two screens. The native token's label is a network's `nativeCurrency` in `src/shared/networks.js`: `ETH` on mainnet, `SepoliaETH` on Sepolia. The diff --git a/TODO.md b/TODO.md index 45a39d5..73b5b7b 100644 --- a/TODO.md +++ b/TODO.md @@ -45,6 +45,19 @@ but the review is broader than any of them. # Completed Steps +- 2026-10-05: Dead code removed and copied view helpers shared + ([#168](https://git.eeqj.de/sneak/AutistMask/issues/168)). AddressDetail and + AddressToken each defined their own `isoDate()` and `timeAgo()` in place of + the ones in `src/popup/views/helpers.js`, so a fix to the shared pair would + not have reached them. The copies were identical; every screen now uses the + shared pair. `blockieHtml()` and `tokenLabel()`, each defined twice, live in + `helpers.js` too. Removed as never called: `explorerLink()` (the views build + explorer links with `explorerUrl()`), `ETHEREUM_SEPOLIA_CHAIN_ID` (the chain + id lives in `src/shared/networks.js`), and `getWalletValue()` and + `getTotalValue()`: Home's "Total:" is the active address's total, as + `README.md` says. `addressColor()` and `etherscanAddressUrl()` are no longer + exported. Nothing the user sees changed. + - 2026-10-05: A prompt raised while another approval window has focus opens a window of its own ([#290](https://git.eeqj.de/sneak/AutistMask/issues/290)). The background centred each approval window on the last focused window, which diff --git a/src/popup/views/addressDetail.js b/src/popup/views/addressDetail.js index 5ea6225..39f1f6e 100644 --- a/src/popup/views/addressDetail.js +++ b/src/popup/views/addressDetail.js @@ -11,6 +11,8 @@ const { attachCopyHandlers, goBack, pushCurrentView, + isoDate, + timeAgo, } = require("./helpers"); const { state, saveState, currentNetwork } = require("../../shared/state"); const { formatAddressTotal, getAddressValue } = require("../../shared/prices"); @@ -88,62 +90,6 @@ function show() { loadTransactions(addr.address); } -function isoDate(timestamp) { - const d = new Date(timestamp * 1000); - const pad = (n) => String(n).padStart(2, "0"); - if (state.utcTimestamps) { - return ( - d.getUTCFullYear() + - "-" + - pad(d.getUTCMonth() + 1) + - "-" + - pad(d.getUTCDate()) + - "T" + - pad(d.getUTCHours()) + - ":" + - pad(d.getUTCMinutes()) + - ":" + - pad(d.getUTCSeconds()) + - "Z" - ); - } - const offsetMin = -d.getTimezoneOffset(); - const sign = offsetMin >= 0 ? "+" : "-"; - const absOff = Math.abs(offsetMin); - const tzStr = sign + pad(Math.floor(absOff / 60)) + ":" + pad(absOff % 60); - return ( - d.getFullYear() + - "-" + - pad(d.getMonth() + 1) + - "-" + - pad(d.getDate()) + - "T" + - pad(d.getHours()) + - ":" + - pad(d.getMinutes()) + - ":" + - pad(d.getSeconds()) + - tzStr - ); -} - -function timeAgo(timestamp) { - const seconds = Math.floor(Date.now() / 1000 - timestamp); - if (seconds < 60) return seconds + " seconds ago"; - const minutes = Math.floor(seconds / 60); - if (minutes < 60) - return minutes + " minute" + (minutes !== 1 ? "s" : "") + " ago"; - const hours = Math.floor(minutes / 60); - if (hours < 24) return hours + " hour" + (hours !== 1 ? "s" : "") + " ago"; - const days = Math.floor(hours / 24); - if (days < 30) return days + " day" + (days !== 1 ? "s" : "") + " ago"; - const months = Math.floor(days / 30); - if (months < 12) - return months + " month" + (months !== 1 ? "s" : "") + " ago"; - const years = Math.floor(days / 365); - return years + " year" + (years !== 1 ? "s" : "") + " ago"; -} - let loadedTxs = []; let ensNameMap = new Map(); diff --git a/src/popup/views/addressToken.js b/src/popup/views/addressToken.js index 1ba8907..451e58b 100644 --- a/src/popup/views/addressToken.js +++ b/src/popup/views/addressToken.js @@ -17,6 +17,8 @@ const { attachCopyHandlers, goBack, pushCurrentView, + isoDate, + timeAgo, } = require("./helpers"); const { state, saveState, currentNetwork } = require("../../shared/state"); const { TOKEN_BY_ADDRESS, resolveSymbol } = require("../../shared/tokenList"); @@ -37,62 +39,6 @@ const { walletDefect } = require("../../shared/walletDefects"); let ctx; -function isoDate(timestamp) { - const d = new Date(timestamp * 1000); - const pad = (n) => String(n).padStart(2, "0"); - if (state.utcTimestamps) { - return ( - d.getUTCFullYear() + - "-" + - pad(d.getUTCMonth() + 1) + - "-" + - pad(d.getUTCDate()) + - "T" + - pad(d.getUTCHours()) + - ":" + - pad(d.getUTCMinutes()) + - ":" + - pad(d.getUTCSeconds()) + - "Z" - ); - } - const offsetMin = -d.getTimezoneOffset(); - const sign = offsetMin >= 0 ? "+" : "-"; - const absOff = Math.abs(offsetMin); - const tzStr = sign + pad(Math.floor(absOff / 60)) + ":" + pad(absOff % 60); - return ( - d.getFullYear() + - "-" + - pad(d.getMonth() + 1) + - "-" + - pad(d.getDate()) + - "T" + - pad(d.getHours()) + - ":" + - pad(d.getMinutes()) + - ":" + - pad(d.getSeconds()) + - tzStr - ); -} - -function timeAgo(timestamp) { - const seconds = Math.floor(Date.now() / 1000 - timestamp); - if (seconds < 60) return seconds + " seconds ago"; - const minutes = Math.floor(seconds / 60); - if (minutes < 60) - return minutes + " minute" + (minutes !== 1 ? "s" : "") + " ago"; - const hours = Math.floor(minutes / 60); - if (hours < 24) return hours + " hour" + (hours !== 1 ? "s" : "") + " ago"; - const days = Math.floor(hours / 24); - if (days < 30) return days + " day" + (days !== 1 ? "s" : "") + " ago"; - const months = Math.floor(days / 30); - if (months < 12) - return months + " month" + (months !== 1 ? "s" : "") + " ago"; - const years = Math.floor(days / 365); - return years + " year" + (years !== 1 ? "s" : "") + " ago"; -} - let loadedTxs = []; let ensNameMap = new Map(); let currentSymbol = null; diff --git a/src/popup/views/approval.js b/src/popup/views/approval.js index d4e9e5d..3f8dddf 100644 --- a/src/popup/views/approval.js +++ b/src/popup/views/approval.js @@ -10,6 +10,7 @@ const { attachCopyHandlers, onViewLeave, formatFee, + tokenLabel, } = require("./helpers"); const { state, saveState } = require("../../shared/state"); const { @@ -73,17 +74,6 @@ function tokenAmountText(rawAmount, decimals, symbol) { }; } -// The symbol shown for a token line, resolved from the bundled list, the -// tokens the user tracks, and the explorer's report — the same chain the -// amount line's scale comes from. Null when no source names one, so the token -// lines keep saying `Unknown token` for a token nothing knows. -function tokenLabel(address) { - return resolveTokenSymbol(address, { - trackedTokens: state.trackedTokens, - wallets: state.wallets, - }); -} - // Try to decode calldata using known ABIs. // Returns { name, description, details } or null. function decodeCalldata(data, toAddress) { diff --git a/src/popup/views/confirmTx.js b/src/popup/views/confirmTx.js index 4752419..942e951 100644 --- a/src/popup/views/confirmTx.js +++ b/src/popup/views/confirmTx.js @@ -13,6 +13,7 @@ const { displaySymbol, nativeCurrency, renderAddressHtml, + blockieHtml, attachCopyHandlers, goBack, onViewLeave, @@ -47,7 +48,6 @@ const { validateTransfer, } = require("../../shared/txValidation"); const { log } = require("../../shared/log"); -const makeBlockie = require("ethereum-blockies-base64"); const txStatus = require("./txStatus"); let pendingTx = null; @@ -67,11 +67,6 @@ function restore() { } } -function blockieHtml(address) { - const src = makeBlockie(address); - return ``; -} - function confirmAddressHtml(address, ensName, title) { const blockie = blockieHtml(address); return ( diff --git a/src/popup/views/helpers.js b/src/popup/views/helpers.js index a285d24..bf44d49 100644 --- a/src/popup/views/helpers.js +++ b/src/popup/views/helpers.js @@ -13,10 +13,12 @@ // reasoning behind it are; it is re-exported below so views keep importing // it from here. const { formatEther } = require("ethers"); +const makeBlockie = require("ethereum-blockies-base64"); const { truncateAmountNeverZero, isBelowOneMillionth, } = require("../../shared/amountDisplay"); +const { resolveTokenSymbol } = require("../../shared/approvalAmount"); const { DEBUG } = require("../../shared/constants"); const { escapeHtml } = require("../../shared/html"); const { isDebug } = require("../../shared/log"); @@ -278,6 +280,17 @@ function nativeCurrency() { return currentNetwork().nativeCurrency; } +// The symbol shown for a token line, resolved from the bundled list, the +// tokens the user tracks, and the explorer's report — the same chain the +// amount line's scale comes from. Null when no source names one, so the token +// lines keep saying `Unknown token` for a token nothing knows. +function tokenLabel(address) { + return resolveTokenSymbol(address, { + trackedTokens: state.trackedTokens, + wallets: state.wallets, + }); +} + // A network fee in wei as the confirmation and approval screens both show it: // the ETH figure through truncateAmountNeverZero() and labelled `symbol`, the // native currency of the network the fee is paid on, then its USD value when @@ -446,6 +459,11 @@ function addressDotHtml(address) { return ``; } +function blockieHtml(address) { + const src = makeBlockie(address); + return ``; +} + // Look up an address across all wallets and return its title // (e.g. "Address 1.2") or null if it's not one of ours. function addressTitle(address, wallets) { @@ -492,6 +510,9 @@ function formatAddressHtml(address, ensName, maxLen, title) { return renderAddressHtml(address, { title, ensName, maxLen }); } +// A transaction's time as every screen shows it (README, Display +// Consistency): the ISO datetime, in UTC when the UTC Timestamps setting is +// on, and the relative age. Views import these two; they keep no copies. function isoDate(timestamp) { const d = new Date(timestamp * 1000); const pad = (n) => String(n).padStart(2, "0"); @@ -691,9 +712,10 @@ module.exports = { addressHoldsFunds, unknownableAmount, nativeCurrency, + tokenLabel, formatFee, - addressColor, addressDotHtml, + blockieHtml, escapeHtml, displaySymbol, addressTitle, @@ -703,7 +725,6 @@ module.exports = { renderAddressHtml, copyableHtml, attachCopyHandlers, - etherscanAddressUrl, etherscanLinkHtml, explorerUrl, EXT_ICON, diff --git a/src/popup/views/settingsAddToken.js b/src/popup/views/settingsAddToken.js index 94f479b..2789e8f 100644 --- a/src/popup/views/settingsAddToken.js +++ b/src/popup/views/settingsAddToken.js @@ -12,7 +12,7 @@ function isTracked(address) { return state.trackedTokens.some((t) => t.address.toLowerCase() === lower); } -function tokenLabel(t) { +function nameAndSymbol(t) { return t.name ? t.name + " (" + t.symbol + ")" : t.symbol; } @@ -60,7 +60,7 @@ function renderDropdown() { let html = ''; for (const t of tokens) { const tracked = isTracked(t.address); - const label = tokenLabel(t) + (tracked ? " (tracked)" : ""); + const label = nameAndSymbol(t) + (tracked ? " (tracked)" : ""); html += `