From f07df2b39e1e4efbbe503ca6a4545a237c6f40d0 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Wed, 23 Sep 2026 08:33:29 +0200 Subject: [PATCH] =?UTF-8?q?fix(aegis):=200.9.6=20=E2=80=94=20adding=20a=20?= =?UTF-8?q?wallet=20asks=20create-or-import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "+ Add another BCH" created a fresh wallet on the spot. The old comment argued that being inside a coin's address list made the intent unambiguous; it isn't. "Add another BCH wallet" is just as often "bring in the one I already have somewhere else", and guessing wrong is not harmless — the user gets an empty new address and has to work out for themselves why their funds aren't in it. Adds aegisChoose(), a pick-one sibling of aegisConfirm for branches where the honest answer is a question rather than a yes/no, and puts it in front of every path that reached addWallet: - + Add another in the coin drilldown - + Add on an unowned coin in the browse picker (now routes to the existing New / Import / Connect chooser, with the coin still preselected through whichever branch is taken) - + Add your first wallet on the empty state — the most important one, since someone arriving with an existing seed was being handed a create-only flow The empty-state copy claimed "there's no separate seed to import", which stopped being true when imports shipped and actively told users the feature they wanted did not exist. Verified in a rendered panel: the chooser appears, addWallet is not called until Create is picked, Import opens the import modal, and Cancel does nothing. --- bundled-addons/aegis/addon.json | 2 +- bundled-addons/aegis/panel.js | 90 +++++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/bundled-addons/aegis/addon.json b/bundled-addons/aegis/addon.json index f237fb85..faeb0158 100644 --- a/bundled-addons/aegis/addon.json +++ b/bundled-addons/aegis/addon.json @@ -1,7 +1,7 @@ { "id": "aegis", "name": "Aegis Wallet", - "version": "0.9.5", + "version": "0.9.6", "category": "plugin", "description": "Multi-chain wallet (BCH, BTC, TRX, ETH, SOL, SC, DGB) derived from your Theseus vault. Dapps get window.bitcoincash on .x sites; window.tronWeb / window.tronLink / window.ethereum / window.solana on any https page.", "author": "Silent Mode", diff --git a/bundled-addons/aegis/panel.js b/bundled-addons/aegis/panel.js index 98c3461c..1c54929b 100644 --- a/bundled-addons/aegis/panel.js +++ b/bundled-addons/aegis/panel.js @@ -595,10 +595,12 @@ function fillPicker() { d.querySelectorAll("[data-browse-add]").forEach((row) => row.addEventListener("click", (e) => { e.stopPropagation(); const chain = row.dataset.browseAdd; - // Jump straight into the create-wallet flow for that coin. The - // Add pane keys off `pickerTab = "add"` and reads the target - // chain from a scratch field the coin-picker fills. - pickerTab = "add"; + // Offer the method chooser (New / Import / Connect) rather than + // dropping straight into create — "add a BCH wallet" is just as + // often "bring in the one I already have". pickerAddChain is + // module-level, so the coin stays preselected through whichever + // branch the user picks. + pickerTab = "method"; pickerAddChain = chain; fillPicker(); })); @@ -880,6 +882,56 @@ function aegisConfirm(opts) { }); } +// Pick-one-of-several sibling of aegisConfirm, for branches where the +// honest answer is a question rather than a yes/no. Resolves the chosen +// option's `id`, or null if dismissed. +// opts: { title, body, icon, options: [{id, label, hint, primary}] } +function aegisChoose(opts) { + const o = opts || {}; + const options = Array.isArray(o.options) ? o.options : []; + return new Promise((resolve) => { + const overlay = document.createElement("div"); + overlay.style.cssText = "position:fixed;inset:0;background:rgba(0,0,0,.55);display:flex;align-items:flex-start;justify-content:center;z-index:100000;padding-top:24px"; + overlay.innerHTML = ` +
+
+ ${o.icon || "+"} +
${esc(o.title || "Choose")}
+
+ ${o.body ? `
${o.body}
` : ""} +
+ ${options.map((op) => ` + `).join("")} +
+
+ +
+
`; + document.body.appendChild(overlay); + let done = false; + const finish = (val) => { + if (done) return; + done = true; + document.removeEventListener("keydown", onKey, true); + try { overlay.remove(); } catch {} + resolve(val); + }; + const onKey = (e) => { if (e.key === "Escape") { e.stopPropagation(); finish(null); } }; + document.addEventListener("keydown", onKey, true); + overlay.addEventListener("click", (e) => { if (e.target === overlay) finish(null); }); + overlay.querySelectorAll("[data-ac]").forEach((b) => b.addEventListener("click", (e) => { + e.stopPropagation(); + finish(b.dataset.ac || null); + })); + const first = overlay.querySelector('[data-ac]:not([data-ac=""])'); + if (first) first.focus(); + }); +} + // Single-button sibling of aegisConfirm, for the error notices that used // window.alert(). Fire-and-forget: callers don't need the result, so it // works from sync handlers too. @@ -2597,10 +2649,26 @@ function renderInlineCoinList(el, groupKey, group) { // the coin list when the last address under a coin goes away. el.querySelector("#stripAddMore").addEventListener("click", async () => { - // Add another wallet of the same coin+network directly, without - // opening the picker sheet — the user is already inside this coin's - // address list so their intent is unambiguous. + // This used to create a fresh wallet immediately, on the reasoning + // that being inside a coin's address list made the intent + // unambiguous. It doesn't: "add another BCH wallet" is just as often + // "bring in the one I already have somewhere else". Creating instead + // of importing is not a harmless guess either — the user ends up with + // an empty new address and has to work out why their funds aren't + // there. So ask. const first = gw[0]; + const pick = await aegisChoose({ + title: `Add another ${meta.ticker} wallet`, + // meta.coinName already carries the network ("TRX Nile"), so naming + // networkLabel again read "TRX Nile · Nile testnet". + body: `On ${esc(meta.coinName)}.`, + options: [ + { id: "create", label: "Create a new wallet", hint: "Derived from your Theseus vault — nothing to write down", primary: true }, + { id: "import", label: "Import an existing wallet", hint: "BIP39 mnemonic, or a chain-native private key" }, + ], + }); + if (!pick) return; + if (pick === "import") { openImportModal(first.chain); return; } try { state = await S.invoke("addWallet", { chain: first.chain, network: first.network }); settingsFilled = false; render(); @@ -2869,7 +2937,7 @@ function render() { if (!ready && !fullLock) { const copy = { error: ["⚠", "This wallet could not start.", s?.error || ""], - empty: ["🛡", "No wallets yet.", "Aegis derives every wallet from your Theseus password vault — there's no separate seed to import. Pick a coin below to create your first one."], + empty: ["🛡", "No wallets yet.", "Aegis can derive a fresh wallet from your Theseus password vault — nothing extra to write down — or import one you already have from its seed phrase or private key."], }[phase] || ["…", "Starting…", ""]; let form = ""; if (phase === "empty") { @@ -2880,7 +2948,11 @@ function render() { const btn = $("gateAddWallet"); if (btn) btn.addEventListener("click", () => { const d = $("drop"); - pickerTab = "add"; + // First-run is the MOST important place to offer import: someone + // arriving with an existing seed who is handed a create-only flow + // ends up staring at an empty wallet wondering where their coins + // went. Method chooser, not straight to create. + pickerTab = "method"; d.hidden = false; fillPicker(); });