Files
AutistMask/src/popup/views/settingsAddToken.js
clawbot 813993f17c
All checks were successful
check / check (push) Successful in 22s
fix: reserve space for all error/status messages — closes #123
Replace display:none (hidden class) with visibility:hidden/visible for all
error, warning, and status message elements across the extension UI. This
prevents layout shift when messages appear or disappear.

Changes:
- helpers.js: showError/hideError now use visibility instead of hidden class
- index.html: all error/status divs use visibility:hidden + min-height
- confirmTx.js: warnings, errors, fee section use visibility
- approval.js: tx-error, sign-error, danger-warning use visibility
- addressDetail.js: export-privkey-flash uses visibility
- deleteWallet.js: delete-wallet-flash uses visibility
- addWallet.js: phrase-warning uses visibility
- receive.js: erc20-warning uses visibility
- addToken.js: add-token-info uses visibility
- settingsAddToken.js: settings-addtoken-info uses visibility
2026-02-28 16:30:43 -08:00

163 lines
5.6 KiB
JavaScript

const { $, showView, showFlash } = require("./helpers");
const { getTopTokens } = require("../../shared/tokenList");
const { state, saveState } = require("../../shared/state");
const { lookupTokenInfo } = require("../../shared/balances");
const { isScamAddress } = require("../../shared/scamlist");
const { log } = require("../../shared/log");
let ctx;
function isTracked(address) {
const lower = address.toLowerCase();
return state.trackedTokens.some((t) => t.address.toLowerCase() === lower);
}
function tokenLabel(t) {
return t.name ? t.name + " (" + t.symbol + ")" : t.symbol;
}
function renderTop10() {
const el = $("settings-addtoken-top10");
el.innerHTML = getTopTokens(10)
.map((t) => {
const tracked = isTracked(t.address);
const cls = tracked
? "border border-border px-1 text-xs opacity-40 cursor-default"
: "border border-border px-1 hover:bg-fg hover:text-bg cursor-pointer text-xs";
return (
`<button class="settings-addtoken-quick ${cls}"` +
` data-address="${t.address}"` +
` data-symbol="${t.symbol}"` +
` data-decimals="${t.decimals}"` +
` data-name="${(t.name || "").replace(/"/g, "&quot;")}"` +
`${tracked ? " disabled" : ""}>${t.symbol}</button>`
);
})
.join("");
el.querySelectorAll(".settings-addtoken-quick:not([disabled])").forEach(
(btn) => {
btn.addEventListener("click", async () => {
const token = {
address: btn.dataset.address,
symbol: btn.dataset.symbol,
decimals: parseInt(btn.dataset.decimals, 10),
name: btn.dataset.name || btn.dataset.symbol,
};
state.trackedTokens.push(token);
await saveState();
showFlash("Added " + token.symbol);
renderTop10();
renderDropdown();
ctx.doRefreshAndRender();
});
},
);
}
function renderDropdown() {
const sel = $("settings-addtoken-select");
const tokens = getTopTokens(100);
let html = '<option value="">-- select --</option>';
for (const t of tokens) {
const tracked = isTracked(t.address);
const label = tokenLabel(t) + (tracked ? " (tracked)" : "");
html +=
`<option value="${t.address}"` +
` data-symbol="${t.symbol}"` +
` data-decimals="${t.decimals}"` +
` data-name="${(t.name || "").replace(/"/g, "&quot;")}"` +
`${tracked ? " disabled" : ""}>${label}</option>`;
}
sel.innerHTML = html;
}
function show() {
$("settings-addtoken-address").value = "";
$("settings-addtoken-info").textContent = "";
$("settings-addtoken-info").style.visibility = "hidden";
renderTop10();
renderDropdown();
showView("settings-addtoken");
}
function init(_ctx) {
ctx = _ctx;
$("btn-settings-addtoken-back").addEventListener("click", () => {
ctx.showSettingsView();
});
$("btn-settings-addtoken-select").addEventListener("click", async () => {
const sel = $("settings-addtoken-select");
const opt = sel.options[sel.selectedIndex];
if (!opt || !opt.value) {
showFlash("Please select a token.");
return;
}
if (isTracked(opt.value)) {
showFlash("Already tracked.");
return;
}
const token = {
address: opt.value,
symbol: opt.dataset.symbol,
decimals: parseInt(opt.dataset.decimals, 10),
name: opt.dataset.name || opt.dataset.symbol,
};
state.trackedTokens.push(token);
await saveState();
showFlash("Added " + token.symbol);
renderTop10();
renderDropdown();
ctx.doRefreshAndRender();
});
$("btn-settings-addtoken-manual").addEventListener("click", async () => {
const addr = $("settings-addtoken-address").value.trim();
if (!addr || !addr.startsWith("0x")) {
showFlash(
"Please enter a valid contract address starting with 0x.",
);
return;
}
if (isTracked(addr)) {
showFlash("Already tracked.");
return;
}
if (isScamAddress(addr)) {
showFlash("This address is on a known scam/fraud list.");
return;
}
const infoEl = $("settings-addtoken-info");
infoEl.textContent = "Looking up token...";
infoEl.style.visibility = "visible";
log.debugf("Looking up token contract", addr);
try {
const info = await lookupTokenInfo(addr, state.rpcUrl);
log.infof("Adding token", info.symbol, addr);
state.trackedTokens.push({
address: addr,
symbol: info.symbol,
decimals: info.decimals,
name: info.name,
});
await saveState();
showFlash("Added " + info.symbol);
$("settings-addtoken-address").value = "";
infoEl.textContent = "";
infoEl.style.visibility = "hidden";
renderTop10();
renderDropdown();
ctx.doRefreshAndRender();
} catch (e) {
const detail = e.shortMessage || e.message || String(e);
log.errorf("Token lookup failed for", addr, detail);
showFlash(detail);
infoEl.textContent = "";
infoEl.style.visibility = "hidden";
}
});
}
module.exports = { init, show };