const { $, showView, showFlash, escapeHtml } = require("./helpers"); const { applyTheme } = require("../theme"); const { state, saveState, currentNetwork } = require("../../shared/state"); const { NETWORKS, SUPPORTED_CHAIN_IDS } = require("../../shared/networks"); const { onChainSwitch } = require("../../shared/chainSwitch"); const { log, debugFetch } = require("../../shared/log"); const deleteWallet = require("./deleteWallet"); const runtime = typeof browser !== "undefined" ? browser.runtime : chrome.runtime; function renderSiteList(containerId, siteMap, stateKey) { const container = $(containerId); const hostnames = [...new Set(Object.values(siteMap).flat())]; if (hostnames.length === 0) { container.innerHTML = '

None

'; return; } let html = ""; hostnames.forEach((hostname) => { html += `
`; html += `${hostname}`; html += ``; html += `
`; }); container.innerHTML = html; container.querySelectorAll(".btn-remove-site").forEach((btn) => { btn.addEventListener("click", async () => { const key = btn.dataset.key; const host = btn.dataset.hostname; for (const addr of Object.keys(state[key])) { state[key][addr] = state[key][addr].filter((h) => h !== host); if (state[key][addr].length === 0) { delete state[key][addr]; } } await saveState(); runtime.sendMessage({ type: "AUTISTMASK_REMOVE_SITE" }); renderSiteList(containerId, state[key], key); }); }); } function renderTrackedTokens() { const container = $("settings-tracked-tokens"); if (state.trackedTokens.length === 0) { container.innerHTML = '

None

'; return; } let html = ""; state.trackedTokens.forEach((token, idx) => { const label = token.name ? escapeHtml(token.name) + " (" + escapeHtml(token.symbol) + ")" : escapeHtml(token.symbol); html += `
`; html += `${label}`; html += ``; html += `
`; }); container.innerHTML = html; container.querySelectorAll(".btn-remove-token").forEach((btn) => { btn.addEventListener("click", async () => { const idx = parseInt(btn.dataset.idx, 10); state.trackedTokens.splice(idx, 1); await saveState(); renderTrackedTokens(); }); }); } function renderWalletListSettings() { const container = $("settings-wallet-list"); if (state.wallets.length === 0) { container.innerHTML = '

No wallets.

'; return; } let html = ""; state.wallets.forEach((wallet, idx) => { const name = escapeHtml(wallet.name || "Wallet " + (idx + 1)); html += `
`; html += `${name}`; html += ``; html += `
`; }); container.innerHTML = html; container.querySelectorAll(".btn-delete-wallet").forEach((btn) => { btn.addEventListener("click", () => { const idx = parseInt(btn.dataset.idx, 10); deleteWallet.show(idx); }); }); // Inline rename on click container.querySelectorAll(".settings-wallet-name").forEach((span) => { span.addEventListener("click", () => { const idx = parseInt(span.dataset.idx, 10); const wallet = state.wallets[idx]; const input = document.createElement("input"); input.type = "text"; input.className = "border border-border p-0 text-xs bg-bg text-fg w-full"; input.value = wallet.name || "Wallet " + (idx + 1); span.replaceWith(input); input.focus(); input.select(); const finish = async () => { const val = input.value.trim(); if (val && val !== wallet.name) { wallet.name = val; await saveState(); } renderWalletListSettings(); }; input.addEventListener("blur", finish); input.addEventListener("keydown", (e) => { if (e.key === "Enter") input.blur(); if (e.key === "Escape") { input.value = wallet.name || "Wallet " + (idx + 1); input.blur(); } }); }); }); } function show() { $("settings-rpc").value = state.rpcUrl; $("settings-blockscout").value = state.blockscoutUrl; const networkSelect = $("settings-network"); if (networkSelect) { networkSelect.value = state.networkId; } renderTrackedTokens(); renderSiteLists(); renderWalletListSettings(); showView("settings"); } function renderSiteLists() { renderSiteList( "settings-allowed-sites", state.allowedSites, "allowedSites", ); renderSiteList("settings-denied-sites", state.deniedSites, "deniedSites"); } function init(ctx) { deleteWallet.init(ctx); $("btn-save-rpc").addEventListener("click", async () => { const url = $("settings-rpc").value.trim(); if (!url) { showFlash("Please enter an RPC URL."); return; } showFlash("Testing endpoint..."); try { const resp = await debugFetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_chainId", params: [], }), }); const json = await resp.json(); if (json.error) { log.errorf("RPC validation error:", json.error); showFlash("Endpoint returned error: " + json.error.message); return; } const net = currentNetwork(); if (json.result !== net.chainId) { showFlash( "Wrong network (expected " + net.name + ", got chain " + json.result + ").", ); return; } } catch (e) { log.errorf("RPC validation fetch failed:", e.message); showFlash("Could not reach endpoint."); return; } state.rpcUrl = url; await saveState(); showFlash("Saved."); }); $("btn-save-blockscout").addEventListener("click", async () => { const url = $("settings-blockscout").value.trim(); if (!url) { showFlash("Please enter a Blockscout API URL."); return; } showFlash("Testing endpoint..."); try { const resp = await debugFetch(url + "/stats"); if (!resp.ok) { showFlash("Endpoint returned HTTP " + resp.status + "."); return; } } catch (e) { log.errorf("Blockscout validation failed:", e.message); showFlash("Could not reach endpoint."); return; } state.blockscoutUrl = url; await saveState(); showFlash("Saved."); }); const networkSelect = $("settings-network"); if (networkSelect) { networkSelect.addEventListener("change", async () => { const newId = networkSelect.value; const net = await onChainSwitch(newId); $("settings-rpc").value = state.rpcUrl; $("settings-blockscout").value = state.blockscoutUrl; showFlash("Switched to " + net.name + "."); }); } $("settings-show-zero-balances").checked = state.showZeroBalanceTokens; $("settings-show-zero-balances").addEventListener("change", async () => { state.showZeroBalanceTokens = $("settings-show-zero-balances").checked; await saveState(); }); $("settings-theme").value = state.theme; $("settings-theme").addEventListener("change", async () => { state.theme = $("settings-theme").value; await saveState(); applyTheme(state.theme); }); $("settings-hide-low-holders").checked = state.hideLowHolderTokens; $("settings-hide-low-holders").addEventListener("change", async () => { state.hideLowHolderTokens = $("settings-hide-low-holders").checked; await saveState(); }); $("settings-hide-fraud-contracts").checked = state.hideFraudContracts; $("settings-hide-fraud-contracts").addEventListener("change", async () => { state.hideFraudContracts = $("settings-hide-fraud-contracts").checked; await saveState(); }); $("settings-hide-dust").checked = state.hideDustTransactions; $("settings-hide-dust").addEventListener("change", async () => { state.hideDustTransactions = $("settings-hide-dust").checked; await saveState(); }); $("settings-dust-threshold").value = state.dustThresholdGwei; $("settings-dust-threshold").addEventListener("change", async () => { const val = parseInt($("settings-dust-threshold").value, 10); if (!isNaN(val) && val >= 0) { state.dustThresholdGwei = val; await saveState(); } }); $("settings-utc-timestamps").checked = state.utcTimestamps; $("settings-utc-timestamps").addEventListener("change", async () => { state.utcTimestamps = $("settings-utc-timestamps").checked; await saveState(); }); $("btn-main-add-wallet").addEventListener("click", ctx.showAddWalletView); $("btn-settings-add-token").addEventListener( "click", ctx.showSettingsAddTokenView, ); $("btn-settings-back").addEventListener("click", () => { ctx.renderWalletList(); showView("main"); }); } module.exports = { init, show, renderSiteLists };