feat: add wallet deletion from settings (closes #13)

- Per-wallet [delete] links in settings wallet list
- Monochrome styling throughout, no red/danger colors
- Password confirmation modal with warning text
- Cleans up site permissions for deleted addresses
- Switches to first remaining wallet or shows welcome if none left
This commit is contained in:
2026-02-27 12:46:03 -08:00
committed by user
parent 34cd72be88
commit 655b90c7df
2 changed files with 50 additions and 51 deletions

View File

@@ -708,9 +708,7 @@
<div class="bg-well p-3 mx-1 mb-3"> <div class="bg-well p-3 mx-1 mb-3">
<h3 class="font-bold mb-1">Wallets</h3> <h3 class="font-bold mb-1">Wallets</h3>
<p class="text-xs text-muted mb-2"> <div id="settings-wallet-list" class="mb-2"></div>
Add a new wallet from a recovery phrase or private key.
</p>
<button <button
id="btn-main-add-wallet" id="btn-main-add-wallet"
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer" class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
@@ -844,35 +842,15 @@
<div id="settings-denied-sites"></div> <div id="settings-denied-sites"></div>
</div> </div>
<div
class="bg-well p-3 mx-1 mb-3 border border-dashed border-red-500"
>
<h3 class="font-bold mb-1 text-red-500">Danger Zone</h3>
<p class="text-xs text-muted mb-2">
Permanently delete the currently selected wallet. This
cannot be undone. Make sure you have backed up your
recovery phrase before proceeding.
</p>
<button
id="btn-delete-wallet"
class="border border-red-500 text-red-500 px-2 py-1 hover:bg-red-500 hover:text-bg cursor-pointer"
>
Delete Wallet
</button>
</div>
<div <div
id="delete-wallet-confirm" id="delete-wallet-confirm"
class="hidden bg-well p-3 mx-1 mb-3 border border-dashed border-red-500" class="hidden border border-border border-dashed p-3 mt-2 mb-3"
> >
<h3 class="font-bold mb-1 text-red-500"> <h3 class="font-bold mb-1">Confirm Deletion</h3>
Confirm Deletion
</h3>
<p class="text-xs text-muted mb-2"> <p class="text-xs text-muted mb-2">
Deleting <strong id="delete-wallet-name"></strong> is Delete <strong id="delete-wallet-name"></strong>? This
permanent. Any funds on this wallet will be is permanent. Any funds will be unrecoverable without
unrecoverable if you have not backed up your recovery your recovery phrase.
phrase.
</p> </p>
<p class="text-xs mb-2">Enter your password to confirm:</p> <p class="text-xs mb-2">Enter your password to confirm:</p>
<input <input
@@ -884,9 +862,9 @@
<div class="flex gap-2"> <div class="flex gap-2">
<button <button
id="btn-delete-wallet-confirm" id="btn-delete-wallet-confirm"
class="border border-red-500 text-red-500 px-2 py-1 hover:bg-red-500 hover:text-bg cursor-pointer" class="border border-border text-fg px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
> >
Delete permanently Delete
</button> </button>
<button <button
id="btn-delete-wallet-cancel" id="btn-delete-wallet-cancel"

View File

@@ -66,20 +66,43 @@ function renderTrackedTokens() {
}); });
} }
let deleteWalletIndex = null;
function renderWalletListSettings() {
const container = $("settings-wallet-list");
if (state.wallets.length === 0) {
container.innerHTML = '<p class="text-xs text-muted">No wallets.</p>';
return;
}
let html = "";
state.wallets.forEach((wallet, idx) => {
const name = escapeHtml(wallet.name || "Wallet " + (idx + 1));
html += `<div class="flex justify-between items-center text-xs py-1 border-b border-border-light">`;
html += `<span>${name}</span>`;
html += `<span class="btn-delete-wallet border-b border-dashed border-fg text-fg cursor-pointer" data-idx="${idx}">[delete]</span>`;
html += `</div>`;
});
container.innerHTML = html;
container.querySelectorAll(".btn-delete-wallet").forEach((btn) => {
btn.addEventListener("click", () => {
const idx = parseInt(btn.dataset.idx, 10);
const wallet = state.wallets[idx];
deleteWalletIndex = idx;
$("delete-wallet-name").textContent =
wallet.name || "Wallet " + (idx + 1);
$("delete-wallet-password").value = "";
$("delete-wallet-confirm").classList.remove("hidden");
});
});
}
function show() { function show() {
$("settings-rpc").value = state.rpcUrl; $("settings-rpc").value = state.rpcUrl;
$("settings-blockscout").value = state.blockscoutUrl; $("settings-blockscout").value = state.blockscoutUrl;
renderTrackedTokens(); renderTrackedTokens();
renderSiteLists(); renderSiteLists();
// Populate wallet deletion dropdown renderWalletListSettings();
const sel = $("delete-wallet-select"); deleteWalletIndex = null;
sel.innerHTML = "";
for (let i = 0; i < state.wallets.length; i++) {
const opt = document.createElement("option");
opt.value = i;
opt.textContent = state.wallets[i].name || "Wallet " + (i + 1);
sel.appendChild(opt);
}
$("delete-wallet-confirm").classList.add("hidden"); $("delete-wallet-confirm").classList.add("hidden");
showView("settings"); showView("settings");
@@ -205,20 +228,10 @@ function init(ctx) {
showView("main"); showView("main");
}); });
$("btn-delete-wallet").addEventListener("click", () => {
if (state.selectedWallet === null || state.wallets.length === 0) {
showFlash("No wallet selected.");
return;
}
const wallet = state.wallets[state.selectedWallet];
$("delete-wallet-name").textContent = wallet.name || "this wallet";
$("delete-wallet-password").value = "";
$("delete-wallet-confirm").classList.remove("hidden");
});
$("btn-delete-wallet-cancel").addEventListener("click", () => { $("btn-delete-wallet-cancel").addEventListener("click", () => {
$("delete-wallet-confirm").classList.add("hidden"); $("delete-wallet-confirm").classList.add("hidden");
$("delete-wallet-password").value = ""; $("delete-wallet-password").value = "";
deleteWalletIndex = null;
}); });
$("btn-delete-wallet-confirm").addEventListener("click", async () => { $("btn-delete-wallet-confirm").addEventListener("click", async () => {
@@ -228,7 +241,12 @@ function init(ctx) {
return; return;
} }
const walletIdx = state.selectedWallet; if (deleteWalletIndex === null) {
showFlash("No wallet selected for deletion.");
return;
}
const walletIdx = deleteWalletIndex;
const wallet = state.wallets[walletIdx]; const wallet = state.wallets[walletIdx];
// Verify password against the wallet's encrypted data // Verify password against the wallet's encrypted data
@@ -251,6 +269,8 @@ function init(ctx) {
delete state.deniedSites[addr]; delete state.deniedSites[addr];
} }
deleteWalletIndex = null;
if (state.wallets.length === 0) { if (state.wallets.length === 0) {
// No wallets left — reset selection and show welcome // No wallets left — reset selection and show welcome
state.selectedWallet = null; state.selectedWallet = null;
@@ -268,6 +288,7 @@ function init(ctx) {
await saveState(); await saveState();
$("delete-wallet-confirm").classList.add("hidden"); $("delete-wallet-confirm").classList.add("hidden");
showFlash("Wallet deleted."); showFlash("Wallet deleted.");
renderWalletListSettings();
ctx.renderWalletList(); ctx.renderWalletList();
showView("main"); showView("main");
} }