diff options
| author | Adam Soltys <[email protected]> | 2026-03-04 23:06:35 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-03-04 23:06:35 -0800 |
| commit | e5f37b003d93a17c2a9958697269b2d3228d8830 (patch) | |
| tree | e2c3a3c4d1bec48d323e830f5be35d5832845a9a /src/routes | |
| parent | e6882c6a8987e23215a2538b242431ae603c7101 (diff) | |
| download | coinos-ui-e5f37b003d93a17c2a9958697269b2d3228d8830.tar.gz coinos-ui-e5f37b003d93a17c2a9958697269b2d3228d8830.zip | |
tuning
Diffstat (limited to 'src/routes')
| -rw-r--r-- | src/routes/(app)/+layout.server.ts | 51 | ||||
| -rw-r--r-- | src/routes/(app)/+layout.svelte | 8 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+page.svelte | 2 | ||||
| -rw-r--r-- | src/routes/(app)/account/new/+page.svelte | 16 | ||||
| -rw-r--r-- | src/routes/(app)/invoice/+page.server.ts | 2 | ||||
| -rw-r--r-- | src/routes/(app)/invoice/[id]/+layout.ts (renamed from src/routes/(app)/invoice/[id]/+layout.server.ts) | 25 | ||||
| -rw-r--r-- | src/routes/(app)/invoice/[id]/+page.svelte | 4 | ||||
| -rw-r--r-- | src/routes/(app)/invoice/[id]/tip/+page.server.ts | 5 | ||||
| -rw-r--r-- | src/routes/(app)/send/ark/[address]/+layout.server.ts | 15 | ||||
| -rw-r--r-- | src/routes/(app)/send/ark/[address]/+page.server.ts | 16 | ||||
| -rw-r--r-- | src/routes/(app)/send/ark/[address]/+page.svelte | 4 | ||||
| -rw-r--r-- | src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.server.ts | 14 | ||||
| -rw-r--r-- | src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.svelte | 4 | ||||
| -rw-r--r-- | src/routes/(static)/login/+page.svelte | 13 | ||||
| -rw-r--r-- | src/routes/+layout.server.ts | 9 | ||||
| -rw-r--r-- | src/routes/+layout.svelte | 13 |
16 files changed, 117 insertions, 84 deletions
diff --git a/src/routes/(app)/+layout.server.ts b/src/routes/(app)/+layout.server.ts index f863e026..09d6f841 100644 --- a/src/routes/(app)/+layout.server.ts +++ b/src/routes/(app)/+layout.server.ts @@ -1,54 +1,45 @@ import { auth, get, isInvalidTokenError, sleep } from "$lib/utils"; -import { error, redirect } from "@sveltejs/kit"; +import { redirect } from "@sveltejs/kit"; -export async function load({ cookies, url, params }) { - const { pathname } = url; - const { id, username } = params; - const token = cookies.get("token"); +export async function load({ cookies, depends }) { + depends("app:user"); - const isSweep = pathname.match(/^\/fund\/[^/]+\/sweep/); - if (!token && !isSweep) redirect(307, "/login"); + const token = cookies.get("token"); let user; let hasArk = false; if (token) { - try { - user = await get("/me", auth(cookies)); - } catch (e) { + const authHeaders = auth(cookies); + + // Fetch user and accounts in parallel + const [userResult, accounts] = await Promise.all([ + get("/me", authHeaders).catch((e) => ({ _error: e })), + get("/accounts", authHeaders).catch(() => []), + ]); + + if (userResult?._error) { + const e = userResult._error; const { message } = e as Error; if (message.startsWith("Rate")) { await sleep(3000); try { - user = await get("/me", auth(cookies)); + user = await get("/me", authHeaders); } catch (retryError) { - if (isInvalidTokenError(retryError) && pathname !== "/logout") { + if (isInvalidTokenError(retryError)) { redirect(307, "/logout"); } throw retryError; } - } else if (isInvalidTokenError(e) && pathname !== "/logout") { + } else if (isInvalidTokenError(e)) { redirect(307, "/logout"); } else { throw e; } + } else { + user = userResult; } - hasArk = (await get("/accounts", auth(cookies))).some((a: any) => a.type === "ark"); - } - - let subject; - if (url.pathname.includes("/invoice") && id) { - try { - ({ user: subject } = await get(`/invoice/${id}`)); - } catch (e) { - console.log("unable to fetch invoice", id, e); - } - } else if (username) { - try { - subject = await get(`/users/${username}`); - } catch { - error(500, "Unable to retrieve user account data"); - } + hasArk = accounts.some((a: any) => a.type === "ark"); } if (user?.needsMigration) { @@ -57,5 +48,5 @@ export async function load({ cookies, url, params }) { const theme = cookies.get("theme") || "light"; - return { user, token, subject, theme, hasArk }; + return { user, token, theme, hasArk }; } diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 991171be..5a9397b5 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -29,14 +29,14 @@ import Nostr from "$comp/Nostr.svelte"; import Password from "$comp/Password.svelte"; import { s, f, toFiat, success, getCookie, warning, post } from "$lib/utils"; - import { t, locale, loading } from "$lib/translations"; + import { t, locale } from "$lib/translations"; import { goto, invalidate, afterNavigate } from "$app/navigation"; let { data, children } = $props(); let user = $derived(data.user); - let subject = $derived(data.subject); + let subject = $derived(($page.data as any).subject); let token = $derived(data.token); let theme = $derived(data.theme); @@ -332,7 +332,5 @@ <main class="pb-4 min-h-dvh" data-theme={theme}> <OfflineBanner /> <AppHeader {user} {subject} /> - {#if !$loading} - {@render children?.()} - {/if} + {@render children?.()} </main> diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte index 123a14e8..9979d77c 100644 --- a/src/routes/(app)/[username]/(profile)/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/+page.svelte @@ -124,7 +124,7 @@ </a> {/if} {#if user?.id && user.id === subject.id} - <div class="space-y-4" data-sveltekit-preload-data="false"> + <div class="space-y-4"> {#each accounts as account} <Account {user} {rates} {account} /> {/each} diff --git a/src/routes/(app)/account/new/+page.svelte b/src/routes/(app)/account/new/+page.svelte index 4365dedd..310660a5 100644 --- a/src/routes/(app)/account/new/+page.svelte +++ b/src/routes/(app)/account/new/+page.svelte @@ -3,9 +3,10 @@ import { t } from "$lib/translations"; import Spinner from "$comp/Spinner.svelte"; import { bytesToHex } from "@noble/hashes/utils.js"; - import { fail, post, versions } from "$lib/utils"; + import { fail, post, versions, wait } from "$lib/utils"; import { goto } from "$app/navigation"; import { arkServerUrl } from "$lib/ark"; + import { password as pw, passwordPrompt } from "$lib/store"; import { HDKey } from "@scure/bip32"; import { entropyToMnemonic, mnemonicToSeed } from "@scure/bip39"; import { wordlist } from "@scure/bip39/wordlists/english.js"; @@ -19,11 +20,16 @@ creatingArk = true; try { const { getWalletEntropy } = await import("$lib/walletEntropy"); - const entropy = await getWalletEntropy(); + let entropy = await getWalletEntropy(); + if (!entropy) { - fail($t("accounts.failedToDecryptSeed") as string); - creatingArk = false; - return; + if (!$pw) $passwordPrompt = true; + await wait(() => !!$pw); + const { deriveAuthKeypair, deriveWalletEntropy } = await import("$lib/deriveAuthKey"); + const { sk } = await deriveAuthKeypair(user.username, $pw!); + entropy = await deriveWalletEntropy(sk); + const { rememberPrfKey, defaultRememberForMs } = await import("$lib/passwordCache"); + rememberPrfKey(entropy, defaultRememberForMs); } const { SingleKey, Wallet } = await import("@arkade-os/sdk"); diff --git a/src/routes/(app)/invoice/+page.server.ts b/src/routes/(app)/invoice/+page.server.ts index 9cf2088a..db54ff8b 100644 --- a/src/routes/(app)/invoice/+page.server.ts +++ b/src/routes/(app)/invoice/+page.server.ts @@ -5,7 +5,7 @@ import { error, redirect } from "@sveltejs/kit"; export const load = async ({ cookies, parent, url }) => { const aid = url.searchParams.get("aid") || cookies.get("aid"); - if (aid) cookies.set("aid", aid, { path: "/", maxAge: 86400 }); + if (aid) cookies.set("aid", aid, { path: "/", maxAge: 86400, httpOnly: false }); let { subject, user } = await parent(); const lnurl = url.searchParams.has("lnurl"); diff --git a/src/routes/(app)/invoice/[id]/+layout.server.ts b/src/routes/(app)/invoice/[id]/+layout.ts index 24b7f1b0..411486c7 100644 --- a/src/routes/(app)/invoice/[id]/+layout.server.ts +++ b/src/routes/(app)/invoice/[id]/+layout.ts @@ -1,24 +1,23 @@ -import getRates from "$lib/rates"; import { get } from "$lib/utils"; import { redirect } from "@sveltejs/kit"; -export async function load({ cookies, depends, params, url, parent }) { +export async function load({ params, url, parent, depends }) { depends("app:invoice"); - const token = cookies.get("token"); - let { subject, user } = await parent(); + const { user, token, theme } = await parent(); const { id } = params; let invoice; + let subject; if (id === "lnurl") { - const rates = await getRates(); + subject = user; invoice = { aid: user.id, uid: user.id, amount: 0, tip: 0, - rate: rates[user?.currency || "USD"], - text: "", + rate: 0, + text: `${user.username}@${url.host}`, hash: "", type: "lightning", items: [], @@ -42,7 +41,6 @@ export async function load({ cookies, depends, params, url, parent }) { subject = invoice.user; - const aid = cookies.get("aid") || user?.id; if ( user && invoice.uid !== user?.id && @@ -56,6 +54,13 @@ export async function load({ cookies, depends, params, url, parent }) { } } - const theme = cookies.get("theme") || "light"; - return { id, invoice, subject, user, token, theme }; + let qrDataUrl: string | undefined; + if (invoice?.text) { + try { + const QRCode = await import("qrcode"); + qrDataUrl = await QRCode.toDataURL(invoice.text, { width: 600, margin: 1 }); + } catch {} + } + + return { id, invoice, subject, user, token, theme, qrDataUrl }; } diff --git a/src/routes/(app)/invoice/[id]/+page.svelte b/src/routes/(app)/invoice/[id]/+page.svelte index 988ba647..7ce7c4e8 100644 --- a/src/routes/(app)/invoice/[id]/+page.svelte +++ b/src/routes/(app)/invoice/[id]/+page.svelte @@ -32,7 +32,7 @@ let user = $derived(data.user); let hasArk = $derived(data.hasArk); let invoice = $derived(data.invoice); - let src = $derived(data.src); + let qrDataUrl = $derived(data.qrDataUrl); let { aid, amount, rate, tip, hash, text, type } = $derived(invoice); let memo = $state(); let { username, currency } = $derived(invoice.user); @@ -185,7 +185,7 @@ <div class="invoice container mx-auto max-w-xl px-4 pb-4 space-y-2"> <InvoiceData - {src} + {qrDataUrl} {link} {txt} invoice={displayInvoice} diff --git a/src/routes/(app)/invoice/[id]/tip/+page.server.ts b/src/routes/(app)/invoice/[id]/tip/+page.server.ts index 91ccebca..84b218f3 100644 --- a/src/routes/(app)/invoice/[id]/tip/+page.server.ts +++ b/src/routes/(app)/invoice/[id]/tip/+page.server.ts @@ -4,8 +4,9 @@ import { fail, redirect } from "@sveltejs/kit"; export async function load({ cookies, params, parent }) { const { id } = params; - const rates = await getRates(); - let { invoice, subject, user } = await parent(); + const [rates, { user }] = await Promise.all([getRates(), parent()]); + let invoice = await get(`/invoice/${id}`, auth(cookies)); + const subject = invoice.user; if (!invoice.amount) redirect(307, `/invoice/${invoice.id}`); const pin = cookies.get("pin"); diff --git a/src/routes/(app)/send/ark/[address]/+layout.server.ts b/src/routes/(app)/send/ark/[address]/+layout.server.ts index 6381463a..18fb7aed 100644 --- a/src/routes/(app)/send/ark/[address]/+layout.server.ts +++ b/src/routes/(app)/send/ark/[address]/+layout.server.ts @@ -1,11 +1,18 @@ import { auth, get } from "$lib/utils"; import { redirect } from "@sveltejs/kit"; -export async function load({ cookies, params, parent }) { +export async function load({ cookies, params, parent, url }) { const { user } = await parent(); - const aid = cookies.get("aid") || user.id; + let aid = url.searchParams.get("aid") || cookies.get("aid") || user.id; const { address, amount } = params; - const { balance } = await get(`/account/${aid}`, auth(cookies)); + let account = await get(`/account/${aid}`, auth(cookies)); + + // Fall back to custodial if cookie points to ark account + if (account.type === "ark" && aid !== user.id) { + aid = user.id; + account = await get(`/account/${aid}`, auth(cookies)); + cookies.set("aid", aid, { path: "/", maxAge: 86400, httpOnly: false }); + } let invoice; try { @@ -21,5 +28,5 @@ export async function load({ cookies, params, parent }) { if (recipient?.id !== user.id && recipient.id === invoice.aid) redirect(307, r); } - return { balance }; + return { balance: account.balance }; } diff --git a/src/routes/(app)/send/ark/[address]/+page.server.ts b/src/routes/(app)/send/ark/[address]/+page.server.ts index fdf129a0..83a5050f 100644 --- a/src/routes/(app)/send/ark/[address]/+page.server.ts +++ b/src/routes/(app)/send/ark/[address]/+page.server.ts @@ -1,10 +1,18 @@ import getRates from "$lib/rates"; import { auth, get } from "$lib/utils"; -export async function load({ cookies, parent }) { +export async function load({ cookies, parent, url }) { const { user } = await parent(); const rates = await getRates(); - const aid = cookies.get("aid") || user.id; - const { balance } = await get(`/account/${aid}`, auth(cookies)); - return { balance, rate: rates[user.currency] }; + let aid = url.searchParams.get("aid") || cookies.get("aid") || user.id; + let account = await get(`/account/${aid}`, auth(cookies)); + + // Fall back to custodial if cookie points to ark account + if (account.type === "ark" && aid !== user.id) { + aid = user.id; + account = await get(`/account/${aid}`, auth(cookies)); + cookies.set("aid", aid, { path: "/", maxAge: 86400, httpOnly: false }); + } + + return { balance: account.balance, rate: rates[user.currency] }; } diff --git a/src/routes/(app)/send/ark/[address]/+page.svelte b/src/routes/(app)/send/ark/[address]/+page.svelte index 631f6689..251bca72 100644 --- a/src/routes/(app)/send/ark/[address]/+page.svelte +++ b/src/routes/(app)/send/ark/[address]/+page.svelte @@ -4,7 +4,7 @@ import Numpad from "$comp/Numpad.svelte"; import { page } from "$app/stores"; import { fiat as fiatStore, rate } from "$lib/store"; - import { loc, fail, s, f, sats } from "$lib/utils"; + import { loc, s, f, sats } from "$lib/utils"; let { data } = $props(); let balance = $derived(data.balance); @@ -47,7 +47,7 @@ {/if} </button> - <form action={`/send/ark/${address}/${amount}`} class="contents"> + <form action={`/send/ark/${address}/${amount}${$page.url.search}`} class="contents"> <button bind:this={submit} type="submit" class="btn !w-auto grow btn-accent"> {$t("payments.next")} </button> diff --git a/src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.server.ts b/src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.server.ts index 9fa754d6..ee0775d3 100644 --- a/src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.server.ts +++ b/src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.server.ts @@ -2,12 +2,20 @@ import getRates from "$lib/rates"; import { auth, fd, get, post } from "$lib/utils"; import { fail, redirect } from "@sveltejs/kit"; -export async function load({ params: { address, amount }, cookies, parent }) { +export async function load({ params: { address, amount }, cookies, parent, url }) { const rates = await getRates(); const { user } = await parent(); - const aid = cookies.get("aid") || user.id; + let aid = url.searchParams.get("aid") || cookies.get("aid") || user.id; - const account = await get(`/account/${aid}`, auth(cookies)); + let account = await get(`/account/${aid}`, auth(cookies)); + + // If cookie points to an ark account but user is sending TO an ark address, + // fall back to custodial account — ark-to-ark sends use client-side wallet + if (account.type === "ark" && aid !== user.id) { + aid = user.id; + account = await get(`/account/${aid}`, auth(cookies)); + cookies.set("aid", aid, { path: "/", maxAge: 86400, httpOnly: false }); + } if (account.type === "bitcoin") { const inv = await post( diff --git a/src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.svelte b/src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.svelte index ced8dfce..9936cabc 100644 --- a/src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.svelte +++ b/src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.svelte @@ -7,7 +7,7 @@ import { goto, invalidate } from "$app/navigation"; import { rate } from "$lib/store"; import { applyAction } from "$app/forms"; - import { sendArk, sendArkViaForward } from "$lib/ark"; + import { sendArk, sendArkViaForward, arkkey } from "$lib/ark"; import Amount from "$comp/Amount.svelte"; @@ -16,7 +16,7 @@ let error = $state(""); let handler = ({ cancel }) => { - if (account.type === "ark") { + if (account.type === "ark" && $arkkey) { cancel(); (async () => { diff --git a/src/routes/(static)/login/+page.svelte b/src/routes/(static)/login/+page.svelte index 0e920d5c..8cb4cf45 100644 --- a/src/routes/(static)/login/+page.svelte +++ b/src/routes/(static)/login/+page.svelte @@ -217,6 +217,19 @@ console.log("Auth key derivation failed, falling back to legacy login", e); return async ({ result }: any) => { if (result.type === "success") { + try { + const { deriveAuthKeypair, deriveWalletEntropy } = await import("$lib/deriveAuthKey"); + const { sk } = await deriveAuthKeypair(uname, pw); + const oldPrfKey = await deriveWalletEntropy(sk); + await resolveCanonicalKey( + passwordMethodId(), + sk.buffer as ArrayBuffer, + result.data?.encryptedKeys, + oldPrfKey, + ); + } catch (e2) { + console.log("PRF key caching in legacy fallback failed", e2); + } await invalidateAll(); goto(result.data?.redirectUrl || "/"); } else { diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 61c46ea6..c14c6f47 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -1,8 +1,6 @@ import { defaultLocale, loadTranslations, locales } from "$lib/translations/index"; -export const load = async ({ cookies, request, url }) => { - const { pathname } = url; - +export const load = async ({ cookies, request }) => { // Try to get the locale from cookie let locale = (cookies.get("lang") || "").toLowerCase(); @@ -21,13 +19,12 @@ export const load = async ({ cookies, request, url }) => { locale = defaultLocale; } - await loadTranslations(locale, pathname); // keep this just before the `return` + await loadTranslations(locale, "/"); const theme = cookies.get("theme") || "light"; return { theme, - pathname, - i18n: { locale, route: pathname }, + i18n: { locale, route: "/" }, }; }; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 9047c382..3c44f905 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -3,14 +3,15 @@ import { browser } from "$app/environment"; import { PUBLIC_DOMAIN } from "$env/static/public"; import "../app.css"; - import { loading, t } from "$lib/translations"; + import { t } from "$lib/translations"; import { onMount } from "svelte"; import { installPrompt, theme as themeStore } from "$lib/store"; + import { page } from "$app/stores"; const map = { light: "lofi", dark: "black", system: "system" }; let { data, children } = $props<any>(); - let pathname = $derived(data.pathname); + let pathname = $derived($page.url.pathname); let theme = $derived(data.theme); $effect(() => { $themeStore = theme; @@ -94,11 +95,9 @@ <meta name="twitter:creator" content="@coinoswallet" /> </svelte:head> -{#if !$loading} - <main> - {@render children?.()} - </main> -{/if} +<main> + {@render children?.()} +</main> <style> :global(:root) { |
