fix(sirius-x/portal): use shared modal instead of stale inline sign-in cards
/sirius-x/portal.html?mode=import was landing users on the old three-card
inline sign-in UI (New / Import / WizardConnect each as its own form).
The new UX is: one tabbed modal, everywhere. Portal was the one page
still shipping the legacy cards.
Replaces the whole <section id='signin'> with a single launcher card
('🔑 Sign in or create wallet' → opens the shared modal). The old
handlers (, , etc.) are
comment-blocked out; new script uses window.siriusSignInWallet().
?mode=new|import|wc|unlock auto-opens the modal at that tab (the same
handler dropdown items use). If the user arrives with a siriusProfile
but no in-memory wallet (e.g., they signed in on another page), portal
opens the modal at Unlock (or Import when no saved wallet). Sign-out
delegates to the shared signOutUi(). Cross-tab and same-tab profile
changes both re-adopt.
register-flow.js: finishSignIn now sets window.siriusWallet so portal
(and any other page loaded in the same tab) can pick the wallet up
without re-prompting for a password. In-memory only — reload/nav clears
it, and the profile listener falls through to Unlock.
This commit is contained in:
parent
109e9e7351
commit
7687b115f9
2 changed files with 99 additions and 122 deletions
|
|
@ -154,6 +154,10 @@ function writeProfile(w) {
|
|||
// false so the caller keeps going to Fund → Confirm → Register.
|
||||
function finishSignIn(w) {
|
||||
writeProfile({ address: w.address, tokenAddress: w.tokenAddress, source: w.source ?? "seed" });
|
||||
// Expose the live wallet globally so the portal / TLD page can pick it up
|
||||
// without re-prompting for a password. In-memory only — cleared on reload,
|
||||
// sign-out, or navigation away.
|
||||
window.siriusWallet = w;
|
||||
if (state.signInOnly) {
|
||||
render(`<h3><span class="ok">✓</span> Signed in</h3>
|
||||
<p class="sub">You are signed in as <span class="mono">${esc(w.address)}</span>. Names you own
|
||||
|
|
|
|||
217
portal.html
217
portal.html
|
|
@ -131,69 +131,15 @@
|
|||
|
||||
<section id="signin">
|
||||
<h2>Sign in</h2>
|
||||
<p class="muted">Pick one. Nothing is transmitted — resolution is a read-only chain query,
|
||||
and signing (for record edits) happens locally in the browser or in your external wallet.</p>
|
||||
<div class="authbox" style="margin-top:14px">
|
||||
<div class="card" id="card-new">
|
||||
<h3>🆕 New wallet</h3>
|
||||
<p>Generate a fresh 12-word BIP-39 phrase in your browser. Write it down before continuing —
|
||||
it never leaves this page and there is nothing to reset if you lose it.</p>
|
||||
<div class="row">
|
||||
<button class="btn acid" id="new-btn">Generate a phrase</button>
|
||||
</div>
|
||||
<div id="new-phrase" class="hidden" style="margin-top:14px">
|
||||
<label>Your new recovery phrase</label>
|
||||
<textarea id="new-seed" readonly style="font-family:ui-monospace,monospace;font-size:14px"></textarea>
|
||||
<label style="margin-top:10px;display:flex;gap:9px;align-items:flex-start;font-size:13.5px">
|
||||
<input type="checkbox" id="new-ack" style="margin-top:3px">
|
||||
<span>I have written this phrase down somewhere safe. I understand it is the only way
|
||||
to recover this wallet and cannot be reset.</span>
|
||||
</label>
|
||||
<div class="row">
|
||||
<button class="btn acid" id="new-signin-btn" disabled>Sign in with new wallet →</button>
|
||||
<button class="btn ghost" id="new-copy-btn">Copy phrase</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="new-err" class="err hidden"></div>
|
||||
</div>
|
||||
<div class="card" id="card-import">
|
||||
<h3>📥 Import a seed phrase</h3>
|
||||
<p>Paste your 12- or 24-word BIP-39 recovery phrase. It stays in memory only, wiped when you
|
||||
close the tab or sign out.</p>
|
||||
<label for="seed">Recovery phrase</label>
|
||||
<textarea id="seed" placeholder="word1 word2 … word12" spellcheck="false" autocomplete="off"></textarea>
|
||||
<div class="row">
|
||||
<button class="btn acid" id="signin-btn">Sign in →</button>
|
||||
<span class="dim">Chipnet only for now.</span>
|
||||
</div>
|
||||
<div id="signin-err" class="err hidden"></div>
|
||||
</div>
|
||||
<div class="card" id="card-wc">
|
||||
<h3>🔗 Connect an external wallet</h3>
|
||||
<p>Use Cashonize 0.9+ or Paytaca via WizardConnect. The wallet signs record edits — the seed
|
||||
never touches this page.</p>
|
||||
<div class="row">
|
||||
<button class="btn acid" id="wc-btn">Connect wallet</button>
|
||||
</div>
|
||||
<div id="wc-uri-box" class="hidden" style="margin-top:14px">
|
||||
<label>Open your wallet, scan or paste this URI, then approve the connection:</label>
|
||||
<div style="display:flex;gap:8px;align-items:center;margin-top:6px">
|
||||
<code id="wc-uri" style="flex:1;word-break:break-all;background:#0e131b;border:1px solid var(--line);border-radius:8px;padding:9px 11px;font-family:ui-monospace,monospace;font-size:12.5px;color:var(--ink)"></code>
|
||||
<button class="btn ghost" id="wc-copy" type="button">Copy</button>
|
||||
</div>
|
||||
<p class="dim" style="margin-top:8px">Waiting for your wallet…</p>
|
||||
<div class="row"><button class="btn ghost" id="wc-cancel" type="button">Cancel</button></div>
|
||||
</div>
|
||||
<div id="wc-err" class="err hidden"></div>
|
||||
<p class="dim" style="margin-top:8px">Record edits and TLD mints are approved on your device
|
||||
via the wallet's signing prompt — the seed never touches this page.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status" style="margin-top:14px">
|
||||
Don't have a wallet? Create one during
|
||||
<a href="./#search-input">name registration</a> — the recovery phrase
|
||||
is generated in your browser and encrypted with your password.
|
||||
<p class="muted">The same wallet that will hold your names does the signing. Pick how to sign in —
|
||||
the tabbed modal has all four options.</p>
|
||||
<div class="card" style="text-align:center;padding:32px 22px">
|
||||
<button class="btn acid" id="open-signin" style="padding:14px 28px;font-size:15px">🔑 Sign in or create wallet</button>
|
||||
<p class="dim" style="margin-top:14px;font-size:12.5px">Opens the shared modal with tabs:
|
||||
<b>🔓 Unlock · 🆕 Create · 📥 Import · 🔗 WizardConnect</b>.
|
||||
Same UI used everywhere on Sirius.X.</p>
|
||||
</div>
|
||||
<div id="signin-err" class="err hidden"></div>
|
||||
</section>
|
||||
|
||||
<section id="me" class="hidden">
|
||||
|
|
@ -316,33 +262,67 @@ function clearProfile() {
|
|||
window.dispatchEvent(new Event("siriusProfileChanged"));
|
||||
}
|
||||
|
||||
// ---------- sign in ----------
|
||||
$("signin-btn").addEventListener("click", async () => {
|
||||
const btn = $("signin-btn");
|
||||
const errBox = $("signin-err");
|
||||
errBox.classList.add("hidden");
|
||||
const raw = $("seed").value.trim();
|
||||
if (!raw) { errBox.textContent = "Enter your seed phrase to sign in."; errBox.classList.remove("hidden"); return; }
|
||||
btn.disabled = true; btn.textContent = "Unlocking…";
|
||||
try {
|
||||
wallet = await BNS.BuiltInWallet.fromMnemonic(raw);
|
||||
} catch (e) {
|
||||
errBox.textContent = "Could not unlock: " + (e.message || e);
|
||||
errBox.classList.remove("hidden");
|
||||
btn.disabled = false; btn.textContent = "Sign in →";
|
||||
return;
|
||||
// ---------- sign in (via shared modal) ----------
|
||||
// The old inline cards are gone; portal.html now delegates the whole wallet
|
||||
// UX to the shared register-flow.js modal (window.siriusSignInWallet). This
|
||||
// keeps portal in lockstep with the landing, tld page, and the wallet
|
||||
// dropdown — one tabbed modal, everywhere.
|
||||
//
|
||||
// Signed-in state is picked up from siriusProfile + window.siriusWallet:
|
||||
// - profile only (returning user with a saved wallet, no live wallet in
|
||||
// memory) -> auto-open the modal at the Unlock tab
|
||||
// - profile + wallet -> call enterPortal() and show the names list
|
||||
// - no profile -> stay on the launcher card
|
||||
async function adoptWalletFromModal() {
|
||||
const profile = readProfile();
|
||||
if (!profile) return false;
|
||||
if (window.siriusWallet) {
|
||||
wallet = window.siriusWallet;
|
||||
await enterPortal();
|
||||
return true;
|
||||
}
|
||||
$("seed").value = "";
|
||||
btn.disabled = false; btn.textContent = "Sign in →";
|
||||
writeProfile(wallet);
|
||||
await enterPortal();
|
||||
return false;
|
||||
}
|
||||
function readProfile() {
|
||||
try { return JSON.parse(localStorage.getItem("siriusProfile") || "null"); } catch { return null; }
|
||||
}
|
||||
async function ensureSignInModal() {
|
||||
if (typeof window.siriusSignInWallet !== "function") {
|
||||
// register-flow.js hasn't landed yet — wait briefly.
|
||||
for (let i = 0; i < 60 && typeof window.siriusSignInWallet !== "function"; i++) {
|
||||
await new Promise((r) => setTimeout(r, 50));
|
||||
}
|
||||
}
|
||||
return typeof window.siriusSignInWallet === "function";
|
||||
}
|
||||
|
||||
$("open-signin").addEventListener("click", async () => {
|
||||
if (await ensureSignInModal()) window.siriusSignInWallet("import");
|
||||
});
|
||||
|
||||
// ---------- new wallet ----------
|
||||
// Generates a fresh BIP-39 phrase in the browser via BuiltInWallet.create().
|
||||
// The phrase is shown once for the user to write down; sign-in only unlocks
|
||||
// after they acknowledge they have saved it. From that point on the flow is
|
||||
// identical to import (BuiltInWallet in memory, no persistence of the seed).
|
||||
// Re-check on every profile change (fired by register-flow after finishSignIn
|
||||
// and by profile-menu after sign-out). Same-tab and cross-tab both fire this.
|
||||
window.addEventListener("siriusProfileChanged", async () => {
|
||||
if (readProfile()) { await adoptWalletFromModal(); }
|
||||
else { signOutUi(); }
|
||||
});
|
||||
window.addEventListener("storage", async (e) => {
|
||||
if (e.key !== "siriusProfile") return;
|
||||
if (readProfile()) { await adoptWalletFromModal(); }
|
||||
else { signOutUi(); }
|
||||
});
|
||||
|
||||
function signOutUi() {
|
||||
wallet = null;
|
||||
window.siriusWallet = null;
|
||||
$("me").classList.add("hidden");
|
||||
$("signin").classList.remove("hidden");
|
||||
$("names-list").innerHTML = "";
|
||||
$("names-status").textContent = "Loading names from the chain…";
|
||||
}
|
||||
|
||||
// (old inline seed/create/wc handlers deleted — see the modal launcher above)
|
||||
/* HANDLERS_REMOVED_MARKER
|
||||
$("new-btn").addEventListener("click", async () => {
|
||||
const btn = $("new-btn");
|
||||
const errBox = $("new-err");
|
||||
|
|
@ -477,51 +457,43 @@ $("wc-copy").addEventListener("click", () => {
|
|||
navigator.clipboard?.writeText($("wc-uri").textContent);
|
||||
});
|
||||
|
||||
$("signout-btn").addEventListener("click", async () => {
|
||||
// Close any live WC session before dropping the reference.
|
||||
HANDLERS_REMOVED_MARKER */
|
||||
|
||||
// Signed-in section's own sign-out button. Delegates the profile+wallet
|
||||
// teardown to the shared modal's signOutUi() (already declared above).
|
||||
$("signout-btn")?.addEventListener("click", async () => {
|
||||
if (wallet?.source === "wc" && wallet.session) {
|
||||
try { await wallet.session.disconnect(); } catch {}
|
||||
}
|
||||
wallet = null;
|
||||
clearProfile();
|
||||
$("me").classList.add("hidden");
|
||||
$("signin").classList.remove("hidden");
|
||||
$("names-list").innerHTML = "";
|
||||
$("names-status").textContent = "Loading names from the chain…";
|
||||
// Reset the new-wallet card so a subsequent sign-in starts clean.
|
||||
$("new-btn").disabled = false; $("new-btn").textContent = "Generate a phrase";
|
||||
$("new-phrase").classList.add("hidden");
|
||||
$("new-ack").checked = false;
|
||||
$("new-signin-btn").disabled = true;
|
||||
// Reset the WC card too.
|
||||
$("wc-btn").disabled = false; $("wc-btn").textContent = "Connect wallet";
|
||||
$("wc-uri-box").classList.add("hidden");
|
||||
signOutUi();
|
||||
});
|
||||
|
||||
// ---------- ?mode= deep-link ----------
|
||||
// The profile dropdown links here with ?mode=new|import|wc. Each mode
|
||||
// AUTO-RUNS its flow the moment the page loads, so the dropdown option
|
||||
// actually feels like it did the thing:
|
||||
// new -> click Generate a phrase (shows the 12-word seed immediately)
|
||||
// import -> focus the seed textarea (user pastes)
|
||||
// wc -> click Connect wallet (starts the WC session + shows QR/URI)
|
||||
// The matching card is highlighted in acid green so context is obvious.
|
||||
(function handleMode() {
|
||||
// Anyone arriving at portal.html?mode=new|import|wc|unlock opens the shared
|
||||
// modal at that tab. Same handler as the wallet dropdown uses — no old
|
||||
// inline forms hijack the URL.
|
||||
(async function handleMode() {
|
||||
const mode = new URLSearchParams(location.search).get("mode");
|
||||
if (!mode) return;
|
||||
const id = { new: "card-new", import: "card-import", wc: "card-wc" }[mode];
|
||||
const el = id && document.getElementById(id);
|
||||
if (!el) return;
|
||||
el.style.borderColor = "var(--acid)";
|
||||
el.style.boxShadow = "0 0 0 3px rgba(214,255,61,.15)";
|
||||
setTimeout(() => el.scrollIntoView({ behavior: "smooth", block: "center" }), 50);
|
||||
setTimeout(() => { el.style.borderColor = ""; el.style.boxShadow = ""; }, 3000);
|
||||
// Trigger the primary action for the mode.
|
||||
setTimeout(() => {
|
||||
if (mode === "new") $("new-btn")?.click();
|
||||
else if (mode === "import") $("seed")?.focus();
|
||||
else if (mode === "wc") $("wc-btn")?.click();
|
||||
}, 200);
|
||||
if (await ensureSignInModal()) window.siriusSignInWallet(mode);
|
||||
})();
|
||||
|
||||
// On load: if user already has a siriusProfile, adopt whatever in-memory
|
||||
// wallet exists (e.g. they signed in via the modal on a nearby page and
|
||||
// navigated here). If profile exists but no wallet, prompt Unlock.
|
||||
(async function bootstrapSession() {
|
||||
const profile = readProfile();
|
||||
if (!profile) return;
|
||||
const adopted = await adoptWalletFromModal();
|
||||
if (adopted) return;
|
||||
// Profile without in-memory wallet — offer Unlock in the modal.
|
||||
if (await ensureSignInModal()) {
|
||||
// If a saved encrypted wallet exists, the modal defaults to Unlock;
|
||||
// otherwise fall back to Import.
|
||||
const hasSaved = (() => { try { return !!localStorage.getItem("bns.wallet.v1"); } catch { return false; } })();
|
||||
window.siriusSignInWallet(hasSaved ? "unlock" : "import");
|
||||
}
|
||||
})();
|
||||
|
||||
async function enterPortal() {
|
||||
|
|
@ -812,6 +784,7 @@ $("tld-submit").addEventListener("click", async () => {
|
|||
updateTldPrice();
|
||||
</script>
|
||||
<script defer src="./js/site-footer.js?v=20260907rel"></script>
|
||||
<script type="module" src="./js/register-flow.js?v=20260908tabs"></script>
|
||||
<script defer src="./js/profile-menu.js?v=20260908tabs"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue