diff options
| author | Adam Soltys <[email protected]> | 2026-02-13 21:08:25 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-02-13 21:08:25 -0800 |
| commit | 8eb6cfb222c6c438374421b4e3918372a66e28b5 (patch) | |
| tree | 10c88e5fc2ef0a729e607feba83b9c42a566b6ea /src | |
| parent | b4c9562ffb5de676cc6c4bf711c517f524d1e946 (diff) | |
| download | coinos-ui-8eb6cfb222c6c438374421b4e3918372a66e28b5.tar.gz coinos-ui-8eb6cfb222c6c438374421b4e3918372a66e28b5.zip | |
get currency from account
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/Account.svelte | 8 | ||||
| -rw-r--r-- | src/components/Balance.svelte | 5 | ||||
| -rw-r--r-- | src/components/InvoiceData.svelte | 5 | ||||
| -rw-r--r-- | src/locales/en.json | 3 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+layout.server.ts | 2 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+layout.svelte | 3 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+page.server.ts | 2 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+page.svelte | 4 | ||||
| -rw-r--r-- | src/routes/(app)/account/[id]/+page.server.ts | 8 | ||||
| -rw-r--r-- | src/routes/(app)/account/[id]/+page.svelte | 155 | ||||
| -rw-r--r-- | src/routes/(app)/settings/account/+page.svelte | 114 |
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" /> |
