diff options
| author | Adam Soltys <[email protected]> | 2026-03-03 16:08:52 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-03-03 16:08:52 -0800 |
| commit | 8551df92b96ff69f3cda50184cb714ba30f3803d (patch) | |
| tree | b53b414d20d40d9bb9ab33764caa113c5996a262 /src | |
| parent | 14fff3ba852b1d696f61a0da25aaf0e04ccd4158 (diff) | |
| download | coinos-ui-8551df92b96ff69f3cda50184cb714ba30f3803d.tar.gz coinos-ui-8551df92b96ff69f3cda50184cb714ba30f3803d.zip | |
get rid of /receive route
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/Account.svelte | 2 | ||||
| -rw-r--r-- | src/components/InvoiceData.svelte | 38 | ||||
| -rw-r--r-- | src/components/InvoiceTypes.svelte | 16 | ||||
| -rw-r--r-- | src/components/SetType.svelte | 3 | ||||
| -rw-r--r-- | src/lib/socket.ts | 1 | ||||
| -rw-r--r-- | src/routes/(app)/+layout.svelte | 5 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/receive/+page.server.js | 16 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/receive/+page.svelte | 187 | ||||
| -rw-r--r-- | src/routes/(app)/invoice/+page.server.ts | 12 | ||||
| -rw-r--r-- | src/routes/(app)/invoice/[id]/+page.svelte | 94 | ||||
| -rw-r--r-- | src/routes/(app)/pay/[username]/[...amount]/+page.server.ts | 4 | ||||
| -rw-r--r-- | src/routes/(app)/qr/[text]/raw/+server.ts | 2 | ||||
| -rw-r--r-- | src/routes/+layout.ts | 2 | ||||
| -rw-r--r-- | src/routes/start/+server.ts | 2 |
14 files changed, 118 insertions, 266 deletions
diff --git a/src/components/Account.svelte b/src/components/Account.svelte index 44b53bdf..0be08a05 100644 --- a/src/components/Account.svelte +++ b/src/components/Account.svelte @@ -86,7 +86,7 @@ } }; - let receiveUrl = $derived(id === user.id ? `/${user.username}/receive` : "/invoice"); + let receiveUrl = $derived(id === user.id ? "/invoice?lnurl=true" : "/invoice"); let displayType = $derived( accountType === "ark" diff --git a/src/components/InvoiceData.svelte b/src/components/InvoiceData.svelte index 617f6f42..ece30457 100644 --- a/src/components/InvoiceData.svelte +++ b/src/components/InvoiceData.svelte @@ -24,31 +24,34 @@ let { memo } = $derived(invoice); let qrSrc = $derived(`/qr/${encodeURIComponent(invoice.text)}/raw`); - let visibleSrc = $state(""); - let first = true; + let srcs: string[] = $state([]); $effect(() => { const next = qrSrc; - if (first) { - first = false; - visibleSrc = next; - return; + if (!srcs.length) { + srcs = [next]; + } else if (next !== srcs[srcs.length - 1]) { + srcs = [...srcs, next]; } - const img = new Image(); - img.onload = () => (visibleSrc = next); - img.src = next; }); + + let onLoaded = (src: string) => { + const idx = srcs.indexOf(src); + if (idx > 0) srcs = srcs.slice(idx); + }; </script> {#if showQr} - <div class="max-w-[280px] w-full mx-auto"> - <a href={link}> - <img - src={visibleSrc} - class="z-10 border-4 border-white" - alt={txt} - /> - </a> + <div class="max-w-[280px] w-full mx-auto aspect-square relative"> + {#each srcs as src, i (src)} + <a href={link} class="absolute inset-0" style:z-index={i}> + <img + {src} + alt={txt} + onload={srcs.length > 1 ? () => onLoaded(src) : undefined} + /> + </a> + {/each} </div> {/if} @@ -92,3 +95,4 @@ {#if memo} <div class="text-xl text-center">{memo}</div> {/if} + diff --git a/src/components/InvoiceTypes.svelte b/src/components/InvoiceTypes.svelte index b518364d..489fb367 100644 --- a/src/components/InvoiceTypes.svelte +++ b/src/components/InvoiceTypes.svelte @@ -2,7 +2,6 @@ import PhLightningFill from "virtual:icons/ph/lightning-fill"; import PhXBold from "virtual:icons/ph/x-bold"; import { types } from "$lib/utils"; - import { page } from "$app/stores"; import { amountPrompt } from "$lib/store"; let { newAmount = $bindable(), @@ -14,6 +13,7 @@ setAmount, setType, activeOnly = false, + lnurlMode = false, } = $props(); let { aid, address_type, type } = $derived(invoice); @@ -60,10 +60,9 @@ <div class="flex flex-wrap gap-2"> <button class="btn flex-nowrap !w-auto grow" - class:bg-base-300={type === types.lightning && $page.url.pathname.includes("/receive")} - class:text-secondary={type === types.lightning && $page.url.pathname.includes("/receive")} - class:hidden={activeOnly && - (type !== types.lightning || !$page.url.pathname.includes("/receive"))} + class:bg-base-300={lnurlMode} + class:text-secondary={lnurlMode} + class:hidden={activeOnly && !lnurlMode} onclick={() => { if (activeOnly) toggleType(); else { @@ -97,10 +96,9 @@ <button class="btn flex-nowrap !w-auto grow" - class:bg-base-300={type === types.lightning && !$page.url.pathname.includes("/receive")} - class:text-secondary={type === types.lightning && !$page.url.pathname.includes("/receive")} - class:hidden={activeOnly && - (type !== types.lightning || $page.url.pathname.includes("/receive"))} + class:bg-base-300={type === types.lightning && !lnurlMode} + class:text-secondary={type === types.lightning && !lnurlMode} + class:hidden={activeOnly && !(type === types.lightning && !lnurlMode)} onclick={() => { if (activeOnly) toggleType(); else { diff --git a/src/components/SetType.svelte b/src/components/SetType.svelte index 99bf676f..334c49ca 100644 --- a/src/components/SetType.svelte +++ b/src/components/SetType.svelte @@ -13,6 +13,7 @@ setType, toggleType, activeOnly = false, + lnurlMode = false, t, }: any = $props(); </script> @@ -20,7 +21,7 @@ {#if settingType} <div class="fixed inset-0 bg-base-100/90 overflow-y-auto h-full w-full z-50"> <div class="relative mx-auto p-12 max-w-xl shadow-lg rounded-md bg-base-100 space-y-2"> - <InvoiceTypes bind:newAmount {setAmount} {invoice} {user} {hasArk} {setType} {toggleType} {t} /> + <InvoiceTypes bind:newAmount {setAmount} {invoice} {user} {hasArk} {setType} {toggleType} {lnurlMode} {t} /> </div> </div> {/if} diff --git a/src/lib/socket.ts b/src/lib/socket.ts index 2262f388..6fbcaeb6 100644 --- a/src/lib/socket.ts +++ b/src/lib/socket.ts @@ -64,7 +64,6 @@ export const messages = (data) => ({ if (amount > 0 && !vaultForwarding) { if ( pathname.includes(`/${username}`) || - pathname.includes("/receive") || pathname.includes("/invoice") ) { if (!get(navigating) && iid) { diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index f6c496c4..6cbcad2b 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -172,6 +172,11 @@ onMount(async () => { if (browser) { + if (import.meta.env.DEV && navigator.serviceWorker) { + const regs = await navigator.serviceWorker.getRegistrations(); + for (const reg of regs) reg.unregister(); + } + $offline = !navigator.onLine; const goOnline = () => ($offline = false); const goOffline = () => ($offline = true); diff --git a/src/routes/(app)/[username]/receive/+page.server.js b/src/routes/(app)/[username]/receive/+page.server.js deleted file mode 100644 index 2aa875dd..00000000 --- a/src/routes/(app)/[username]/receive/+page.server.js +++ /dev/null @@ -1,16 +0,0 @@ -import getRates from "$lib/rates"; -import { redirect } from "@sveltejs/kit"; - -export const load = async ({ cookies, params: { username }, parent, url }) => { - const token = cookies.get("token"); - const rates = await getRates(); - if (!token) redirect(307, `/pay/${username}`); - const { subject, user } = await parent(); - const aid = cookies.get("aid"); - - if (aid && user?.id !== aid) redirect(307, "/invoice"); - return { - rate: rates[subject.currency], - text: `${username}@${url.host}`, - }; -}; diff --git a/src/routes/(app)/[username]/receive/+page.svelte b/src/routes/(app)/[username]/receive/+page.svelte deleted file mode 100644 index 10aea0ef..00000000 --- a/src/routes/(app)/[username]/receive/+page.svelte +++ /dev/null @@ -1,187 +0,0 @@ -<script lang="ts"> - import { run } from "svelte/legacy"; - import { btc, loc, post, copy, fail, f, get, types, sat, s, sats } from "$lib/utils"; - import { tick, onMount, onDestroy } from "svelte"; - import { browser } from "$app/environment"; - import { last, lastPayment, showQr, amountPrompt } from "$lib/store"; - import Avatar from "$comp/Avatar.svelte"; - import InvoiceData from "$comp/InvoiceData.svelte"; - import InvoiceActions from "$comp/InvoiceActions.svelte"; - import SetAmount from "$comp/SetAmount.svelte"; - import SetMemo from "$comp/SetMemo.svelte"; - import SetType from "$comp/SetType.svelte"; - import { t } from "$lib/translations"; - import { goto, invalidate } from "$app/navigation"; - import { page } from "$app/stores"; - - let { data }: any = $props(); - - let showOptions; - - let id = $state(); - let { rate, subject, user, text, hasArk } = $derived(data); - let locale = $derived(loc(user)); - - let { currency, username } = $derived(subject); - let type = types.lightning; - let hash = ""; - let qr; - - let aid = $derived(subject.id); - let invoice: any = $derived({ - aid, - type: types.lightning, - items: [], - rate, - text, - uid: subject.id, - user: subject, - }); - let amount = $state(), - amountFiat, - tip; - - let update = async () => { - try { - ({ id } = await post(`/invoice`, { - invoice, - user: { username, currency }, - })); - - let url = `/invoice/${id}`; - if (subject?.prompt) url += "/tip"; - else url += "?options=true"; - - goto(url, { invalidateAll: true, noScroll: true }); - } catch (e: any) { - fail(e.message); - } - }; - - let settingType = $state(); - let toggleType = () => (settingType = !settingType); - let setType = async (type, address_type) => { - $showQr = true; - if (type === types.lightning && !amount && typeof newAmount === "undefined") - goto(`/${username}/receive`, { invalidateAll: true, noScroll: true }); - else { - if (typeof newAmount !== "undefined") invoice.amount = newAmount; - invoice.address_type = address_type; - invoice.type = type; - await update(); - } - settingType = false; - }; - - let setAmount = async (e) => { - e?.preventDefault(); - e?.stopPropagation(); - - settingAmount = false; - - if (typeof $amountPrompt === "undefined") $amountPrompt = true; - amount = newAmount; - invoice.amount = newAmount; - invoice.tip = null; - - await update(); - }; - - let newAmount = $state(0); - let settingAmount = $state(); - let fiat = $state(true); - let toggleAmount = () => (settingAmount = !settingAmount); - - let setMemo = async (e) => { - e.preventDefault(); - e.stopPropagation(); - - settingMemo = false; - invoice.memo = memo; - await update(); - }; - - let memo = $state(); - let settingMemo = $state(); - let toggleMemo = () => (settingMemo = !settingMemo); - - let link = $derived([types.bitcoin, types.liquid].includes(type) ? text : `lightning:${text}`); - let txt = $derived([types.bitcoin, types.liquid].includes(type) ? hash : text); - - onMount(() => { - $lastPayment = null; - if ($amountPrompt && !amount) toggleAmount(); - let address_type = $page.url.searchParams.get("address_type"); - if (address_type) setType(types.bitcoin, address_type); - }); - - $effect(() => { - if ($lastPayment?.iid) { - const { iid, confirmed } = $lastPayment; - const target = confirmed ? `/invoice/${iid}/paid` : `/invoice/${iid}`; - $lastPayment = null; - goto(target); - } - }); -</script> - -<div class="invoice container mx-auto max-w-xl px-4 space-y-2"> - <InvoiceData - {locale} - {link} - {qr} - {txt} - {invoice} - {amount} - {amountFiat} - {currency} - {tip} - {rate} - showQr={$showQr} - t={$t} - /> - - <InvoiceActions - bind:newAmount - {setAmount} - {toggleType} - {setType} - {toggleAmount} - {toggleMemo} - {user} - {invoice} - {copy} - {link} - {showQr} - {txt} - t={$t} - /> -</div> - -<SetAmount - bind:newAmount - bind:fiat - {currency} - locale={loc(user)} - {rate} - {settingAmount} - {setAmount} - {toggleAmount} - t={$t} -/> - -<SetMemo bind:memo {settingMemo} {setMemo} {toggleMemo} t={$t} /> - -<SetType - {aid} - {invoice} - {user} - {hasArk} - {type} - {settingType} - {setType} - {toggleType} - t={$t} - bind:newAmount - {setAmount} -/> diff --git a/src/routes/(app)/invoice/+page.server.ts b/src/routes/(app)/invoice/+page.server.ts index 92cfd18d..e3d8125f 100644 --- a/src/routes/(app)/invoice/+page.server.ts +++ b/src/routes/(app)/invoice/+page.server.ts @@ -3,16 +3,20 @@ import type { Invoice } from "$lib/types"; import { auth, post } from "$lib/utils"; import { error, redirect } from "@sveltejs/kit"; -export const load = async ({ cookies, parent }) => { +export const load = async ({ cookies, parent, url }) => { const aid = cookies.get("aid"); let { subject, user } = await parent(); const rates = await getRates(); - if (aid === user.id) redirect(307, `/${user.username}/receive`); + + const type = url.searchParams.get("type") || undefined; + const address_type = url.searchParams.get("address_type") || undefined; let invoice: Invoice = { aid: aid || "", rate: rates[user?.currency || subject?.currency], + ...(type && { type }), + ...(address_type && { address_type }), }; if (!user) user = subject; @@ -26,7 +30,9 @@ export const load = async ({ cookies, parent }) => { const { id } = invoice; + const lnurl = url.searchParams.has("lnurl"); + if (invoice.memoPrompt && !invoice.memo) { redirect(307, `/invoice/${id}/memo`); - } else redirect(307, `/invoice/${id}`); + } else redirect(307, `/invoice/${id}${lnurl ? "?lnurl=true" : ""}`); }; diff --git a/src/routes/(app)/invoice/[id]/+page.svelte b/src/routes/(app)/invoice/[id]/+page.svelte index 9dac7a7c..6405bd6f 100644 --- a/src/routes/(app)/invoice/[id]/+page.svelte +++ b/src/routes/(app)/invoice/[id]/+page.svelte @@ -5,7 +5,8 @@ import { btc, loc, post, copy, f, fail, success, get, types, sat, s, sats } from "$lib/utils"; import { tick, onMount, onDestroy } from "svelte"; import { browser } from "$app/environment"; - import { last, showQr, amountPrompt } from "$lib/store"; + import { last, lastPayment, showQr, amountPrompt } from "$lib/store"; + import { page } from "$app/stores"; import Avatar from "$comp/Avatar.svelte"; import InvoiceData from "$comp/InvoiceData.svelte"; import InvoiceActions from "$comp/InvoiceActions.svelte"; @@ -37,6 +38,18 @@ let { username, currency } = $derived(invoice.user); let locale = $derived(loc(user)); + let lnurlMode = $state(false); + let lnAddress = $derived(`${username}@${$page.url.host}`); + + $effect(() => { + if ($page.url.searchParams.has("lnurl")) { + lnurlMode = true; + $showQr = true; + } else { + lnurlMode = false; + } + }); + let tipPercent = $derived(tip ? (tip / amount) * 100 : 0); $effect(() => { @@ -67,14 +80,14 @@ if ($amountPrompt && !amount) toggleAmount(); }); - let update = async () => { + let update = async (changes: any = {}) => { try { - ({ id } = await post(`/invoice`, { - invoice, + const result = await post(`/invoice`, { + invoice: { ...$state.snapshot(invoice), ...changes }, user: { username, currency }, - })); + }); - let url = `/invoice/${id}`; + let url = `/invoice/${result.id}`; if (subject?.prompt) url += "/tip"; else url += "?options=true"; @@ -88,20 +101,21 @@ let toggleType = () => (settingType = !settingType); let setType = async (type, address_type) => { $showQr = true; - if (type === types.lightning && !amount && typeof newAmount === "undefined") - goto(`/${username}/receive`, { invalidateAll: true, noScroll: true }); - else { - if (typeof newAmount !== "undefined") invoice.amount = newAmount; - invoice.address_type = address_type; - invoice.type = type; + if (type === types.lightning && !amount && typeof newAmount === "undefined") { + lnurlMode = true; + settingType = false; + return; + } - if (type === "ark" && invoice.aid !== invoice.uid) { - const wallet = await getWallet(); - invoice.hash = await wallet.getAddress(); - } + const changes: any = { type, address_type }; + if (typeof newAmount !== "undefined") changes.amount = newAmount; - await update(); + if (type === "ark" && invoice.aid !== invoice.uid) { + const wallet = await getWallet(); + changes.hash = await wallet.getAddress(); } + + await update(changes); settingType = false; }; @@ -116,10 +130,10 @@ settingAmount = false; if (typeof $amountPrompt === "undefined") $amountPrompt = true; - invoice.amount = newAmount; - invoice.tip = 0; + const changes: any = { amount: newAmount, tip: 0 }; + if (lnurlMode) changes.type = types.lightning; - await update(); + await update(changes); }; let setMemo = async (e) => { @@ -127,8 +141,7 @@ e.stopPropagation(); settingMemo = false; - invoice.memo = memo; - await update(); + await update({ memo }); }; let settingMemo = $state(); @@ -137,9 +150,37 @@ let fiat = $state(true); let amountFiat = $derived(parseFloat(((amount * rate) / sats).toFixed(2))); let tipAmount = $derived(((tip * rate) / sats).toFixed(2)); - let link = $derived([types.bitcoin, types.liquid].includes(type) ? text : `lightning:${text}`); - let txt = $derived([types.bitcoin, types.liquid].includes(type) ? hash : text); + let displayInvoice = $derived( + lnurlMode + ? { ...invoice, text: lnAddress, type: types.lightning, items: [] } + : invoice, + ); + + let link = $derived( + lnurlMode + ? `lightning:${lnAddress}` + : [types.bitcoin, types.liquid].includes(type) + ? text + : `lightning:${text}`, + ); + + let txt = $derived( + lnurlMode + ? lnAddress + : [types.bitcoin, types.liquid].includes(type) + ? hash + : text, + ); + + $effect(() => { + if (lnurlMode && $lastPayment?.iid) { + const { iid, confirmed } = $lastPayment; + const target = confirmed ? `/invoice/${iid}/paid` : `/invoice/${iid}`; + $lastPayment = null; + goto(target); + } + }); </script> <div class="invoice container mx-auto max-w-xl px-4 pb-4 space-y-2"> @@ -147,7 +188,7 @@ {src} {link} {txt} - {invoice} + invoice={displayInvoice} {amount} {amountFiat} {currency} @@ -166,7 +207,7 @@ {toggleAmount} {toggleMemo} {user} - {invoice} + invoice={displayInvoice} {copy} {link} {txt} @@ -200,5 +241,6 @@ {settingType} {setType} {toggleType} + {lnurlMode} t={$t} /> diff --git a/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts b/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts index ca0f382d..75cdac63 100644 --- a/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts +++ b/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts @@ -25,7 +25,7 @@ export async function load({ cookies, params, parent }) { redirect(307, `/send/ark/${serverAddr}`); } } - redirect(307, `/${username}/receive`); + redirect(307, `/invoice`); } const subject = await get(`/users/${username}`); @@ -84,7 +84,7 @@ export const actions = { } if (parseInt(amount) > 0 && prompt) redirect(307, `/invoice/${id}/tip`); - else if (parseInt(amount) <= 0) redirect(307, `/${username}/receive`); + else if (parseInt(amount) <= 0) redirect(307, `/invoice/${id}`); redirect(307, `/invoice/${id}`); }, }; diff --git a/src/routes/(app)/qr/[text]/raw/+server.ts b/src/routes/(app)/qr/[text]/raw/+server.ts index 77bb8a09..baac03e5 100644 --- a/src/routes/(app)/qr/[text]/raw/+server.ts +++ b/src/routes/(app)/qr/[text]/raw/+server.ts @@ -3,7 +3,7 @@ import QRCode from "qrcode"; export async function GET({ params }) { const { text } = params; - const buffer = await QRCode.toBuffer(text, { width: 600, type: "png" }); + const buffer = await QRCode.toBuffer(text, { width: 600, type: "png", margin: 1 }); return new Response(buffer, { headers: { diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 56a0cc14..b2b71c26 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -21,7 +21,7 @@ export const load = async ({ data }) => { setCookie("theme", theme, 21000000); - if (theme === "dark") setTimeout(() => themeStore.set(theme), 100); + if (theme === "dark") setTimeout(() => themeStore.set(theme!), 100); } } diff --git a/src/routes/start/+server.ts b/src/routes/start/+server.ts index d31d1b3d..2d1319ec 100644 --- a/src/routes/start/+server.ts +++ b/src/routes/start/+server.ts @@ -5,7 +5,7 @@ export async function GET({ cookies }) { const username = cookies.get("username"); if (username) { if (pathname) redirect(307, pathname); - else redirect(307, `/${username}/receive`); + else redirect(307, `/${username}`); } redirect(307, "/"); |
