aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-02-13 21:08:25 -0800
committerAdam Soltys <[email protected]>2026-02-13 21:08:25 -0800
commit8eb6cfb222c6c438374421b4e3918372a66e28b5 (patch)
tree10c88e5fc2ef0a729e607feba83b9c42a566b6ea /src
parentb4c9562ffb5de676cc6c4bf711c517f524d1e946 (diff)
downloadcoinos-ui-8eb6cfb222c6c438374421b4e3918372a66e28b5.tar.gz
coinos-ui-8eb6cfb222c6c438374421b4e3918372a66e28b5.zip
get currency from account
Diffstat (limited to 'src')
-rw-r--r--src/components/Account.svelte8
-rw-r--r--src/components/Balance.svelte5
-rw-r--r--src/components/InvoiceData.svelte5
-rw-r--r--src/locales/en.json3
-rw-r--r--src/routes/(app)/[username]/(profile)/+layout.server.ts2
-rw-r--r--src/routes/(app)/[username]/(profile)/+layout.svelte3
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.server.ts2
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.svelte4
-rw-r--r--src/routes/(app)/account/[id]/+page.server.ts8
-rw-r--r--src/routes/(app)/account/[id]/+page.svelte155
-rw-r--r--src/routes/(app)/settings/account/+page.svelte114
11 files changed, 171 insertions, 138 deletions
diff --git a/src/components/Account.svelte b/src/components/Account.svelte
index df2c51ea..d588932a 100644
--- a/src/components/Account.svelte
+++ b/src/components/Account.svelte
@@ -12,7 +12,9 @@
import { arkkey } from "$lib/ark";
import { getRememberedWalletPassword, forgetWalletPassword } from "$lib/passwordCache";
- let { user, rate, account }: any = $props();
+ let { user, rates, account }: any = $props();
+ let currency = $derived(account.currency || user.currency);
+ let rate = $derived(rates[currency]);
let { name, seed, fingerprint, balance, pending, id, type: accountType, arkAddress } = $derived(account);
let arkBalance = $state(0);
@@ -135,10 +137,10 @@
<div class="flex items-start justify-between gap-4">
<div>
<div class="text-lg text-gray-400">{displayName}</div>
- <Balance {balance} {user} {rate} {id} />
+ <Balance {balance} {user} {rate} {id} {currency} />
{#if pending}
<div class="text-lg text-gray-600">
- +{$fiat && rate ? f(toFiat(pending, rate), user.currency, loc(user)) : s(pending)} pending
+ +{$fiat && rate ? f(toFiat(pending, rate), currency, loc(user)) : s(pending)} pending
</div>
{/if}
</div>
diff --git a/src/components/Balance.svelte b/src/components/Balance.svelte
index 440c260d..6abb1449 100644
--- a/src/components/Balance.svelte
+++ b/src/components/Balance.svelte
@@ -6,8 +6,9 @@
import { t } from "$lib/translations";
let show = $state();
- let { id, user, rate, balance } = $props();
- let { currency, locked } = $derived(user);
+ let { id, user, rate, balance, currency: currencyProp = undefined } = $props();
+ let currency = $derived(currencyProp || user.currency);
+ let { locked } = $derived(user);
let locale = $derived(loc(user));
let toggleShow = (e) => {
e.preventDefault();
diff --git a/src/components/InvoiceData.svelte b/src/components/InvoiceData.svelte
index 84a52308..105eb471 100644
--- a/src/components/InvoiceData.svelte
+++ b/src/components/InvoiceData.svelte
@@ -19,6 +19,7 @@
}: any = $props();
let loaded = $state(false);
+ let expanded = $state(false);
let { memo } = $derived(invoice);
let load = () => (loaded = true);
</script>
@@ -52,11 +53,11 @@
<button
data-testid="invoice-text"
- onclick={() => copy(txt)}
+ onclick={() => { copy(txt); expanded = !expanded; }}
aria-label="Copy invoice"
class="w-full text-center text-secondary text-xl flex gap-1 items-center justify-center cursor-pointer hover:opacity-70"
>
- <span class="truncate">{txt}</span>
+ <span class:truncate={!expanded} class="break-all">{txt}</span>
<iconify-icon noobserver icon="ph:copy-bold" width="32" class="shrink-0"></iconify-icon>
</button>
diff --git a/src/locales/en.json b/src/locales/en.json
index 93fe95a8..9f23dd47 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -70,7 +70,8 @@
"bitcoin": "Bitcoin",
"custodial": "Custodial",
"submit": "Submit",
- "type": "Type"
+ "type": "Type",
+ "accountType": "Account type"
},
"404": {
"404": "404",
diff --git a/src/routes/(app)/[username]/(profile)/+layout.server.ts b/src/routes/(app)/[username]/(profile)/+layout.server.ts
index 9c916d7a..9fd42861 100644
--- a/src/routes/(app)/[username]/(profile)/+layout.server.ts
+++ b/src/routes/(app)/[username]/(profile)/+layout.server.ts
@@ -6,5 +6,5 @@ export async function load({ depends, parent }) {
const rates = await getRates();
const { subject } = await parent();
- return { rate: rates[subject.currency] };
+ return { rates };
}
diff --git a/src/routes/(app)/[username]/(profile)/+layout.svelte b/src/routes/(app)/[username]/(profile)/+layout.svelte
index 61aa38d5..39aca251 100644
--- a/src/routes/(app)/[username]/(profile)/+layout.svelte
+++ b/src/routes/(app)/[username]/(profile)/+layout.svelte
@@ -10,7 +10,8 @@
let { encode, toWords } = bech32;
- let { rate, user, subject } = $derived(data);
+ let { rates, user, subject } = $derived(data);
+ let rate = $derived(rates[subject.currency]);
let { events, src, text } = $derived(data as any);
let satsPerCurrency = $derived((1 * sats) / rate);
diff --git a/src/routes/(app)/[username]/(profile)/+page.server.ts b/src/routes/(app)/[username]/(profile)/+page.server.ts
index 9c97c947..f5be6aae 100644
--- a/src/routes/(app)/[username]/(profile)/+page.server.ts
+++ b/src/routes/(app)/[username]/(profile)/+page.server.ts
@@ -11,5 +11,5 @@ export async function load({ cookies, depends, parent }) {
if (cookies.get("token")) accounts = await get("/accounts", auth(cookies));
const rates = await getRates();
- return { accounts, rate: rates[subject.currency] };
+ return { accounts, rates };
}
diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte
index 60af01c8..966925d5 100644
--- a/src/routes/(app)/[username]/(profile)/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/+page.svelte
@@ -15,7 +15,7 @@
preloadData("/send");
});
- let { accounts, subject, rate, user } = $derived(data as any);
+ let { accounts, subject, rates, user } = $derived(data as any);
let { locked } = $derived(user);
let install = async () => {
@@ -62,7 +62,7 @@
{#if user?.id && user.id === subject.id}
<div class="space-y-10 pb-8" data-sveltekit-preload-data="false">
{#each accounts as account}
- <Account {user} {rate} {account} />
+ <Account {user} {rates} {account} />
{/each}
<div class="flex justify-end">
<a href="/account/new" class="contents" aria-label={$t("accounts.addAccount")}>
diff --git a/src/routes/(app)/account/[id]/+page.server.ts b/src/routes/(app)/account/[id]/+page.server.ts
index 0fcfbdd9..8e16f839 100644
--- a/src/routes/(app)/account/[id]/+page.server.ts
+++ b/src/routes/(app)/account/[id]/+page.server.ts
@@ -1,9 +1,11 @@
import { error, redirect } from "@sveltejs/kit";
import { auth, get, post, fd } from "$lib/utils";
+import getRates from "$lib/rates";
export const load = async ({ cookies, params }) => {
const account = await get(`/account/${params.id}`, auth(cookies));
- return { account };
+ const rates = await getRates();
+ return { account, rates };
};
export const actions = {
@@ -12,6 +14,10 @@ export const actions = {
const body = await fd(request);
const username = cookies.get("username");
+ body.autowithdraw = body.autowithdraw === "on";
+ if (body.threshold) body.threshold = parseInt(body.threshold);
+ if (body.reserve) body.reserve = parseInt(body.reserve);
+
try {
await post(`/account/${id}`, body, auth(cookies));
} catch (e: any) {
diff --git a/src/routes/(app)/account/[id]/+page.svelte b/src/routes/(app)/account/[id]/+page.svelte
index 05add104..fa9d744d 100644
--- a/src/routes/(app)/account/[id]/+page.svelte
+++ b/src/routes/(app)/account/[id]/+page.svelte
@@ -1,11 +1,14 @@
<script lang="ts">
import { page } from "$app/stores";
import Mnemonic from "$comp/Mnemonic.svelte";
+ import Numpad from "$comp/Numpad.svelte";
+ import Toggle from "$comp/Toggle.svelte";
import WalletPass from "$comp/WalletPass.svelte";
import { goto } from "$app/navigation";
import { copy, fail, focus, post } from "$lib/utils";
import { enhance } from "$app/forms";
import { t } from "$lib/translations";
+ import { tick } from "svelte";
import { entropyToMnemonic, mnemonicToSeed } from "@scure/bip39";
import { wordlist } from "@scure/bip39/wordlists/english.js";
import { getRememberedWalletPassword, forgetWalletPassword } from "$lib/passwordCache";
@@ -13,6 +16,7 @@
let { data } = $props();
let account = $derived(data.account);
let user = $derived(data.user);
+ let rates = $derived(data.rates);
let id = $derived(account.id);
let seed = $derived(account.seed || (account.fingerprint && user.seed));
let displayType = $derived(
@@ -23,12 +27,49 @@
: $t("accounts.custodial"),
);
let name = $state(data.account.name || displayType);
+ let currency = $state(data.account.currency || data.user.currency);
+ let autowithdraw = $state(data.account.autowithdraw || false);
+ let threshold = $state(data.account.threshold || data.user.threshold || 1000000);
+ let reserve = $state(data.account.reserve || data.user.reserve || 0);
+ let destination = $state(data.account.destination || data.user.destination || "");
+
+ let rate = $derived(rates[currency]);
+ let fiats = $derived(Object.keys(rates).sort((a, b) => a.localeCompare(b)));
+
let mnemonic: string | undefined = $state(),
password: string | undefined = $state();
let passwordPrompt = $state();
let cancel = $state(() => (passwordPrompt = false));
let toggle = () => (passwordPrompt = !passwordPrompt);
+ let editingReserve = $state(false),
+ editingThreshold = $state(false),
+ doneReserve = $state(),
+ doneThreshold;
+ let reserveEl: any = $state(),
+ thresholdEl: any = $state();
+
+ let doneEditing = () => {
+ editingReserve = false;
+ editingThreshold = false;
+ };
+
+ let editReserve = async () => {
+ editingReserve = true;
+ await tick();
+ reserveEl?.focus();
+ };
+
+ let editThreshold = async () => {
+ editingThreshold = true;
+ await tick();
+ thresholdEl?.focus();
+ };
+
+ let keypress = (e: any) => e.key === "Enter" && e.preventDefault();
+
+ let isCustodial = $derived(!seed && account.type !== "ark");
+
let revealWithCachedPassword = async () => {
const cached = getRememberedWalletPassword();
if (!cached) return false;
@@ -75,7 +116,19 @@
<h1 class="text-center text-3xl font-semibold">{$t("payments.accountSettings")}</h1>
<div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-2">
- <form class="space-y-5" method="POST" use:enhance>
+ <form class="space-y-5 pb-8" method="POST" use:enhance>
+ <div class="flex items-center gap-2 text-lg">
+ <span class="font-bold">{$t("accounts.accountType")}:</span>
+ {#if account.type === "ark"}
+ <img src="/images/ark.png" class="w-6 h-6 rounded-full object-cover" alt="Ark" />
+ {:else if seed}
+ <iconify-icon noobserver icon="cryptocurrency-color:btc" width="24"></iconify-icon>
+ {:else}
+ <img src="/images/icon.png" class="w-6 h-6" alt="Coinos" />
+ {/if}
+ {displayType}
+ </div>
+
<div class="space-y-1">
<label for="name" class="font-bold block">{$t("accounts.name")}</label>
<label
@@ -85,18 +138,66 @@
</label>
</div>
- <div class="flex items-center gap-2 text-lg">
- <span class="font-bold">{$t("accounts.type")}:</span>
- {#if account.type === "ark"}
- <img src="/images/ark.png" class="w-6 h-6 rounded-full object-cover" alt="Ark" />
- {:else if seed}
- <iconify-icon noobserver icon="cryptocurrency-color:btc" width="24"></iconify-icon>
- {:else}
- <iconify-icon noobserver icon="ph:lightning-fill" class="text-yellow-300" width="24"></iconify-icon>
- {/if}
- {displayType}
+ <div>
+ <label for="currency" class="font-bold block mb-1">{$t("user.settings.localCurrency")}</label>
+ <select name="currency" bind:value={currency}>
+ {#each fiats as fiat}
+ <option value={fiat}>{fiat}</option>
+ {/each}
+ </select>
</div>
+ {#if isCustodial}
+ <div>
+ <div class="flex justify-between items-center">
+ <span class="font-bold">{$t("user.settings.autoWithdraw")}</span>
+ <Toggle id="autowithdraw" bind:value={autowithdraw} />
+ </div>
+ <p class="text-secondary mt-1 w-9/12">{$t("user.settings.autoWithdrawDescription")}</p>
+ </div>
+
+ <div class:hidden={!autowithdraw}>
+ <div class="mb-2">
+ <label for="destination" class="font-bold mb-1 block">{$t("user.settings.destination")}</label>
+ <textarea
+ name="destination"
+ placeholder={$t("user.settings.destinationPlaceholder")}
+ onkeypress={keypress}
+ class="w-full p-4 border rounded-xl h-48"
+ bind:value={destination}
+ ></textarea>
+ </div>
+
+ <div>
+ <label for="threshold" class="font-bold mb-1 block">{$t("user.settings.threshold")}</label>
+ <button type="button" class="flex w-full" onclick={editThreshold}>
+ <div class="p-4 border rounded-2xl rounded-r-none border-r-0 bg-base-200">
+ <iconify-icon noobserver icon="ph:lightning-fill" class="text-yellow-300"></iconify-icon>
+ </div>
+ <div class="border-l-0 rounded-l-none pl-2 w-full p-4 border rounded-2xl text-left">
+ {threshold}
+ </div>
+ <input type="hidden" name="threshold" bind:value={threshold} />
+ </button>
+ <p class="text-secondary mt-1">{$t("user.settings.thresholdDesc")}</p>
+ </div>
+
+ <div>
+ <label for="reserve" class="font-bold mb-1 block">{$t("user.settings.reserve")}</label>
+ <button type="button" class="flex w-full" onclick={editReserve}>
+ <div class="p-4 border rounded-2xl rounded-r-none border-r-0 bg-base-200">
+ <iconify-icon noobserver icon="ph:lightning-fill" class="text-yellow-300"></iconify-icon>
+ </div>
+ <div class="border-l-0 rounded-l-none pl-2 w-full p-4 border rounded-2xl text-left">
+ {reserve}
+ </div>
+ <input type="hidden" name="reserve" bind:value={reserve} />
+ </button>
+ <p class="text-secondary mt-1">{$t("user.settings.reserveDesc")}</p>
+ </div>
+ </div>
+ {/if}
+
<div class="space-y-2">
{#if mnemonic}
<Mnemonic {mnemonic} />
@@ -130,3 +231,35 @@
{#if passwordPrompt}
<WalletPass bind:password bind:cancel {submit} />
{/if}
+
+{#if editingThreshold}
+ <div class="fixed bg-base-100/90 inset-0 overflow-y-auto h-full w-full z-50 max-w-lg mx-auto">
+ <div class="relative p-5 border shadow-lg rounded-md bg-base-100 space-y-5">
+ <h1 class="text-center text-2xl font-semibold">{$t("user.settings.threshold")}</h1>
+ <Numpad
+ bind:amount={threshold}
+ {currency}
+ bind:rate
+ bind:submit={doneReserve}
+ bind:element={thresholdEl}
+ />
+ <button bind:this={doneReserve} type="button" onclick={doneEditing} class="btn">Ok</button>
+ </div>
+ </div>
+{/if}
+
+{#if editingReserve}
+ <div class="fixed bg-base-100/90 inset-0 overflow-y-auto h-full w-full z-50 mx-auto max-w-lg">
+ <div class="relative mx-auto p-5 border shadow-lg rounded-md bg-base-100 space-y-5 text-center">
+ <h1 class="text-2xl font-semibold">{$t("user.settings.reserve")}</h1>
+ <Numpad
+ bind:amount={reserve}
+ {currency}
+ bind:rate
+ bind:submit={doneReserve}
+ bind:element={reserveEl}
+ />
+ <button bind:this={doneReserve} type="button" onclick={doneEditing} class="btn">Ok</button>
+ </div>
+ </div>
+{/if}
diff --git a/src/routes/(app)/settings/account/+page.svelte b/src/routes/(app)/settings/account/+page.svelte
index 5c4c3161..e3c6ab92 100644
--- a/src/routes/(app)/settings/account/+page.svelte
+++ b/src/routes/(app)/settings/account/+page.svelte
@@ -2,9 +2,8 @@
import { run } from "svelte/legacy";
import { theme } from "$lib/store";
- import { onMount, tick } from "svelte";
+ import { onMount } from "svelte";
import { browser } from "$app/environment";
- import Numpad from "$comp/Numpad.svelte";
import LocaleSelector from "$comp/LocaleSelector.svelte";
import Toggle from "$comp/Toggle.svelte";
import { locale, t } from "$lib/translations";
@@ -18,37 +17,8 @@
let rates = $derived(data.rates);
let subscriptions = $derived(data.subscriptions);
let { currency, email, tip, verified } = $state(user);
- let rate = $derived(rates[currency]);
let fiats = Object.keys(rates).sort((a, b) => a.localeCompare(b));
- let el: any;
- let keypress = (e: any) => e.key === "Enter" && (e.preventDefault() || el.click());
-
- let editingReserve = $state(),
- editingThreshold = $state(),
- doneReserve = $state(),
- doneThreshold;
- let doneEditing = () => {
- editingReserve = false;
- editingThreshold = false;
- };
-
- let editReserve = async () => {
- editingReserve = true;
- await tick();
- reserveEl.focus();
- };
-
- let editThreshold = async () => {
- editingThreshold = true;
- await tick();
- thresholdEl.focus();
- };
-
- if (!user.threshold) user.threshold = 1000000;
- if (!user.reserve) user.reserve = 0;
- let reserveEl: any = $state(),
- thresholdEl: any = $state();
let push = $state(),
pm: any,
@@ -174,88 +144,6 @@
</label>
</div>
-<div>
- <div class="flex justify-between items-center">
- <span class="font-bold">{$t("user.settings.autoWithdraw")}</span>
- <Toggle id="autowithdraw" bind:value={user.autowithdraw} />
- </div>
- <p class="text-secondary mt-1 w-9/12">{$t("user.settings.autoWithdrawDescription")}</p>
-</div>
-
-<div class:hidden={!user.autowithdraw}>
- <div class="mb-2">
- <label for="display" class="font-bold mb-1 block">{$t("user.settings.destination")}</label>
- <textarea
- name="destination"
- placeholder={$t("user.settings.destinationPlaceholder")}
- onkeypress={keypress}
- class="w-full p-4 border rounded-xl h-48"
- bind:value={user.destination}
- ></textarea>
- </div>
-
- <div>
- <label for="display" class="font-bold mb-1 block">{$t("user.settings.threshold")}</label>
- <button type="button" class="flex w-full" onclick={editThreshold}>
- <div class="p-4 border rounded-2xl rounded-r-none border-r-0 bg-base-200">
- <iconify-icon noobserver icon="ph:lightning-fill" class="text-yellow-300"></iconify-icon>
- </div>
- <div class="border-l-0 rounded-l-none pl-2 w-full p-4 border rounded-2xl text-left">
- {user.threshold}
- </div>
- <input type="hidden" name="threshold" bind:value={user.threshold} />
- </button>
- <p class="text-secondary mt-1">{$t("user.settings.thresholdDesc")}</p>
- </div>
-
- <div>
- <label for="display" class="font-bold mb-1 block">{$t("user.settings.reserve")}</label>
- <button type="button" class="flex w-full" onclick={editReserve}>
- <div class="p-4 border rounded-2xl rounded-r-none border-r-0 bg-base-200">
- <iconify-icon noobserver icon="ph:lightning-fill" class="text-yellow-300"></iconify-icon>
- </div>
- <div class="border-l-0 rounded-l-none pl-2 w-full p-4 border rounded-2xl text-left">
- {user.reserve}
- </div>
- <input type="hidden" name="reserve" bind:value={user.reserve} />
- </button>
- <p class="text-secondary mt-1">{$t("user.settings.reserveDesc")}</p>
- </div>
-</div>
-
-{#if editingThreshold}
- <div class="fixed bg-base-100/90 inset-0 overflow-y-auto h-full w-full z-50 max-w-lg mx-auto">
- <div class="relative p-5 border shadow-lg rounded-md bg-base-100 space-y-5">
- <h1 class="text-center text-2xl font-semibold">{$t("user.settings.threshold")}</h1>
- <Numpad
- bind:amount={user.threshold}
- {currency}
- bind:rate
- bind:submit={doneReserve}
- bind:element={thresholdEl}
- />
-
- <button bind:this={doneReserve} type="button" onclick={doneEditing} class="btn">Ok</button>
- </div>
- </div>
-{/if}
-
-{#if editingReserve}
- <div class="fixed bg-base-100/90 inset-0 overflow-y-auto h-full w-full z-50 mx-auto max-w-lg">
- <div class="relative mx-auto p-5 border shadow-lg rounded-md bg-base-100 space-y-5 text-center">
- <h1 class="text-2xl font-semibold">{$t("user.settings.reserve")}</h1>
- <Numpad
- bind:amount={user.reserve}
- {currency}
- bind:rate
- bind:submit={doneReserve}
- bind:element={reserveEl}
- />
- <button bind:this={doneReserve} type="button" onclick={doneEditing} class="btn">Ok</button>
- </div>
- </div>
-{/if}
-
{#if connect !== "connected"}
<a href={connect} class="btn flex">
<img src="/images/square.svg" class="w-12" alt="Square" />