From 350a2a46aa3b48d3223b6e5c14ddc5952d260dba Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 4 Mar 2026 12:43:18 -0800 Subject: fund ui --- src/components/Menu.svelte | 2 +- src/locales/en.json | 12 +++- src/routes/(app)/+layout.server.ts | 42 +++++++------ src/routes/(app)/fund/[id]/+page.svelte | 71 ++++++++++++---------- src/routes/(app)/fund/[id]/gift/+page.server.ts | 6 -- src/routes/(app)/fund/[id]/gift/+page.svelte | 41 ------------- src/routes/(app)/fund/[id]/share/+page.server.ts | 6 ++ src/routes/(app)/fund/[id]/share/+page.svelte | 64 +++++++++++++++++++ .../fund/[id]/sweep/[...amount]/+page.server.ts | 30 +++++++-- src/routes/(app)/fund/[id]/withdraw/+page.svelte | 3 +- src/routes/(app)/qr/[text]/+page.svelte | 8 +++ .../fund/[...name]/[...amount]/+page.server.ts | 16 ++++- .../send/fund/[...name]/[...amount]/+page.svelte | 49 ++++++++++----- 13 files changed, 230 insertions(+), 120 deletions(-) delete mode 100644 src/routes/(app)/fund/[id]/gift/+page.server.ts delete mode 100644 src/routes/(app)/fund/[id]/gift/+page.svelte create mode 100644 src/routes/(app)/fund/[id]/share/+page.server.ts create mode 100644 src/routes/(app)/fund/[id]/share/+page.svelte (limited to 'src') diff --git a/src/components/Menu.svelte b/src/components/Menu.svelte index 4b0204fd..9803e7a2 100644 --- a/src/components/Menu.svelte +++ b/src/components/Menu.svelte @@ -16,11 +16,11 @@ let showMenu = $state(false); let menuButtons = [ - { key: "nav.funds", icon: PhLightningFill, href: `/funds` }, { key: "nav.settings", icon: PhFadersBold, href: `/settings` }, { key: "nav.support", icon: PhLifebuoyBold, href: `/support` }, { key: "nav.map", icon: PhMapTrifoldBold, href: `/map` }, { key: "nav.dm", icon: PhChatBold, href: `/messages` }, + { key: "nav.funds", icon: PhLightningFill, href: `/funds` }, { key: "nav.dark", icon: PhMoonStarsBold, href: `/dark` }, { key: "nav.signOut", icon: PhSignOutBold, href: `/logout` }, ]; diff --git a/src/locales/en.json b/src/locales/en.json index 2d49ac8d..68492464 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -6,7 +6,12 @@ "manage": "Manage", "withdrawAccess": "Withdraw access", "username": "Username", - "returnToFund": "Back to fund" + "returnToFund": "Back to fund", + "authorizations": "Authorizations", + "immediateContribution": "Immediate contribution", + "fiatAuthorization": "Fiat authorization", + "authorizeDesc": "Authorize a fiat amount that someone can claim later at the current exchange rate", + "contributeDesc": "Send sats to the fund immediately from your balance" }, "notes": { "contents": "Note contents", @@ -546,6 +551,10 @@ "custom": "Custom", "trustUser": "Send and enable auto-pay", "gift": "Gift all funds", + "entireBalance": "Entire balance", + "shareSetAmount": "Share set amount", + "shareEntireBalance": "Share entire balance", + "denominateIn": "Denominate in", "giftLink": "Gift link", "giftDesc1": "Whoever opens the link below will receive all the available funds.", "giftDesc2": "If they are logged in, the funds will go to their account, otherwise a new account will be generated.", @@ -632,6 +641,7 @@ "empty": "No transactions yet.", "send": "Send", "next": "Next", + "authorize": "Authorize", "sendTo": "Send to", "to": "to", "for": "for", diff --git a/src/routes/(app)/+layout.server.ts b/src/routes/(app)/+layout.server.ts index f1cb4d24..f863e026 100644 --- a/src/routes/(app)/+layout.server.ts +++ b/src/routes/(app)/+layout.server.ts @@ -6,31 +6,35 @@ export async function load({ cookies, url, params }) { const { id, username } = params; const token = cookies.get("token"); - if (!token) redirect(307, "/login"); + const isSweep = pathname.match(/^\/fund\/[^/]+\/sweep/); + if (!token && !isSweep) redirect(307, "/login"); let user; - try { - user = await get("/me", auth(cookies)); - } catch (e) { - const { message } = e as Error; - if (message.startsWith("Rate")) { - await sleep(3000); - try { - user = await get("/me", auth(cookies)); - } catch (retryError) { - if (isInvalidTokenError(retryError) && pathname !== "/logout") { - redirect(307, "/logout"); + let hasArk = false; + if (token) { + try { + user = await get("/me", auth(cookies)); + } catch (e) { + const { message } = e as Error; + if (message.startsWith("Rate")) { + await sleep(3000); + try { + user = await get("/me", auth(cookies)); + } catch (retryError) { + if (isInvalidTokenError(retryError) && pathname !== "/logout") { + redirect(307, "/logout"); + } + throw retryError; } - throw retryError; + } else if (isInvalidTokenError(e) && pathname !== "/logout") { + redirect(307, "/logout"); + } else { + throw e; } - } else if (isInvalidTokenError(e) && pathname !== "/logout") { - redirect(307, "/logout"); - } else { - throw e; } - } - const hasArk = (await get("/accounts", auth(cookies))).some((a: any) => a.type === "ark"); + hasArk = (await get("/accounts", auth(cookies))).some((a: any) => a.type === "ark"); + } let subject; if (url.pathname.includes("/invoice") && id) { diff --git a/src/routes/(app)/fund/[id]/+page.svelte b/src/routes/(app)/fund/[id]/+page.svelte index dea5a19b..c19db2ac 100644 --- a/src/routes/(app)/fund/[id]/+page.svelte +++ b/src/routes/(app)/fund/[id]/+page.svelte @@ -2,55 +2,51 @@ import PhGearBold from "virtual:icons/ph/gear-bold"; import PhPlusBold from "virtual:icons/ph/plus-bold"; import PhHandCoinsBold from "virtual:icons/ph/hand-coins-bold"; - import PhLinkBold from "virtual:icons/ph/link-bold"; - import PhGiftBold from "virtual:icons/ph/gift-bold"; - import PhLightningFill from "virtual:icons/ph/lightning-fill"; + import PhShareNetworkBold from "virtual:icons/ph/share-network-bold"; + import PhTrashBold from "virtual:icons/ph/trash-bold"; + import PhCopyBold from "virtual:icons/ph/copy-bold"; import Amount from "$comp/Amount.svelte"; import Payments from "$comp/Payments.svelte"; - import { enhance } from "$app/forms"; + import { invalidate } from "$app/navigation"; import { page } from "$app/stores"; import Avatar from "$comp/Avatar.svelte"; import { toast } from "@zerodevx/svelte-toast"; import { t } from "$lib/translations"; - import { types, copy, f, s, sat, sats, loc } from "$lib/utils"; + import { copy, sats, loc, post } from "$lib/utils"; import { loginRedirect } from "$lib/store"; - import { bech32 } from "@scure/base"; - const encoder = new TextEncoder(); let { data }: any = $props(); let amount = $derived(data.amount); let payments = $derived(data.payments); let managers = $derived(data.managers); + let authorizations = $derived(data.authorizations || []); let rate = $derived(data.rate); let user = $derived(data.user); let { id } = $page.params; let locale = $derived(loc(user)); let currency = $derived(user ? user.currency : "CAD"); + let isManager = $derived(!managers.length || managers.some((m) => m.id === user?.id)); let amountFiat = $derived(parseFloat(((amount * rate) / sats).toFixed(2))); $effect(() => { $loginRedirect = $page.url.pathname; }); - let show = $state(); - let toggle = () => (show = !show); - let href = $derived(encodeURIComponent($page.url.href)); - // let withdrawUrl = encoder.encode( - // `${$page.url.origin}/api/fund/${id}/withdraw`, - // ); - // let lnurlw = bech32.encode("lnurl", bech32.toWords(withdrawUrl), 20000); + async function deleteAuth(authId: string) { + try { + await post(`/fund/${id}/authorization/${authId}/delete`, {}); + invalidate("app:payments"); + } catch (e: any) { + toast.push(e.message); + } + }
- {#if show} -
- URL -
- {/if}
@@ -74,7 +70,7 @@ {/if}
- {#if (user && !managers.length) || managers.some((m) => m.id === user?.id)} + {#if user && isManager} {$t("funds.manage")} @@ -90,7 +86,7 @@
- {#if !managers.length || managers.some((m) => m.id === user?.id)} + {#if isManager} + {#if isManager && authorizations.length} +
+
{$t("funds.authorizations")}
+ {#each authorizations as auth} +
+
+ {auth.fiat} {auth.currency} + {new Date(auth.created).toLocaleDateString(locale)} +
+
+ + +
+
+ {/each} +
+ {/if}
diff --git a/src/routes/(app)/fund/[id]/gift/+page.server.ts b/src/routes/(app)/fund/[id]/gift/+page.server.ts deleted file mode 100644 index 549fc9d0..00000000 --- a/src/routes/(app)/fund/[id]/gift/+page.server.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const load = async ({ params, parent }) => { - const p = await parent(); - const { amount, rate } = p; - const { id } = params; - return { amount, id, rate }; -}; diff --git a/src/routes/(app)/fund/[id]/gift/+page.svelte b/src/routes/(app)/fund/[id]/gift/+page.svelte deleted file mode 100644 index c7c82ccd..00000000 --- a/src/routes/(app)/fund/[id]/gift/+page.svelte +++ /dev/null @@ -1,41 +0,0 @@ - - -
-

{$t("payments.gift")}

- - - -
-

{$t("payments.giftDesc1")}

-

{$t("payments.giftDesc2")}

-
- -
- - - - - -
-
diff --git a/src/routes/(app)/fund/[id]/share/+page.server.ts b/src/routes/(app)/fund/[id]/share/+page.server.ts new file mode 100644 index 00000000..549fc9d0 --- /dev/null +++ b/src/routes/(app)/fund/[id]/share/+page.server.ts @@ -0,0 +1,6 @@ +export const load = async ({ params, parent }) => { + const p = await parent(); + const { amount, rate } = p; + const { id } = params; + return { amount, id, rate }; +}; diff --git a/src/routes/(app)/fund/[id]/share/+page.svelte b/src/routes/(app)/fund/[id]/share/+page.svelte new file mode 100644 index 00000000..5a3d0276 --- /dev/null +++ b/src/routes/(app)/fund/[id]/share/+page.svelte @@ -0,0 +1,64 @@ + + +
+

{$t("payments.shareLink")}

+ + + +
+ {$t("payments.denominateIn")} {currency} + +
+ + + +
+
+ or +
+
+ + +
diff --git a/src/routes/(app)/fund/[id]/sweep/[...amount]/+page.server.ts b/src/routes/(app)/fund/[id]/sweep/[...amount]/+page.server.ts index a20f8764..362e3387 100644 --- a/src/routes/(app)/fund/[id]/sweep/[...amount]/+page.server.ts +++ b/src/routes/(app)/fund/[id]/sweep/[...amount]/+page.server.ts @@ -7,7 +7,20 @@ import { env } from "$env/dynamic/private"; export async function load({ cookies, request, params, parent }) { let { user } = await parent(); const { id } = params; - let [amount, currency] = params.amount.split("/"); + const parts = params.amount.split("/"); + + let amount: string | undefined; + let currency: string | undefined; + let authId: string | undefined; + + let urlHasCurrency = false; + if (parts[0] === "auth") { + authId = parts[1]; + } else { + amount = parts[0]; + currency = parts[1]; + if (currency) urlHasCurrency = true; + } if (!currency) currency = user?.currency; if (currency) currency = currency.toUpperCase(); @@ -17,8 +30,17 @@ export async function load({ cookies, request, params, parent }) { if (!amount) { const balance = await get(`/fund/${id}`); - amount = balance.authorization || balance.amount; - } else if (currency) { + if (authId) { + const a = balance.authorizations?.find((a: any) => a.authId === authId); + if (a) amount = a.amount; + } + if (!amount && balance.authorizations?.length) { + const a = balance.authorizations[0]; + authId = a.authId; + amount = a.amount; + } + if (!amount) amount = balance.amount; + } else if (urlHasCurrency) { amount = String(Math.round((Number(amount) * sats) / rate)); } @@ -45,7 +67,7 @@ export async function load({ cookies, request, params, parent }) { } try { - ({ amount } = await post("/take", { amount, id }, auth(cookies))); + ({ amount } = await post("/take", { amount, id, authId }, auth(cookies))); } catch { redirect(307, `/fund/${id}`); } diff --git a/src/routes/(app)/fund/[id]/withdraw/+page.svelte b/src/routes/(app)/fund/[id]/withdraw/+page.svelte index 8dd52e6e..c09c9049 100644 --- a/src/routes/(app)/fund/[id]/withdraw/+page.svelte +++ b/src/routes/(app)/fund/[id]/withdraw/+page.svelte @@ -40,7 +40,7 @@
{form.message}
{/if} -
+
@@ -69,4 +69,5 @@
+
diff --git a/src/routes/(app)/qr/[text]/+page.svelte b/src/routes/(app)/qr/[text]/+page.svelte index 9c030ef1..d89343c2 100644 --- a/src/routes/(app)/qr/[text]/+page.svelte +++ b/src/routes/(app)/qr/[text]/+page.svelte @@ -1,4 +1,5 @@