diff options
| author | Adam Soltys <[email protected]> | 2026-02-16 21:18:37 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-02-16 21:18:37 -0800 |
| commit | 314c86901e8aadf731d0ab235ef371248670e940 (patch) | |
| tree | f305cfdf0efb951012cc988322a701e58b01d1a2 /src | |
| parent | b22c813f9fb3b4d69dade75d22e90e6779919f07 (diff) | |
| download | coinos-ui-314c86901e8aadf731d0ab235ef371248670e940.tar.gz coinos-ui-314c86901e8aadf731d0ab235ef371248670e940.zip | |
account creation flow
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/Account.svelte | 18 | ||||
| -rw-r--r-- | src/routes/(app)/+layout.svelte | 52 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+page.svelte | 59 | ||||
| -rw-r--r-- | src/routes/(app)/account/new/+page.server.ts | 8 | ||||
| -rw-r--r-- | src/routes/(app)/account/seed/+page.svelte | 4 |
5 files changed, 78 insertions, 63 deletions
diff --git a/src/components/Account.svelte b/src/components/Account.svelte index 70bded13..88b08978 100644 --- a/src/components/Account.svelte +++ b/src/components/Account.svelte @@ -194,28 +194,36 @@ </div> {/if} </div> - <div class="flex items-start gap-4 shrink-0 text-black"> + <div class="flex items-center gap-1 shrink-0 text-black"> <a href="/payments" - class="hover:text-gray-800" + class="hover:text-gray-800 w-10 h-10 flex items-center justify-center" aria-label="Payment history" onclick={(e) => setAccount(e, "/payments")} > - <iconify-icon noobserver icon="ph:clock-counter-clockwise-bold" width="32"></iconify-icon> + <iconify-icon noobserver icon="ph:clock-counter-clockwise-bold" width="38"></iconify-icon> </a> <a href={`/account/${id}`} - class="hover:opacity-80" + class="hover:opacity-80 relative w-10 h-10 flex items-center justify-center" aria-label="Account settings" onclick={goSettings} > {#if isArk} <img src="/images/ark.png" class="w-8 h-8 rounded-full object-cover" alt="Ark" /> {:else if seed || fingerprint} - <iconify-icon noobserver icon="cryptocurrency-color:btc" width="32"></iconify-icon> + <div class="w-8 h-8 flex items-center justify-center"> + <iconify-icon noobserver icon="cryptocurrency-color:btc" width="32"></iconify-icon> + </div> {:else} <img src="/images/icon.png" class="w-8 h-8" alt="Coinos" /> {/if} + <iconify-icon + noobserver + icon="ph:gear-bold" + width="20" + class="absolute -bottom-1 -right-1 text-black bg-base-100 rounded-full" + ></iconify-icon> </a> </div> </div> diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 0fde24fa..8cd580e2 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -1,5 +1,5 @@ <script lang="ts"> - import { getWallet, syncTransactions, settle, refresh, arkkey, arkaid, sendArk, arkSending, arkServerUrl } from "$lib/ark"; + import { getWallet, syncTransactions, settle, refresh, arkkey, arkaid, sendArk, arkSending } from "$lib/ark"; import { SvelteToast } from "@zerodevx/svelte-toast"; import { onDestroy, onMount } from "svelte"; import { close, connect, send, socket } from "$lib/socket"; @@ -22,10 +22,10 @@ import AppHeader from "$comp/AppHeader.svelte"; import Nostr from "$comp/Nostr.svelte"; import Password from "$comp/Password.svelte"; - import { s, f, toFiat, success, getCookie, warning, post, versions } from "$lib/utils"; + import { s, f, toFiat, success, getCookie, warning, post } from "$lib/utils"; import { t, locale, loading } from "$lib/translations"; import { goto, invalidate, afterNavigate, preloadData } from "$app/navigation"; - import { getCachedPrfKey } from "$lib/passwordCache"; + let { data, children } = $props(); @@ -127,51 +127,6 @@ if (browser && user && $arkkey) initArk(); }); - let autoProvisionAccounts = async () => { - if (!user) return; - if (sessionStorage.getItem("accounts:provisioned")) return; - let prfKey = getCachedPrfKey(); - if (!prfKey) { - const { getWalletEntropy } = await import("$lib/walletEntropy"); - prfKey = await getWalletEntropy(); - } - if (!prfKey) return; - - sessionStorage.setItem("accounts:provisioned", "1"); - - try { - const res = await fetch("/account", { headers: { accept: "application/json" } }); - const accounts = await res.json(); - - const hasArk = accounts.some((a: any) => a.type === "ark"); - if (hasArk) return; - - const [{ HDKey }, { entropyToMnemonic, mnemonicToSeed }, { wordlist }] = await Promise.all([ - import("@scure/bip32"), - import("@scure/bip39"), - import("@scure/bip39/wordlists/english.js"), - ]); - - const entropy = new Uint8Array(prfKey); - const mnemonic = entropyToMnemonic(entropy, wordlist); - const seed = await mnemonicToSeed(mnemonic); - const master = HDKey.fromMasterSeed(seed, versions); - - const { SingleKey, Wallet } = await import("@arkade-os/sdk"); - const { bytesToHex } = await import("@noble/hashes/utils.js"); - const arkChild = master.derive("m/86'/0'/0'/0/0"); - const arkHex = bytesToHex(arkChild.privateKey!); - const identity = SingleKey.fromHex(arkHex); - const wallet = await Wallet.create({ identity, arkServerUrl }); - const arkAddress = await wallet.getAddress(); - await post("/account", { name: $t("accounts.savings"), type: "ark", arkAddress }); - - invalidate("app:payments"); - } catch (e) { - console.error("Auto-provision accounts failed:", e); - sessionStorage.removeItem("accounts:provisioned"); - } - }; onMount(async () => { if (browser) { @@ -186,7 +141,6 @@ localStorage.removeItem("arkkey:uid"); } - autoProvisionAccounts(); } // if (window.NDEFReader) { diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte index b9af8756..b37c2009 100644 --- a/src/routes/(app)/[username]/(profile)/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/+page.svelte @@ -3,10 +3,12 @@ import { onMount } from "svelte"; import Account from "$comp/Account.svelte"; import Balance from "$comp/Balance.svelte"; + import Spinner from "$comp/Spinner.svelte"; import { t } from "$lib/translations"; import { installPrompt, password } from "$lib/store"; - import { afterNavigate, preloadData } from "$app/navigation"; + import { afterNavigate, goto, invalidate, preloadData } from "$app/navigation"; import { page } from "$app/stores"; + import { versions, post, fail } from "$lib/utils"; let { data } = $props(); @@ -17,6 +19,51 @@ let { accounts, subject, rates, user } = $derived(data as any); let { locked } = $derived(user); + let hasArk = $derived(accounts.some((a: any) => a.type === "ark")); + let adding = $state(false); + + let addAccount = async (e) => { + e.preventDefault(); + if (!hasArk) { + goto("/account/new"); + return; + } + + adding = true; + try { + const { getWalletEntropy } = await import("$lib/walletEntropy"); + const prfKey = await getWalletEntropy(); + if (!prfKey) { + goto("/account/seed"); + return; + } + + const [{ HDKey }, { entropyToMnemonic, mnemonicToSeed }, { wordlist }] = await Promise.all([ + import("@scure/bip32"), + import("@scure/bip39"), + import("@scure/bip39/wordlists/english.js"), + ]); + + const entropy = new Uint8Array(prfKey); + const mn = entropyToMnemonic(entropy, wordlist); + const seed = await mnemonicToSeed(mn); + const master = HDKey.fromMasterSeed(seed, versions); + const child = master.derive("m/84'/0'/0'"); + const pubkey = child.publicExtendedKey; + const fingerprint = child.fingerprint.toString(16).padStart(8, "0"); + await post("/account", { + fingerprint, + pubkey, + name: $t("accounts.vault"), + type: "bitcoin", + accountIndex: 0, + }); + invalidate("app:payments"); + } catch (e: any) { + fail(e.message); + } + adding = false; + }; let install = async () => { if (!$installPrompt) return; @@ -64,12 +111,14 @@ {#each accounts as account} <Account {user} {rates} {account} /> {/each} - <a href="/account/seed" class="contents"> - <button class="btn btn-accent w-full"> + <button class="btn btn-accent w-full" onclick={addAccount}> + {#if adding} + <Spinner /> + {:else} <iconify-icon noobserver icon="ph:plus-bold" width="24"></iconify-icon> {$t("accounts.addAccount")} - </button> - </a> + {/if} + </button> </div> {#if locked} diff --git a/src/routes/(app)/account/new/+page.server.ts b/src/routes/(app)/account/new/+page.server.ts index d4a0d0cb..7615c6f6 100644 --- a/src/routes/(app)/account/new/+page.server.ts +++ b/src/routes/(app)/account/new/+page.server.ts @@ -1,5 +1,9 @@ +import { auth, get } from "$lib/utils"; import { redirect } from "@sveltejs/kit"; -export async function load() { - redirect(307, "/account/seed"); +export async function load({ cookies }) { + const accounts = await get("/accounts", auth(cookies)); + const hasArk = accounts.some((a: any) => a.type === "ark"); + if (hasArk) redirect(307, "/account/seed"); + return { hasArk }; } diff --git a/src/routes/(app)/account/seed/+page.svelte b/src/routes/(app)/account/seed/+page.svelte index 43b690b3..335d92f2 100644 --- a/src/routes/(app)/account/seed/+page.svelte +++ b/src/routes/(app)/account/seed/+page.svelte @@ -14,14 +14,13 @@ let { data } = $props(); let user = $derived(data.user); - let creating = $state(false); + let creating = $state(true); onMount(async () => { if (!browser) return; const { getWalletEntropy } = await import("$lib/walletEntropy"); const prfKey = await getWalletEntropy(); if (prfKey) { - creating = true; try { const entropy = new Uint8Array(prfKey); const mn = entropyToMnemonic(entropy, wordlist); @@ -45,6 +44,7 @@ } return; } + creating = false; if (!$mnemonic) await generate(); }); |
