chore(aegis): 0.8.4 — network chip on its own row + coin-catalogue search
Two follow-ups on 0.8.0's currency picker after a testing pass: - The network label was a tiny gray suffix next to the wallet name in the header, so "which network am I on" wasn't obvious at a glance. Now it's a separate row of one clickable chip under the coin ticker; clicking jumps into the browse:chain view where the network strip actually switches network. - Clicking the header opened a "Pick a coin" pane that only listed coins the user already had a wallet for — a first-time user with a single BCH wallet would see one row and no way to add more. Now it shows every supported coin behind a search box; owned coins jump to the wallet list, unowned coins jump straight into the create-wallet flow with that coin's network group pre-expanded.
This commit is contained in:
parent
b32b353db7
commit
e03c728116
3 changed files with 143 additions and 29 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "aegis",
|
"id": "aegis",
|
||||||
"name": "Aegis Wallet",
|
"name": "Aegis Wallet",
|
||||||
"version": "0.8.3",
|
"version": "0.8.4",
|
||||||
"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",
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,24 @@
|
||||||
#drop .brnet .hint { color: currentColor; opacity: .65; font-size: 11px; }
|
#drop .brnet .hint { color: currentColor; opacity: .65; font-size: 11px; }
|
||||||
.ttag { display: inline-block; font-size: 9.5px; letter-spacing: .06em; padding: 1px 5px; border-radius: 3px;
|
.ttag { display: inline-block; font-size: 9.5px; letter-spacing: .06em; padding: 1px 5px; border-radius: 3px;
|
||||||
background: rgba(224,179,65,.18); color: #e0b341; font-weight: 700; vertical-align: middle; margin-left: 2px; }
|
background: rgba(224,179,65,.18); color: #e0b341; font-weight: 700; vertical-align: middle; margin-left: 2px; }
|
||||||
#hNet { color: var(--dim); font-size: 11px; margin-left: 4px; font-weight: 500; }
|
/* 0.8.4: hNet became a proper chip on its own row (.netrow) so the
|
||||||
|
network the wallet is on is easy to spot AND easy to switch. */
|
||||||
|
.netrow { display: flex; margin-top: 6px; padding: 0 2px; }
|
||||||
|
.netchip { background: rgba(255,255,255,.05); border: 1px solid var(--line);
|
||||||
|
color: var(--mut); border-radius: 999px; padding: 3px 10px 3px 8px;
|
||||||
|
font: inherit; font-size: 11.5px; cursor: pointer; display: inline-flex;
|
||||||
|
align-items: center; gap: 6px; line-height: 1.2; }
|
||||||
|
.netchip:hover { border-color: var(--acid, #d6ff3d); color: var(--acid, #d6ff3d); }
|
||||||
|
.netchip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: .7; }
|
||||||
|
/* Currency-picker search + all-coins catalogue (0.8.4). */
|
||||||
|
#drop .pickersearch { padding: 8px; border-bottom: 1px solid var(--line); }
|
||||||
|
#drop .pickersearch input { width: 100%; box-sizing: border-box; background: rgba(255,255,255,.04);
|
||||||
|
border: 1px solid var(--line); color: var(--ink); border-radius: 7px;
|
||||||
|
padding: 7px 10px; font: inherit; font-size: 12.5px; outline: none; }
|
||||||
|
#drop .pickersearch input:focus { border-color: rgb(from var(--acid, #d6ff3d) r g b / .55); }
|
||||||
|
#drop .catgroup { padding: 4px 6px 2px 10px; color: var(--dim); font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; }
|
||||||
|
#drop .row.unowned { opacity: .8; }
|
||||||
|
#drop .row.unowned .v { color: var(--acid, #d6ff3d); font-weight: 600; font-size: 11px; }
|
||||||
.fiat { color: var(--dim); font-size: 12.5px; margin-left: 10px; font-weight: 500; letter-spacing: .2px; }
|
.fiat { color: var(--dim); font-size: 12.5px; margin-left: 10px; font-weight: 500; letter-spacing: .2px; }
|
||||||
.portfolio { margin-top: 6px; color: var(--mut); font-size: 11.5px; }
|
.portfolio { margin-top: 6px; color: var(--mut); font-size: 11.5px; }
|
||||||
.portfolio b { color: var(--ink); font-weight: 600; }
|
.portfolio b { color: var(--ink); font-weight: 600; }
|
||||||
|
|
@ -500,7 +517,6 @@
|
||||||
<div class="t">
|
<div class="t">
|
||||||
<span class="badge" id="hBadge"></span>
|
<span class="badge" id="hBadge"></span>
|
||||||
<span class="lbl" id="hLabel">Aegis Wallet</span>
|
<span class="lbl" id="hLabel">Aegis Wallet</span>
|
||||||
<span id="hNet"></span>
|
|
||||||
<!-- Edit chip sits inline with the wallet name so it reads as
|
<!-- Edit chip sits inline with the wallet name so it reads as
|
||||||
"edit THIS wallet". Was in picker-actions on the far right
|
"edit THIS wallet". Was in picker-actions on the far right
|
||||||
until 0.7.2, which mixed poorly with the global + button. -->
|
until 0.7.2, which mixed poorly with the global + button. -->
|
||||||
|
|
@ -514,6 +530,12 @@
|
||||||
<button type="button" id="hAdd" class="chip" title="Add or import a wallet">+</button>
|
<button type="button" id="hAdd" class="chip" title="Add or import a wallet">+</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 0.8.4: network picker is its own row below the coin label so it
|
||||||
|
reads as "the network I'm on right now" instead of a tiny suffix
|
||||||
|
next to the wallet name. Click cycles / opens the network menu. -->
|
||||||
|
<div class="netrow" id="hNetRow" hidden>
|
||||||
|
<button type="button" class="netchip" id="hNet" title="Switch network"></button>
|
||||||
|
</div>
|
||||||
<div id="drop" hidden></div>
|
<div id="drop" hidden></div>
|
||||||
<div class="bal">
|
<div class="bal">
|
||||||
<div class="big">
|
<div class="big">
|
||||||
|
|
|
||||||
|
|
@ -378,6 +378,19 @@ $("pickerBtn").addEventListener("click", (e) => {
|
||||||
d.hidden = false;
|
d.hidden = false;
|
||||||
fillPicker();
|
fillPicker();
|
||||||
});
|
});
|
||||||
|
// 0.8.4: separate netchip row. Click jumps straight to the browse:chain
|
||||||
|
// view for the current wallet's chain — the network-chip strip at the
|
||||||
|
// top of that view is what actually switches networks.
|
||||||
|
$("hNet").addEventListener("click", (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const w = (state?.wallets || []).find((x) => x.id === state?.selectedWalletId);
|
||||||
|
if (!w) return;
|
||||||
|
pickerTab = "browse:" + w.chain;
|
||||||
|
const d = $("drop");
|
||||||
|
positionDropBelowTabs(d);
|
||||||
|
d.hidden = false;
|
||||||
|
fillPicker();
|
||||||
|
});
|
||||||
// + Add and ⋯ More chips moved from the wallet strip into the header
|
// + Add and ⋯ More chips moved from the wallet strip into the header
|
||||||
// (0.6.31). Same handlers as before — fillPicker for the Add-only picker,
|
// (0.6.31). Same handlers as before — fillPicker for the Add-only picker,
|
||||||
// openMoreMenu for Import/Connect/About. Each stopsPropagation so the
|
// openMoreMenu for Import/Connect/About. Each stopsPropagation so the
|
||||||
|
|
@ -426,6 +439,11 @@ document.addEventListener("click", (e) => {
|
||||||
// user who opens the picker → picks Import → cancels → reopens returns to
|
// user who opens the picker → picks Import → cancels → reopens returns to
|
||||||
// Wallets (the sane default).
|
// Wallets (the sane default).
|
||||||
let pickerTab = "wallets";
|
let pickerTab = "wallets";
|
||||||
|
// 0.8.4: coin catalogue search query + one-shot preselected chain for the
|
||||||
|
// Add pane (set when the browse view routes into "add" for a specific
|
||||||
|
// unowned coin).
|
||||||
|
let browseQuery = "";
|
||||||
|
let pickerAddChain = null;
|
||||||
|
|
||||||
function fillPicker() {
|
function fillPicker() {
|
||||||
const d = $("drop");
|
const d = $("drop");
|
||||||
|
|
@ -508,42 +526,94 @@ function fillPicker() {
|
||||||
walletsByChain.get(w.chain).push(w);
|
walletsByChain.get(w.chain).push(w);
|
||||||
}
|
}
|
||||||
if (!focused) {
|
if (!focused) {
|
||||||
// COINS: one row per chain user has a wallet for.
|
// 0.8.4: full catalogue with search. Every supported chain is
|
||||||
const rows = [];
|
// listed, whether the user already has a wallet on it or not.
|
||||||
for (const [chain, ws] of walletsByChain) {
|
// Rows for owned coins jump to that coin's wallet list; rows for
|
||||||
const first = ws[0];
|
// unowned coins jump straight into the Add-wallet flow for that
|
||||||
const nets = Array.from(new Set(ws.map((w) => w.network)));
|
// coin, so the header picker doubles as a fast on-ramp.
|
||||||
let active = activeNetworkByChain.get(chain);
|
const q = String(browseQuery || "").trim().toLowerCase();
|
||||||
if (!active || !nets.includes(active)) active = nets.includes("mainnet") ? "mainnet" : nets[0];
|
const matches = (c) => !q
|
||||||
const netLbl = networkLabelFor(chain, active, active);
|
|| String(c.chain || "").toLowerCase().includes(q)
|
||||||
const totalUnits = sumGroupUnits(ws.filter((w) => w.network === active));
|
|| String(c.label || "").toLowerCase().includes(q)
|
||||||
const dec = first.decimals || 8;
|
|| String(c.ticker || "").toLowerCase().includes(q)
|
||||||
const bal = fmtBig(totalUnits || 0, dec) + " " + esc(first.ticker || chain.toUpperCase());
|
|| String(c.short || "").toLowerCase().includes(q);
|
||||||
const usd = usdOf(chain, totalUnits || 0, dec);
|
const ownedRows = [];
|
||||||
const fiat = usd != null ? `<div class="fs">${esc(fmtFiat(usd))}</div>` : "";
|
const otherRows = [];
|
||||||
const activeMark = ws.some((w) => w.id === state?.selectedWalletId) ? "on" : "";
|
for (const c of coins) {
|
||||||
rows.push(`<div class="row ${activeMark}" data-browse-chain="${esc(chain)}" style="cursor:pointer">
|
if (!matches(c)) continue;
|
||||||
${logoSvg(first.logo, 22)}
|
const ws = walletsByChain.get(c.chain) || [];
|
||||||
<div class="m">
|
if (ws.length > 0) {
|
||||||
<div class="l">${esc(first.coinLabel || chain.toUpperCase())} <span class="hint" style="font-weight:400">· ${ws.length} wallet${ws.length === 1 ? "" : "s"}${nets.length > 1 ? ` · ${esc(netLbl)}` : ""}</span></div>
|
const first = ws[0];
|
||||||
<div class="s mono">${esc(chain)}</div>
|
const nets = Array.from(new Set(ws.map((w) => w.network)));
|
||||||
</div>
|
let active = activeNetworkByChain.get(c.chain);
|
||||||
<div class="v"><div>${bal}</div>${fiat}</div>
|
if (!active || !nets.includes(active)) active = nets.includes("mainnet") ? "mainnet" : nets[0];
|
||||||
</div>`);
|
const netLbl = networkLabelFor(c.chain, active, active);
|
||||||
|
const totalUnits = sumGroupUnits(ws.filter((w) => w.network === active));
|
||||||
|
const dec = first.decimals || c.decimals || 8;
|
||||||
|
const bal = fmtBig(totalUnits || 0, dec) + " " + esc(first.ticker || c.ticker || c.chain.toUpperCase());
|
||||||
|
const usd = usdOf(c.chain, totalUnits || 0, dec);
|
||||||
|
const fiat = usd != null ? `<div class="fs">${esc(fmtFiat(usd))}</div>` : "";
|
||||||
|
const activeMark = ws.some((w) => w.id === state?.selectedWalletId) ? "on" : "";
|
||||||
|
ownedRows.push(`<div class="row ${activeMark}" data-browse-chain="${esc(c.chain)}" style="cursor:pointer">
|
||||||
|
${logoSvg(c.logo, 22)}
|
||||||
|
<div class="m">
|
||||||
|
<div class="l">${esc(c.label)} <span class="hint" style="font-weight:400">· ${ws.length} wallet${ws.length === 1 ? "" : "s"}${nets.length > 1 ? ` · ${esc(netLbl)}` : ""}</span></div>
|
||||||
|
<div class="s mono">${esc(c.ticker || c.chain)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="v"><div>${bal}</div>${fiat}</div>
|
||||||
|
</div>`);
|
||||||
|
} else {
|
||||||
|
otherRows.push(`<div class="row unowned" data-browse-add="${esc(c.chain)}" style="cursor:pointer">
|
||||||
|
${logoSvg(c.logo, 22)}
|
||||||
|
<div class="m">
|
||||||
|
<div class="l">${esc(c.label)}</div>
|
||||||
|
<div class="s mono">${esc(c.ticker || c.chain)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="v">+ Add</div>
|
||||||
|
</div>`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
d.innerHTML = `
|
d.innerHTML = `
|
||||||
<div class="droptabs">
|
<div class="droptabs">
|
||||||
<span style="flex:1;font-size:12.5px;color:var(--dim);padding-left:10px">Pick a coin</span>
|
<span style="flex:1;font-size:12.5px;color:var(--dim);padding-left:10px">Pick a coin</span>
|
||||||
<button class="closex" id="pickerClose" title="Close">✕</button>
|
<button class="closex" id="pickerClose" title="Close">✕</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pickersearch"><input id="pickerSearch" type="search" autocomplete="off" spellcheck="false"
|
||||||
|
placeholder="Search coins by name or ticker…" value="${esc(q)}"></div>
|
||||||
<div class="droppane" style="padding:6px">
|
<div class="droppane" style="padding:6px">
|
||||||
${rows.length ? rows.join("") : `<div class="hint" style="padding:14px;text-align:center">No wallets yet. Use + to create one.</div>`}
|
${ownedRows.length ? `<div class="catgroup">Your wallets</div>${ownedRows.join("")}` : ""}
|
||||||
|
${otherRows.length ? `<div class="catgroup" style="margin-top:6px">Add a new wallet</div>${otherRows.join("")}` : ""}
|
||||||
|
${(!ownedRows.length && !otherRows.length) ? `<div class="hint" style="padding:14px;text-align:center">No coins match "${esc(q)}".</div>` : ""}
|
||||||
</div>`;
|
</div>`;
|
||||||
d.querySelectorAll("[data-browse-chain]").forEach((row) => row.addEventListener("click", (e) => {
|
d.querySelectorAll("[data-browse-chain]").forEach((row) => row.addEventListener("click", (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
pickerTab = "browse:" + row.dataset.browseChain;
|
pickerTab = "browse:" + row.dataset.browseChain;
|
||||||
fillPicker();
|
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";
|
||||||
|
pickerAddChain = chain;
|
||||||
|
fillPicker();
|
||||||
|
}));
|
||||||
|
const searchEl = d.querySelector("#pickerSearch");
|
||||||
|
if (searchEl) {
|
||||||
|
searchEl.addEventListener("input", () => {
|
||||||
|
browseQuery = searchEl.value;
|
||||||
|
// Preserve caret across re-render.
|
||||||
|
const caret = searchEl.selectionStart;
|
||||||
|
fillPicker();
|
||||||
|
const s2 = d.querySelector("#pickerSearch");
|
||||||
|
if (s2) { s2.focus(); try { s2.setSelectionRange(caret, caret); } catch (_e) {} }
|
||||||
|
});
|
||||||
|
// Autofocus on first render, but not on every re-render — the
|
||||||
|
// re-focus above handles that. Guard with a data flag.
|
||||||
|
if (!searchEl.dataset.autof) { searchEl.dataset.autof = "1"; searchEl.focus(); }
|
||||||
|
}
|
||||||
const closeBtn = d.querySelector("#pickerClose");
|
const closeBtn = d.querySelector("#pickerClose");
|
||||||
if (closeBtn) closeBtn.addEventListener("click", (e) => { e.stopPropagation(); d.hidden = true; });
|
if (closeBtn) closeBtn.addEventListener("click", (e) => { e.stopPropagation(); d.hidden = true; });
|
||||||
return;
|
return;
|
||||||
|
|
@ -734,6 +804,21 @@ function fillPicker() {
|
||||||
group.hidden = !group.hidden;
|
group.hidden = !group.hidden;
|
||||||
r.querySelector(".caret").textContent = group.hidden ? "▸" : "▾";
|
r.querySelector(".caret").textContent = group.hidden ? "▸" : "▾";
|
||||||
}));
|
}));
|
||||||
|
// 0.8.4: if the browse view routed here with a preselected chain (user
|
||||||
|
// clicked "+ Add" on an unowned coin), expand that coin's network
|
||||||
|
// group AND scroll it into view so the user sees which networks
|
||||||
|
// exist without a second click.
|
||||||
|
if (pickerTab === "add" && pickerAddChain) {
|
||||||
|
const target = pickerAddChain;
|
||||||
|
pickerAddChain = null;
|
||||||
|
const row = d.querySelector(`.coinrow[data-coin="${target.replace(/["\\]/g, "")}"]`);
|
||||||
|
const group = d.querySelector(`#netgroup-${target.replace(/["\\]/g, "")}`);
|
||||||
|
if (row && group) {
|
||||||
|
group.hidden = false;
|
||||||
|
const caret = row.querySelector(".caret"); if (caret) caret.textContent = "▾";
|
||||||
|
row.scrollIntoView({ block: "nearest" });
|
||||||
|
}
|
||||||
|
}
|
||||||
d.querySelectorAll("[data-add]").forEach((r) => r.addEventListener("click", async () => {
|
d.querySelectorAll("[data-add]").forEach((r) => r.addEventListener("click", async () => {
|
||||||
const [c, n] = r.dataset.add.split(":");
|
const [c, n] = r.dataset.add.split(":");
|
||||||
d.hidden = true;
|
d.hidden = true;
|
||||||
|
|
@ -2586,9 +2671,16 @@ function render() {
|
||||||
// <wallet label> <coin · network + optional TEST tag>
|
// <wallet label> <coin · network + optional TEST tag>
|
||||||
$("hBadge").innerHTML = s?.meta?.logo ? logoSvg(s.meta.logo, 22) : logoSvg(null, 22);
|
$("hBadge").innerHTML = s?.meta?.logo ? logoSvg(s.meta.logo, 22) : logoSvg(null, 22);
|
||||||
$("hLabel").textContent = s?.label || "Aegis Wallet";
|
$("hLabel").textContent = s?.label || "Aegis Wallet";
|
||||||
$("hNet").innerHTML = s?.meta
|
// 0.8.4: hNet is a chip on its own row. Show only the network name
|
||||||
? `${esc(s.meta.coinLabel)} · ${esc(s.meta.networkLabel)}${s.meta.testnet ? " " + testnetTag() : ""}`
|
// (coin name is already in hLabel above); hide the row when we don't
|
||||||
: "";
|
// have any wallet-context to describe yet.
|
||||||
|
if (s?.meta) {
|
||||||
|
$("hNet").innerHTML = `${esc(s.meta.networkLabel)}${s.meta.testnet ? " " + testnetTag() : ""}`;
|
||||||
|
$("hNetRow").hidden = false;
|
||||||
|
} else {
|
||||||
|
$("hNet").innerHTML = "";
|
||||||
|
$("hNetRow").hidden = true;
|
||||||
|
}
|
||||||
if (showLock) {
|
if (showLock) {
|
||||||
renderLockScreen(phase);
|
renderLockScreen(phase);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue