fix(aegis): 0.9.6 — adding a wallet asks create-or-import
"+ 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 <TICKER> 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.
This commit is contained in:
parent
7b9049f6fc
commit
f07df2b39e
2 changed files with 82 additions and 10 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "aegis",
|
"id": "aegis",
|
||||||
"name": "Aegis Wallet",
|
"name": "Aegis Wallet",
|
||||||
"version": "0.9.5",
|
"version": "0.9.6",
|
||||||
"category": "plugin",
|
"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.",
|
"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",
|
"author": "Silent Mode",
|
||||||
|
|
|
||||||
|
|
@ -595,10 +595,12 @@ function fillPicker() {
|
||||||
d.querySelectorAll("[data-browse-add]").forEach((row) => row.addEventListener("click", (e) => {
|
d.querySelectorAll("[data-browse-add]").forEach((row) => row.addEventListener("click", (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const chain = row.dataset.browseAdd;
|
const chain = row.dataset.browseAdd;
|
||||||
// Jump straight into the create-wallet flow for that coin. The
|
// Offer the method chooser (New / Import / Connect) rather than
|
||||||
// Add pane keys off `pickerTab = "add"` and reads the target
|
// dropping straight into create — "add a BCH wallet" is just as
|
||||||
// chain from a scratch field the coin-picker fills.
|
// often "bring in the one I already have". pickerAddChain is
|
||||||
pickerTab = "add";
|
// module-level, so the coin stays preselected through whichever
|
||||||
|
// branch the user picks.
|
||||||
|
pickerTab = "method";
|
||||||
pickerAddChain = chain;
|
pickerAddChain = chain;
|
||||||
fillPicker();
|
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 = `
|
||||||
|
<div style="width:min(94vw,360px);background:var(--panel,#12161e);border:1px solid var(--line,#2a2f38);border-radius:10px;padding:14px 14px 12px;box-shadow:0 10px 40px rgba(0,0,0,.4)">
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
||||||
|
<span style="font-size:17px;line-height:1">${o.icon || "+"}</span>
|
||||||
|
<div style="font-weight:600;flex:1">${esc(o.title || "Choose")}</div>
|
||||||
|
</div>
|
||||||
|
${o.body ? `<div class="hint" style="margin-bottom:12px;line-height:1.5">${o.body}</div>` : ""}
|
||||||
|
<div style="display:flex;flex-direction:column;gap:6px">
|
||||||
|
${options.map((op) => `
|
||||||
|
<button class="btn ${op.primary ? "primary" : ""}" data-ac="${esc(op.id)}"
|
||||||
|
style="text-align:left;padding:9px 11px;display:block;width:100%">
|
||||||
|
<div style="font-weight:600">${esc(op.label)}</div>
|
||||||
|
${op.hint ? `<div class="hint" style="margin-top:2px;font-weight:400">${esc(op.hint)}</div>` : ""}
|
||||||
|
</button>`).join("")}
|
||||||
|
</div>
|
||||||
|
<div class="actions" style="justify-content:flex-end;margin-top:10px">
|
||||||
|
<button class="btn" data-ac="">${esc(o.cancelLabel || "Cancel")}</button>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
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
|
// Single-button sibling of aegisConfirm, for the error notices that used
|
||||||
// window.alert(). Fire-and-forget: callers don't need the result, so it
|
// window.alert(). Fire-and-forget: callers don't need the result, so it
|
||||||
// works from sync handlers too.
|
// 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.
|
// the coin list when the last address under a coin goes away.
|
||||||
|
|
||||||
el.querySelector("#stripAddMore").addEventListener("click", async () => {
|
el.querySelector("#stripAddMore").addEventListener("click", async () => {
|
||||||
// Add another wallet of the same coin+network directly, without
|
// This used to create a fresh wallet immediately, on the reasoning
|
||||||
// opening the picker sheet — the user is already inside this coin's
|
// that being inside a coin's address list made the intent
|
||||||
// address list so their intent is unambiguous.
|
// 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 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 <b>${esc(meta.coinName)}</b>.`,
|
||||||
|
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 {
|
try {
|
||||||
state = await S.invoke("addWallet", { chain: first.chain, network: first.network });
|
state = await S.invoke("addWallet", { chain: first.chain, network: first.network });
|
||||||
settingsFilled = false; render();
|
settingsFilled = false; render();
|
||||||
|
|
@ -2869,7 +2937,7 @@ function render() {
|
||||||
if (!ready && !fullLock) {
|
if (!ready && !fullLock) {
|
||||||
const copy = {
|
const copy = {
|
||||||
error: ["⚠", "This wallet could not start.", s?.error || ""],
|
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…", ""];
|
}[phase] || ["…", "Starting…", ""];
|
||||||
let form = "";
|
let form = "";
|
||||||
if (phase === "empty") {
|
if (phase === "empty") {
|
||||||
|
|
@ -2880,7 +2948,11 @@ function render() {
|
||||||
const btn = $("gateAddWallet");
|
const btn = $("gateAddWallet");
|
||||||
if (btn) btn.addEventListener("click", () => {
|
if (btn) btn.addEventListener("click", () => {
|
||||||
const d = $("drop");
|
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;
|
d.hidden = false;
|
||||||
fillPicker();
|
fillPicker();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue