- Tracked Tokens well in settings with [x] remove buttons - New settings-addtoken view with: - Top-10 quick-pick buttons (tracked ones dimmed+disabled) - Top-100 dropdown showing "Token Name (SYMBOL)", tracked disabled - Manual contract address entry with RPC lookup - View comment in helpers.js about keeping README in sync
This commit is contained in:
159
src/popup/views/settingsAddToken.js
Normal file
159
src/popup/views/settingsAddToken.js
Normal file
@@ -0,0 +1,159 @@
|
||||
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, """)}"` +
|
||||
`${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, """)}"` +
|
||||
`${tracked ? " disabled" : ""}>${label}</option>`;
|
||||
}
|
||||
sel.innerHTML = html;
|
||||
}
|
||||
|
||||
function show() {
|
||||
$("settings-addtoken-address").value = "";
|
||||
$("settings-addtoken-info").classList.add("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.classList.remove("hidden");
|
||||
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.classList.add("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.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { init, show };
|
||||
Reference in New Issue
Block a user