diff --git a/src/popup/index.html b/src/popup/index.html index bc25e3d..74dfb69 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -358,12 +358,6 @@ - - -
+ + +
@@ -708,9 +708,7 @@

Wallets

-

- Add a new wallet from a recovery phrase or private key. -

+
+ + + ` + - `
${dot}` + + const knownToken = TOKEN_BY_ADDRESS.get(tokenId.toLowerCase()); + const projectUrl = knownToken && knownToken.url ? knownToken.url : null; + let infoHtml = `
Contract Address
`; + infoHtml += + `
${dot}` + `${escapeHtml(tokenId)}` + `${EXT_ICON}` + `
`; - const details = []; if (tokenName) - details.push(`Name: ${tokenName}`); + infoHtml += `
Name: ${tokenName}
`; if (tokenSymbol) - details.push( - `Symbol: ${tokenSymbol}`, - ); + infoHtml += `
Symbol: ${tokenSymbol}
`; if (tokenDecimals != null) - details.push( - `Decimals: ${tokenDecimals}`, - ); + infoHtml += `
Decimals: ${tokenDecimals}
`; if (tokenHolders != null) - details.push( - `Holders: ${Number(tokenHolders).toLocaleString()}`, - ); - if (details.length > 0) { - infoHtml += `
${details.join("")}
`; - } + infoHtml += `
Holders: ${Number(tokenHolders).toLocaleString()}
`; + if (projectUrl) + infoHtml += ``; contractInfo.innerHTML = infoHtml; contractInfo.classList.remove("hidden"); } else { diff --git a/src/popup/views/deleteWallet.js b/src/popup/views/deleteWallet.js new file mode 100644 index 0000000..49e47bf --- /dev/null +++ b/src/popup/views/deleteWallet.js @@ -0,0 +1,90 @@ +const { $, showView, showFlash } = require("./helpers"); +const { state, saveState } = require("../../shared/state"); +const { decryptWithPassword } = require("../../shared/vault"); + +let deleteWalletIndex = null; +let ctx = null; + +function show(walletIdx) { + deleteWalletIndex = walletIdx; + const wallet = state.wallets[walletIdx]; + $("delete-wallet-name").textContent = + wallet.name || "Wallet " + (walletIdx + 1); + $("delete-wallet-password").value = ""; + $("delete-wallet-flash").textContent = ""; + $("delete-wallet-flash").classList.add("hidden"); + showView("delete-wallet-confirm"); +} + +function init(_ctx) { + ctx = _ctx; + + $("btn-delete-wallet-back").addEventListener("click", () => { + deleteWalletIndex = null; + ctx.showSettingsView(); + }); + + $("btn-delete-wallet-confirm").addEventListener("click", async () => { + const pw = $("delete-wallet-password").value; + if (!pw) { + $("delete-wallet-flash").textContent = + "Please enter your password."; + $("delete-wallet-flash").classList.remove("hidden"); + return; + } + + if (deleteWalletIndex === null) { + $("delete-wallet-flash").textContent = + "No wallet selected for deletion."; + $("delete-wallet-flash").classList.remove("hidden"); + return; + } + + const walletIdx = deleteWalletIndex; + const wallet = state.wallets[walletIdx]; + + // Verify password against the wallet's encrypted data + try { + await decryptWithPassword(wallet.encryptedSecret, pw); + } catch (_e) { + $("delete-wallet-flash").textContent = "Wrong password."; + $("delete-wallet-flash").classList.remove("hidden"); + return; + } + + // Collect addresses to clean up from allowedSites/deniedSites + const addresses = (wallet.addresses || []).map((a) => a.address); + + // Remove wallet + state.wallets.splice(walletIdx, 1); + + // Clean up site permissions for deleted addresses + for (const addr of addresses) { + delete state.allowedSites[addr]; + delete state.deniedSites[addr]; + } + + deleteWalletIndex = null; + + if (state.wallets.length === 0) { + // No wallets left — reset selection and show welcome + state.selectedWallet = null; + state.selectedAddress = null; + state.activeAddress = null; + await saveState(); + showView("welcome"); + } else { + // Switch to first wallet if deleted wallet was active + state.selectedWallet = 0; + state.selectedAddress = 0; + state.activeAddress = + state.wallets[0].addresses[0]?.address || null; + await saveState(); + ctx.renderWalletList(); + ctx.showSettingsView(); + showFlash("Wallet deleted."); + } + }); +} + +module.exports = { init, show }; diff --git a/src/popup/views/helpers.js b/src/popup/views/helpers.js index 5d9daa8..e5b71d0 100644 --- a/src/popup/views/helpers.js +++ b/src/popup/views/helpers.js @@ -25,6 +25,7 @@ const VIEWS = [ "receive", "add-token", "settings", + "delete-wallet-confirm", "settings-addtoken", "transaction", "approve-site", diff --git a/src/popup/views/settings.js b/src/popup/views/settings.js index 8562b96..ea67337 100644 --- a/src/popup/views/settings.js +++ b/src/popup/views/settings.js @@ -2,6 +2,7 @@ const { $, showView, showFlash, escapeHtml } = require("./helpers"); const { state, saveState } = require("../../shared/state"); const { ETHEREUM_MAINNET_CHAIN_ID } = require("../../shared/constants"); const { log, debugFetch } = require("../../shared/log"); +const deleteWallet = require("./deleteWallet"); const runtime = typeof browser !== "undefined" ? browser.runtime : chrome.runtime; @@ -65,11 +66,68 @@ function 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; renderTrackedTokens(); renderSiteLists(); + renderWalletListSettings(); + showView("settings"); } @@ -83,6 +141,8 @@ function renderSiteLists() { } function init(ctx) { + deleteWallet.init(ctx); + $("btn-save-rpc").addEventListener("click", async () => { const url = $("settings-rpc").value.trim(); if (!url) {