| index 3cb8a4b..78f8084 100644 |
| --- a/home/web/static/home.js |
| +++ b/home/web/static/home.js |
| @@ -122,7 +122,7 @@ async function signIn() { |
| state.dek = await exportDEK(dekKey); |
| const opened = await openBlob(got.bytes, dekKey); |
| vault = { obj: opened.obj, keys: opened.keys, version: got.version }; |
| - setStatus("Signed in.", "ok"); |
| + setStatus(""); // "Signed in" is obvious — the list appearing says it |
| showVault(); |
| } |
| |
| @@ -139,8 +139,7 @@ function signOut() { |
| vault = null; |
| dekKey = null; |
| $("vault").classList.add("hidden"); |
| - $("space-section").classList.add("hidden"); |
| - $("new-space-result").classList.add("hidden"); |
| + $("create").classList.add("hidden"); |
| $("spaces").textContent = ""; |
| $("signed-out").classList.remove("hidden"); |
| $("reassure").classList.remove("hidden"); |
| @@ -154,8 +153,9 @@ function signOut() { |
| function showVault() { |
| $("signed-out").classList.add("hidden"); |
| $("reassure").classList.add("hidden"); |
| + $("create").classList.add("hidden"); |
| $("vault").classList.remove("hidden"); |
| - if (spacesEnabled) $("space-section").classList.remove("hidden"); |
| + $("open-create").classList.toggle("hidden", !spacesEnabled); // the + to create |
| |
| const box = $("spaces"); |
| box.textContent = ""; |
| @@ -411,7 +411,8 @@ async function createSpace() { |
| spaceStatus(body.message || body.error || `Couldn't create a space (HTTP ${res.status}).`, "error"); |
| return; |
| } |
| - // Save it into the vault (last-writer-wins per url, like the add form). |
| + // Save it into the list (last-writer-wins per url). It'll appear once you |
| + // return; for now the create screen shows the single-serve confirmation. |
| const url = body.url; |
| const host = new URL(url).host; |
| vault.obj.servers = (vault.obj.servers || []).filter((s) => s.url !== url); |
| @@ -420,30 +421,42 @@ async function createSpace() { |
| try { |
| await saveVault(); |
| } catch { |
| - // The space exists even if the vault write raced; say so but still show it. |
| - saved = false; |
| + saved = false; // the space exists even if the save raced; still confirm it |
| } |
| - showVault(); |
| - showNewSpaceResult(host, body.setup_link, saved); |
| spaceStatus(""); |
| + $("create-pitch").classList.add("hidden"); |
| + showNewSpaceResult(host, body.setup_link, saved); |
| } finally { |
| btn.disabled = false; |
| } |
| } |
| |
| +// A checkmark for the single-serve confirmation (static, in-code SVG). |
| +const CHECK_SVG = |
| + '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" ' + |
| + 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>'; |
| + |
| +// showNewSpaceResult fills the confirmation pane inside the create screen: a |
| +// sealed check, the new space's name, and the one-time setup link (the admin |
| +// flow — never the bare instance URL). The Done button is static in the markup. |
| function showNewSpaceResult(host, setupLink, saved) { |
| - const box = $("new-space-result"); |
| - box.classList.remove("hidden"); |
| - box.textContent = ""; |
| + const body = $("new-space-body"); |
| + body.textContent = ""; |
| + |
| + const check = document.createElement("div"); |
| + check.className = "ok-check"; |
| + check.innerHTML = CHECK_SVG; |
| |
| - const ready = document.createElement("p"); |
| - ready.className = "status ok"; |
| - ready.textContent = "Your space " + host + " is ready."; |
| - box.append(ready); |
| + const title = document.createElement("p"); |
| + title.className = "created-name"; |
| + title.textContent = "Your space is ready"; |
| + |
| + const hostLine = document.createElement("p"); |
| + hostLine.className = "sub"; |
| + hostLine.textContent = host; |
| + body.append(check, title, hostLine); |
| |
| if (setupLink) { |
| - // The setup link (not the bare instance): claiming it makes you the space's |
| - // keyholder and hands you to Eleven to set it up and administer it. |
| const a = document.createElement("a"); |
| a.className = "btn-link"; |
| a.href = setupLink; |
| @@ -452,36 +465,39 @@ function showNewSpaceResult(host, setupLink, saved) { |
| a.textContent = "Set up your space →"; |
| const note = document.createElement("p"); |
| note.className = "sub"; |
| + note.style.marginTop = "12px"; |
| note.textContent = saved |
| - ? "This one-time link makes you the keyholder and starts setup with Eleven. It's saved below so you can find it again." |
| - : "This one-time link makes you the keyholder and starts setup with Eleven. Couldn't save it just now — add it below to be safe."; |
| - box.append(a, note); |
| + ? "This one-time link makes you the keyholder and starts setup with Eleven. Your space is saved to this list." |
| + : "This one-time link makes you the keyholder and starts setup with Eleven."; |
| + body.append(a, note); |
| } else { |
| - // No setup link means the mint failed — deliberately don't offer the bare |
| - // instance URL (it lands on a signed-out card, not the admin flow). |
| + // No setup link (mint failed) — deliberately not the bare instance URL, which |
| + // lands on a signed-out card rather than the admin flow. |
| const note = document.createElement("p"); |
| note.className = "sub"; |
| - note.textContent = "Your space is saved below, but the setup link couldn't be created just now — reload and create again, or reach the operator to mint one."; |
| - box.append(note); |
| + note.textContent = "The setup link couldn't be created just now — head back and create again."; |
| + body.append(note); |
| } |
| + |
| + $("create-back").classList.add("hidden"); // the confirmation's Done is the way out |
| + $("new-space-result").classList.remove("hidden"); |
| } |
| |
| -// ---- add-space form ----------------------------------------------------------- |
| +// ---- create-screen navigation ------------------------------------------------- |
| |
| -async function addServer(e) { |
| - e.preventDefault(); |
| - const url = $("add-url").value.trim(); |
| - const name = $("add-name").value.trim(); |
| - if (!url || !vault) return; |
| - try { new URL(url); } catch { vaultStatus("That doesn't look like a URL.", "error"); return; } |
| - vault.obj.servers = (vault.obj.servers || []).filter((s) => s.url !== url); |
| - vault.obj.servers.push(name ? { url, name } : { url }); |
| - vaultStatus("Saving…", "busy"); |
| - await saveVault(); |
| - $("add-url").value = ""; |
| - $("add-name").value = ""; |
| - vaultStatus("Saved.", "ok"); |
| - showVault(); |
| +function openCreate() { |
| + $("create-pitch").classList.remove("hidden"); |
| + $("new-space-result").classList.add("hidden"); |
| + $("create-back").classList.remove("hidden"); |
| + $("new-space").disabled = false; |
| + spaceStatus(""); |
| + $("vault").classList.add("hidden"); |
| + $("create").classList.remove("hidden"); |
| +} |
| + |
| +function closeCreate() { |
| + $("create").classList.add("hidden"); |
| + showVault(); // reveals the list again, now including any newly created space |
| } |
| |
| // ---- wiring ------------------------------------------------------------------- |
| @@ -507,20 +523,25 @@ if (!window.PublicKeyCredential) { |
| $("signin").addEventListener("click", () => signIn().catch(onSignInError)); |
| } |
| $("signout").addEventListener("click", signOut); |
| +$("open-create").addEventListener("click", openCreate); |
| +$("create-back").addEventListener("click", closeCreate); |
| +$("create-done").addEventListener("click", closeCreate); |
| $("new-space").addEventListener("click", () => createSpace().catch((err) => { |
| if (err && err.name === "NotAllowedError") spaceStatus("Passkey prompt was dismissed.", ""); |
| else spaceStatus((err && err.message) || String(err), "error"); |
| })); |
| -$("add-form").addEventListener("submit", (e) => addServer(e).catch((err) => vaultStatus(err.message || String(err), "error"))); |
| // Live fuzzy filter: re-render the (unlocked) list on each keystroke. The input |
| // lives outside #spaces, so re-rendering the lists never steals its focus. |
| $("space-search").addEventListener("input", () => { if (vault) showVault(); }); |
| |
| -// Learn whether this home server offers self-serve space creation. If we're |
| -// already signed in (session rehydrate below), reveal the controls too. |
| +// Learn whether this home server offers self-serve space creation, and reveal |
| +// the + on the list if it does (and we're already signed in — session rehydrate). |
| apiJSON("/api/features") |
| - .then((f) => { spacesEnabled = !!f.spaces; if (spacesEnabled && vault) $("space-section").classList.remove("hidden"); }) |
| - .catch(() => { /* older home server without the endpoint: leave it hidden */ }); |
| + .then((f) => { |
| + spacesEnabled = !!f.spaces; |
| + if (vault) $("open-create").classList.toggle("hidden", !spacesEnabled); |
| + }) |
| + .catch(() => { /* older home server without the endpoint: leave the + hidden */ }); |
| |
| // Rehydrate a same-tab session (e.g. a reload): the DEK and token are still in |
| // sessionStorage, so the vault can reopen without a ceremony. |
| @@ -532,7 +553,7 @@ if (state.token && state.dek) { |
| dekKey = await importDEK(state.dek); |
| const opened = await openBlob(got.bytes, dekKey); |
| vault = { obj: opened.obj, keys: opened.keys, version: got.version }; |
| - setStatus("Signed in (this session).", "ok"); |
| + setStatus(""); |
| showVault(); |
| } catch { /* token or DEK went stale; the button still works */ } |
| })(); |