aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-03-04 23:06:35 -0800
committerAdam Soltys <[email protected]>2026-03-04 23:06:35 -0800
commite5f37b003d93a17c2a9958697269b2d3228d8830 (patch)
treee2c3a3c4d1bec48d323e830f5be35d5832845a9a /src/routes
parente6882c6a8987e23215a2538b242431ae603c7101 (diff)
downloadcoinos-ui-e5f37b003d93a17c2a9958697269b2d3228d8830.tar.gz
coinos-ui-e5f37b003d93a17c2a9958697269b2d3228d8830.zip
tuning
Diffstat (limited to 'src/routes')
-rw-r--r--src/routes/(app)/+layout.server.ts51
-rw-r--r--src/routes/(app)/+layout.svelte8
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.svelte2
-rw-r--r--src/routes/(app)/account/new/+page.svelte16
-rw-r--r--src/routes/(app)/invoice/+page.server.ts2
-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.svelte4
-rw-r--r--src/routes/(app)/invoice/[id]/tip/+page.server.ts5
-rw-r--r--src/routes/(app)/send/ark/[address]/+layout.server.ts15
-rw-r--r--src/routes/(app)/send/ark/[address]/+page.server.ts16
-rw-r--r--src/routes/(app)/send/ark/[address]/+page.svelte4
-rw-r--r--src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.server.ts14
-rw-r--r--src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.svelte4
-rw-r--r--src/routes/(static)/login/+page.svelte13
-rw-r--r--src/routes/+layout.server.ts9
-rw-r--r--src/routes/+layout.svelte13
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) {