Compare commits

...

6 Commits

Author SHA1 Message Date
e8c4dde81e refactor: consistent [x] delete buttons + [rename] with inline editing
Some checks failed
check / check (push) Has been cancelled
- Replace dashed-underline wallet name with plain text + [rename] button
- [rename] button matches existing [x] button style (border border-border px-1)
- Inline rename shows input field with [save] and [cancel] buttons
- Support Enter to save, Escape to cancel
- Wallet list row: Name [rename] [x] with flex justify-between layout
- All buttons use monochrome hover style (hover:bg-fg hover:text-bg)
2026-02-27 12:53:46 -08:00
c5066f9aaf fix: use consistent [x] delete buttons, add inline rename
All checks were successful
check / check (push) Successful in 23s
- Delete buttons now use [x] with border, matching token and site
  removal patterns in settings
- Wallet names are click-to-rename (inline input), matching the
  home view rename UX
2026-02-27 12:53:05 -08:00
459ffed1bc Merge branch 'main' into feat/delete-wallet
All checks were successful
check / check (push) Successful in 22s
2026-02-27 21:51:15 +01:00
53506bad7b feat: add wallet deletion from settings (closes #13)
All checks were successful
check / check (push) Successful in 23s
- 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
2026-02-27 12:48:04 -08:00
d5849c831b fix: rework wallet deletion per review feedback
All checks were successful
check / check (push) Successful in 21s
- Remove all red/danger styling, use standard monochrome colors
- Add wallet picker dropdown instead of relying on selectedWallet
- Fix encryptedSecret field name (was wallet.encrypted)
- Populate dropdown when settings view opens
- Confirmation modal uses standard border styling
2026-02-27 12:44:40 -08:00
user
6e4dcb2e4f feat: add wallet deletion from settings (closes #13)
All checks were successful
check / check (push) Successful in 22s
2026-02-27 12:11:46 -08:00
2 changed files with 177 additions and 3 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"
@@ -843,6 +841,39 @@
</p> </p>
<div id="settings-denied-sites"></div> <div id="settings-denied-sites"></div>
</div> </div>
<div
id="delete-wallet-confirm"
class="hidden border border-border border-dashed p-3 mt-2 mb-3"
>
<h3 class="font-bold mb-1">Confirm Deletion</h3>
<p class="text-xs text-muted mb-2">
Delete <strong id="delete-wallet-name"></strong>? This
is permanent. Any funds will be unrecoverable without
your recovery phrase.
</p>
<p class="text-xs mb-2">Enter your password to confirm:</p>
<input
type="password"
id="delete-wallet-password"
class="border border-border p-1 w-full text-sm bg-bg text-fg mb-2"
placeholder="Password"
/>
<div class="flex gap-2">
<button
id="btn-delete-wallet-confirm"
class="border border-border text-fg px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
>
Delete
</button>
<button
id="btn-delete-wallet-cancel"
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
>
Cancel
</button>
</div>
</div>
</div> </div>
<!-- ============ SETTINGS: ADD TOKEN ============ --> <!-- ============ SETTINGS: ADD TOKEN ============ -->

View File

@@ -2,6 +2,7 @@ const { $, showView, showFlash, escapeHtml } = require("./helpers");
const { state, saveState } = require("../../shared/state"); const { state, saveState } = require("../../shared/state");
const { ETHEREUM_MAINNET_CHAIN_ID } = require("../../shared/constants"); const { ETHEREUM_MAINNET_CHAIN_ID } = require("../../shared/constants");
const { log, debugFetch } = require("../../shared/log"); const { log, debugFetch } = require("../../shared/log");
const { decryptWithPassword } = require("../../shared/vault");
const runtime = const runtime =
typeof browser !== "undefined" ? browser.runtime : chrome.runtime; typeof browser !== "undefined" ? browser.runtime : chrome.runtime;
@@ -65,11 +66,87 @@ 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 class="wallet-name-label">${name}</span>`;
html += `<span class="flex gap-1">`;
html += `<button class="btn-rename-wallet border border-border px-1 hover:bg-fg hover:text-bg cursor-pointer" data-idx="${idx}">[rename]</button>`;
html += `<button class="btn-delete-wallet border border-border px-1 hover:bg-fg hover:text-bg cursor-pointer" data-idx="${idx}">[x]</button>`;
html += `</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");
});
});
container.querySelectorAll(".btn-rename-wallet").forEach((btn) => {
btn.addEventListener("click", () => {
const idx = parseInt(btn.dataset.idx, 10);
const wallet = state.wallets[idx];
const currentName = wallet.name || "Wallet " + (idx + 1);
const row = btn.closest(".flex.justify-between");
const nameSpan = row.querySelector(".wallet-name-label");
const buttonsSpan = row.querySelector("span.flex.gap-1");
nameSpan.innerHTML = `<input type="text" class="rename-wallet-input border border-border px-1 text-xs bg-bg text-fg" value="${escapeHtml(currentName)}" />`;
buttonsSpan.innerHTML =
`<button class="btn-rename-save border border-border px-1 hover:bg-fg hover:text-bg cursor-pointer">[save]</button>` +
`<button class="btn-rename-cancel border border-border px-1 hover:bg-fg hover:text-bg cursor-pointer">[cancel]</button>`;
const input = nameSpan.querySelector("input");
input.focus();
input.select();
const save = async () => {
const newName = input.value.trim();
if (newName) {
wallet.name = newName;
await saveState();
}
renderWalletListSettings();
};
buttonsSpan
.querySelector(".btn-rename-save")
.addEventListener("click", save);
buttonsSpan
.querySelector(".btn-rename-cancel")
.addEventListener("click", () => renderWalletListSettings());
input.addEventListener("keydown", (e) => {
if (e.key === "Enter") save();
if (e.key === "Escape") renderWalletListSettings();
});
});
});
}
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();
renderWalletListSettings();
deleteWalletIndex = null;
$("delete-wallet-confirm").classList.add("hidden");
showView("settings"); showView("settings");
} }
@@ -192,6 +269,72 @@ function init(ctx) {
ctx.renderWalletList(); ctx.renderWalletList();
showView("main"); showView("main");
}); });
$("btn-delete-wallet-cancel").addEventListener("click", () => {
$("delete-wallet-confirm").classList.add("hidden");
$("delete-wallet-password").value = "";
deleteWalletIndex = null;
});
$("btn-delete-wallet-confirm").addEventListener("click", async () => {
const pw = $("delete-wallet-password").value;
if (!pw) {
showFlash("Password required.");
return;
}
if (deleteWalletIndex === null) {
showFlash("No wallet selected for deletion.");
return;
}
const walletIdx = deleteWalletIndex;
const wallet = state.wallets[walletIdx];
// Verify password against the wallet's encrypted data
try {
await decryptWithPassword(wallet.encrypted, pw);
} catch (_e) {
showFlash("Wrong password.");
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();
$("delete-wallet-confirm").classList.add("hidden");
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();
$("delete-wallet-confirm").classList.add("hidden");
showFlash("Wallet deleted.");
renderWalletListSettings();
ctx.renderWalletList();
showView("main");
}
});
} }
module.exports = { init, show, renderSiteLists }; module.exports = { init, show, renderSiteLists };