// AutistMask popup UI — view management and event wiring // All wallet logic will live in background/; this file is purely UI. const VIEWS = [ "lock", "welcome", "create", "import-phrase", "import-key", "main", "add-wallet", "address", "send", "receive", "add-token", "settings", "approve", ]; function showView(name) { for (const v of VIEWS) { const el = document.getElementById(`view-${v}`); if (el) { el.classList.toggle("hidden", v !== name); } } } // -- mock state (will be replaced by background messaging) -- // A wallet is either { type: "hd", name, mnemonic, addresses: [...] } // or { type: "key", name, privateKey, addresses: [single] }. // Each address is { address, balance, tokens: [...] }. const state = { locked: true, hasWallet: false, password: null, wallets: [], selectedWallet: null, selectedAddress: null, rpcUrl: "https://eth.llamarpc.com", isFirstSetup: true, }; // -- helpers -- function $(id) { return document.getElementById(id); } function showError(id, msg) { const el = $(id); el.textContent = msg; el.classList.remove("hidden"); } function hideError(id) { $(id).classList.add("hidden"); } function truncateAddress(addr) { if (!addr) return ""; return addr.slice(0, 6) + "\u2026" + addr.slice(-4); } function makeStubAddress() { const hex = Array.from({ length: 40 }, () => Math.floor(Math.random() * 16).toString(16), ).join(""); return { address: "0x" + hex, balance: "0.0000", tokens: [], }; } // -- render wallet list on main view -- function renderWalletList() { const container = $("wallet-list"); if (state.wallets.length === 0) { container.innerHTML = '

No wallets yet. Add one to get started.

'; return; } let html = ""; state.wallets.forEach((wallet, wi) => { html += `
`; html += `
`; html += `${wallet.name}`; if (wallet.type === "hd") { html += ``; } html += `
`; wallet.addresses.forEach((addr, ai) => { html += `
`; html += `${truncateAddress(addr.address)}`; html += `${addr.balance} ETH`; html += `
`; }); html += `
`; }); container.innerHTML = html; // bind clicks on address rows container.querySelectorAll(".address-row").forEach((row) => { row.addEventListener("click", () => { state.selectedWallet = parseInt(row.dataset.wallet, 10); state.selectedAddress = parseInt(row.dataset.address, 10); showAddressDetail(); }); }); // bind clicks on + buttons within HD wallets container.querySelectorAll(".btn-add-address").forEach((btn) => { btn.addEventListener("click", (e) => { e.stopPropagation(); const wi = parseInt(btn.dataset.wallet, 10); state.wallets[wi].addresses.push(makeStubAddress()); renderWalletList(); }); }); } function showAddressDetail() { const wallet = state.wallets[state.selectedWallet]; const addr = wallet.addresses[state.selectedAddress]; $("address-title").textContent = wallet.name; $("address-full").textContent = addr.address; $("address-copied-msg").textContent = ""; $("address-eth-balance").textContent = addr.balance; $("address-usd-value").textContent = ""; updateTokenList(addr); updateSendTokenSelect(addr); showView("address"); } function updateTokenList(addr) { const list = $("token-list"); if (addr.tokens.length === 0) { list.innerHTML = '
No tokens added yet. Use "+ Add" to track a token.
'; return; } list.innerHTML = addr.tokens .map( (t) => `
` + `${t.symbol}` + `${t.balance || "0"}` + `
`, ) .join(""); } function updateSendTokenSelect(addr) { const sel = $("send-token"); sel.innerHTML = ''; addr.tokens.forEach((t) => { const opt = document.createElement("option"); opt.value = t.contractAddress; opt.textContent = t.symbol; sel.appendChild(opt); }); } function currentAddress() { if (state.selectedWallet === null || state.selectedAddress === null) { return null; } return state.wallets[state.selectedWallet].addresses[state.selectedAddress]; } function addWalletAndGoToMain(wallet) { state.wallets.push(wallet); state.hasWallet = true; state.isFirstSetup = false; renderWalletList(); showView("main"); } function showImportView(type) { if (type === "phrase") { $("import-mnemonic").value = ""; hideError("import-phrase-error"); const needsPw = state.isFirstSetup; $("import-phrase-password-section").classList.toggle( "hidden", !needsPw, ); $("import-phrase-password-confirm-section").classList.toggle( "hidden", !needsPw, ); showView("import-phrase"); } else { $("import-private-key").value = ""; hideError("import-key-error"); const needsPw = state.isFirstSetup; $("import-key-password-section").classList.toggle("hidden", !needsPw); $("import-key-password-confirm-section").classList.toggle( "hidden", !needsPw, ); showView("import-key"); } } function showCreateView() { // TODO: generate real mnemonic via background $("create-mnemonic").textContent = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; hideError("create-error"); showView("create"); } function validatePasswords(pwId, pw2Id, errorId) { if (!state.isFirstSetup) return true; const pw = $(pwId).value; const pw2 = $(pw2Id).value; if (!pw) { showError(errorId, "Please choose a password."); return false; } if (pw.length < 8) { showError(errorId, "Password must be at least 8 characters."); return false; } if (pw !== pw2) { showError(errorId, "Passwords do not match."); return false; } state.password = pw; return true; } // -- init -- function init() { if (!state.hasWallet) { showView("welcome"); } else if (state.locked) { showView("lock"); } else { renderWalletList(); showView("main"); } // -- Lock screen -- $("btn-unlock").addEventListener("click", () => { const pw = $("unlock-password").value; if (!pw) { showError("unlock-error", "Please enter your password."); return; } hideError("unlock-error"); // TODO: send unlock message to background state.locked = false; renderWalletList(); showView("main"); }); // -- Welcome -- $("btn-welcome-new").addEventListener("click", showCreateView); $("btn-welcome-recovery").addEventListener("click", () => showImportView("phrase"), ); $("btn-welcome-key").addEventListener("click", () => showImportView("key")); // -- Create wallet -- $("btn-create-confirm").addEventListener("click", () => { if ( !validatePasswords( "create-password", "create-password-confirm", "create-error", ) ) { return; } hideError("create-error"); const walletNum = state.wallets.length + 1; addWalletAndGoToMain({ type: "hd", name: "Wallet " + walletNum, mnemonic: $("create-mnemonic").textContent, addresses: [makeStubAddress()], }); }); $("btn-create-back").addEventListener("click", () => { showView(state.isFirstSetup ? "welcome" : "add-wallet"); }); // -- Import recovery phrase -- $("btn-import-phrase-confirm").addEventListener("click", () => { const mnemonic = $("import-mnemonic").value.trim(); if (!mnemonic) { showError( "import-phrase-error", "Please enter your recovery phrase.", ); return; } const words = mnemonic.split(/\s+/); if (words.length !== 12 && words.length !== 24) { showError( "import-phrase-error", "Recovery phrase must be 12 or 24 words. You entered " + words.length + ".", ); return; } if ( !validatePasswords( "import-phrase-password", "import-phrase-password-confirm", "import-phrase-error", ) ) { return; } hideError("import-phrase-error"); const walletNum = state.wallets.length + 1; addWalletAndGoToMain({ type: "hd", name: "Wallet " + walletNum, mnemonic: mnemonic, addresses: [makeStubAddress()], }); }); $("btn-import-phrase-back").addEventListener("click", () => { showView(state.isFirstSetup ? "welcome" : "add-wallet"); }); // -- Import private key -- $("btn-import-key-confirm").addEventListener("click", () => { const key = $("import-private-key").value.trim(); if (!key) { showError("import-key-error", "Please enter your private key."); return; } if ( !validatePasswords( "import-key-password", "import-key-password-confirm", "import-key-error", ) ) { return; } hideError("import-key-error"); const walletNum = state.wallets.length + 1; addWalletAndGoToMain({ type: "key", name: "Wallet " + walletNum, privateKey: key, addresses: [makeStubAddress()], }); }); $("btn-import-key-back").addEventListener("click", () => { showView(state.isFirstSetup ? "welcome" : "add-wallet"); }); // -- Main view -- $("btn-lock").addEventListener("click", () => { state.locked = true; $("unlock-password").value = ""; showView("lock"); }); $("btn-settings").addEventListener("click", () => { $("settings-rpc").value = state.rpcUrl; showView("settings"); }); $("btn-add-wallet").addEventListener("click", () => { showView("add-wallet"); }); // -- Add wallet menu (from main view) -- $("btn-add-wallet-new").addEventListener("click", showCreateView); $("btn-add-wallet-phrase").addEventListener("click", () => showImportView("phrase"), ); $("btn-add-wallet-key").addEventListener("click", () => showImportView("key"), ); $("btn-add-wallet-back").addEventListener("click", () => { showView("main"); }); // -- Address detail -- $("address-full").addEventListener("click", () => { const addr = $("address-full").textContent; if (addr) { navigator.clipboard.writeText(addr); $("address-copied-msg").textContent = "Copied!"; setTimeout(() => { $("address-copied-msg").textContent = ""; }, 2000); } }); $("btn-address-back").addEventListener("click", () => { renderWalletList(); showView("main"); }); $("btn-send").addEventListener("click", () => { $("send-to").value = ""; $("send-amount").value = ""; $("send-fee-estimate").classList.add("hidden"); $("send-status").classList.add("hidden"); showView("send"); }); $("btn-receive").addEventListener("click", () => { const addr = currentAddress(); $("receive-address").textContent = addr ? addr.address : ""; showView("receive"); }); $("btn-add-token").addEventListener("click", () => { $("add-token-address").value = ""; $("add-token-info").classList.add("hidden"); hideError("add-token-error"); showView("add-token"); }); // -- Send -- $("btn-send-confirm").addEventListener("click", () => { const to = $("send-to").value.trim(); const amount = $("send-amount").value.trim(); if (!to) { showError("send-status", "Please enter a recipient address."); $("send-status").classList.remove("hidden"); return; } if (!amount || isNaN(parseFloat(amount)) || parseFloat(amount) <= 0) { showError("send-status", "Please enter a valid amount."); $("send-status").classList.remove("hidden"); return; } // TODO: construct and send transaction via background const el = $("send-status"); el.textContent = "Sent! (stub)"; el.classList.remove("hidden"); }); $("btn-send-back").addEventListener("click", () => { showAddressDetail(); }); // -- Receive -- $("btn-receive-copy").addEventListener("click", () => { const addr = $("receive-address").textContent; if (addr) { navigator.clipboard.writeText(addr); } }); $("btn-receive-back").addEventListener("click", () => { showAddressDetail(); }); // -- Add Token -- $("btn-add-token-confirm").addEventListener("click", () => { const contractAddr = $("add-token-address").value.trim(); if (!contractAddr || !contractAddr.startsWith("0x")) { showError( "add-token-error", "Please enter a valid contract address starting with 0x.", ); return; } hideError("add-token-error"); // TODO: look up token name/symbol/decimals from contract via background const addr = currentAddress(); if (addr) { addr.tokens.push({ contractAddress: contractAddr, symbol: "TKN", decimals: 18, balance: "0", }); } showAddressDetail(); }); $("btn-add-token-back").addEventListener("click", () => { showAddressDetail(); }); // -- Settings -- $("btn-save-rpc").addEventListener("click", () => { state.rpcUrl = $("settings-rpc").value.trim(); // TODO: persist via background }); $("btn-settings-back").addEventListener("click", () => { renderWalletList(); showView("main"); }); // -- Approval -- $("btn-approve").addEventListener("click", () => { // TODO: send approval to background renderWalletList(); showView("main"); }); $("btn-reject").addEventListener("click", () => { // TODO: send rejection to background renderWalletList(); showView("main"); }); } document.addEventListener("DOMContentLoaded", init);