Split popup into one file per view
All checks were successful
check / check (push) Successful in 4s

popup/index.js reduced to ~75 lines: loads state, builds a
shared context object, initializes all views, shows first screen.

Each view in popup/views/:
  helpers.js      — $(), showError, hideError, showView
  welcome.js      — welcome screen
  addWallet.js    — unified create/import recovery phrase
  importKey.js    — private key import
  home.js         — wallet list, total value, address derivation
  addressDetail.js — address view, token list, QR, copy
  send.js         — send form, ENS resolution, tx broadcast
  receive.js      — QR + copy
  addToken.js     — token lookup, common token picker
  settings.js     — RPC endpoint
  approval.js     — dApp approval (stub)

Views communicate via a ctx object with shared callbacks
(renderWalletList, showAddressDetail, doRefreshAndRender, etc).
This commit is contained in:
2026-02-25 18:51:41 +07:00
parent f50a2a0389
commit 023d8441bc
12 changed files with 673 additions and 581 deletions

View File

@@ -0,0 +1,103 @@
const { $, showError, hideError, showView } = require("./helpers");
const {
generateMnemonic,
hdWalletFromMnemonic,
isValidMnemonic,
} = require("../../shared/wallet");
const { encryptWithPassword } = require("../../shared/vault");
const { state, saveState } = require("../../shared/state");
function show() {
$("wallet-mnemonic").value = "";
$("add-wallet-password").value = "";
$("add-wallet-password-confirm").value = "";
$("add-wallet-phrase-warning").classList.add("hidden");
hideError("add-wallet-error");
showView("add-wallet");
}
function init(ctx) {
$("btn-generate-phrase").addEventListener("click", () => {
$("wallet-mnemonic").value = generateMnemonic();
$("add-wallet-phrase-warning").classList.remove("hidden");
});
$("btn-add-wallet-confirm").addEventListener("click", async () => {
const mnemonic = $("wallet-mnemonic").value.trim();
if (!mnemonic) {
showError(
"add-wallet-error",
"Please enter a recovery phrase or press the die to generate one.",
);
return;
}
const words = mnemonic.split(/\s+/);
if (words.length !== 12 && words.length !== 24) {
showError(
"add-wallet-error",
"Recovery phrase must be 12 or 24 words. You entered " +
words.length +
".",
);
return;
}
if (!isValidMnemonic(mnemonic)) {
showError(
"add-wallet-error",
"Invalid recovery phrase. Please check for typos.",
);
return;
}
const pw = $("add-wallet-password").value;
const pw2 = $("add-wallet-password-confirm").value;
if (!pw) {
showError("add-wallet-error", "Please choose a password.");
return;
}
if (pw.length < 8) {
showError(
"add-wallet-error",
"Password must be at least 8 characters.",
);
return;
}
if (pw !== pw2) {
showError("add-wallet-error", "Passwords do not match.");
return;
}
hideError("add-wallet-error");
const encrypted = await encryptWithPassword(mnemonic, pw);
const { xpub, firstAddress } = hdWalletFromMnemonic(mnemonic);
const walletNum = state.wallets.length + 1;
state.wallets.push({
type: "hd",
name: "Wallet " + walletNum,
xpub: xpub,
encryptedSecret: encrypted,
nextIndex: 1,
addresses: [
{ address: firstAddress, balance: "0.0000", tokenBalances: [] },
],
});
state.hasWallet = true;
await saveState();
ctx.renderWalletList();
showView("main");
});
$("btn-add-wallet-back").addEventListener("click", () => {
if (!state.hasWallet) {
showView("welcome");
} else {
ctx.renderWalletList();
showView("main");
}
});
$("btn-add-wallet-import-key").addEventListener(
"click",
ctx.showImportKeyView,
);
}
module.exports = { init, show };