// AutistMask popup entry point. // Loads state, initializes views, triggers first render. const { DEBUG } = require("../shared/constants"); const { state, saveState, loadState, currentNetwork, } = require("../shared/state"); const { refreshPrices } = require("../shared/prices"); const { refreshBalances } = require("../shared/balances"); const { $, showView, setRenderMain, pushCurrentView, goBack, clearViewStack, } = require("./views/helpers"); const { applyTheme } = require("./theme"); const home = require("./views/home"); const welcome = require("./views/welcome"); const addWallet = require("./views/addWallet"); const addressDetail = require("./views/addressDetail"); const addressToken = require("./views/addressToken"); const send = require("./views/send"); const confirmTx = require("./views/confirmTx"); const txStatus = require("./views/txStatus"); const transactionDetail = require("./views/transactionDetail"); const receive = require("./views/receive"); const addToken = require("./views/addToken"); const settings = require("./views/settings"); const settingsAddToken = require("./views/settingsAddToken"); const approval = require("./views/approval"); function renderWalletList() { home.render(ctx); } let refreshInFlight = false; async function doRefreshAndRender() { if (refreshInFlight) return; refreshInFlight = true; try { await Promise.all([ refreshPrices(), refreshBalances( state.wallets, state.rpcUrl, state.blockscoutUrl, state.trackedTokens, ), ]); state.lastBalanceRefresh = Date.now(); await saveState(); renderWalletList(); } finally { refreshInFlight = false; } } const ctx = { renderWalletList, doRefreshAndRender, showAddWalletView: () => { pushCurrentView(); addWallet.show(); }, showAddressDetail: () => { pushCurrentView(); addressDetail.show(); }, showAddressToken: () => { pushCurrentView(); addressToken.show(); }, showAddTokenView: () => { pushCurrentView(); addToken.show(); }, showConfirmTx: (txInfo) => { pushCurrentView(); confirmTx.show(txInfo); }, showReceive: () => { pushCurrentView(); receive.show(); }, showTransactionDetail: (tx) => { pushCurrentView(); transactionDetail.show(tx); }, showSettingsView: () => { pushCurrentView(); settings.show(); }, showSettingsAddTokenView: () => { pushCurrentView(); settingsAddToken.show(); }, }; // Views that can be fully re-rendered from persisted state. // All others fall back to the nearest restorable parent. const RESTORABLE_VIEWS = new Set([ "main", "address", "address-token", "receive", "settings", "settings-addtoken", "confirm-tx", "transaction", "success-tx", "error-tx", ]); function needsAddress(view) { return ( view === "address" || view === "address-token" || view === "receive" || view === "transaction" ); } function hasValidAddress() { return ( state.selectedWallet !== null && state.selectedAddress !== null && state.wallets[state.selectedWallet] && state.wallets[state.selectedWallet].addresses[state.selectedAddress] ); } function restoreView() { const view = state.currentView; if (!view || !RESTORABLE_VIEWS.has(view)) { return fallbackView(); } if (needsAddress(view) && !hasValidAddress()) { return fallbackView(); } if (view === "address-token" && !state.selectedToken) { return fallbackView(); } switch (view) { case "address": addressDetail.show(); break; case "address-token": addressToken.show(); break; case "receive": receive.show(); break; case "settings": settings.show(); break; case "settings-addtoken": settingsAddToken.show(); break; case "confirm-tx": if (state.viewData && state.viewData.pendingTx) { confirmTx.restore(); } else { fallbackView(); } break; case "transaction": if (state.viewData && state.viewData.tx) { transactionDetail.render(); } else { fallbackView(); } break; case "success-tx": if (state.viewData && state.viewData.hash) { txStatus.renderSuccess(); } else { fallbackView(); } break; case "error-tx": if (state.viewData && state.viewData.message) { txStatus.renderError(); } else { fallbackView(); } break; default: fallbackView(); break; } } function fallbackView() { renderWalletList(); showView("main"); } async function init() { await loadState(); applyTheme(state.theme); const net = currentNetwork(); if (DEBUG || net.isTestnet) { const banner = document.createElement("div"); banner.id = "debug-banner"; if (DEBUG && net.isTestnet) { banner.textContent = "DEBUG / INSECURE [TESTNET]"; } else if (net.isTestnet) { banner.textContent = "[TESTNET]"; } else { banner.textContent = "DEBUG / INSECURE"; } banner.style.cssText = "background:#c00;color:#fff;text-align:center;font-size:10px;padding:1px 0;font-family:monospace;position:sticky;top:0;z-index:9999;"; document.body.prepend(banner); } // Auto-default active address if ( state.activeAddress === null && state.wallets.length > 0 && state.wallets[0].addresses.length > 0 ) { state.activeAddress = state.wallets[0].addresses[0].address; await saveState(); } // Always init approval and txStatus — they may run in the approval popup window approval.init(ctx); txStatus.init(ctx); // Check for approval mode const params = new URLSearchParams(window.location.search); const approvalId = params.get("approval"); if (approvalId) { approval.show(approvalId); showView("approve-site"); return; } $("btn-settings").addEventListener("click", () => { if ( !document .getElementById("view-settings") .classList.contains("hidden") ) { goBack(); return; } pushCurrentView(); settings.show(); }); setRenderMain(renderWalletList); welcome.init(ctx); addWallet.init(ctx); home.init(ctx); addressDetail.init(ctx); addressToken.init(ctx); send.init(ctx); confirmTx.init(ctx); transactionDetail.init(ctx); receive.init(ctx); addToken.init(ctx); settings.init(ctx); settingsAddToken.init(ctx); if (!state.hasWallet) { showView("welcome"); } else { renderWalletList(); restoreView(); doRefreshAndRender(); setInterval(doRefreshAndRender, 10000); } } document.addEventListener("DOMContentLoaded", init);