diff options
| -rw-r--r-- | src/components/Request.svelte | 7 | ||||
| -rw-r--r-- | src/lib/invoice.js | 6 | ||||
| -rw-r--r-- | src/lib/store.js | 1 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/+page.svelte | 56 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/dashboard/+page.server.js | 3 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/dashboard/+page.svelte | 62 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/receive/[...network]/+page.server.js | 5 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/receive/[...network]/+page.svelte | 79 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/receive/[...param]/+page.server.js | 15 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/receive/[...param]/+page.svelte | 49 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/request/[id]/+page.server.js | 3 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/request/[id]/+page.svelte | 38 | ||||
| -rw-r--r-- | src/routes/(app)/send/[...text]/+page.svelte | 81 |
13 files changed, 250 insertions, 155 deletions
diff --git a/src/components/Request.svelte b/src/components/Request.svelte index e1071272..2f05dc75 100644 --- a/src/components/Request.svelte +++ b/src/components/Request.svelte @@ -1,9 +1,12 @@ <script> import { fly } from 'svelte/transition'; import { Avatar, Icon } from '$comp'; - import { request } from '$lib/store'; + import { request, requestRedirect } from '$lib/store'; + import { page } from '$app/stores'; export let user; + + $requestRedirect = $page.url.pathname; </script> {#if $request} @@ -19,7 +22,7 @@ {/if} </div> <div class="mx-auto my-auto flex gap-2"> - <a href={`/${user.username}/receive`}> + <a href={`/${user.username}/receive/${$request.id}`}> <button class="text-lg rounded-full border py-3 px-7 font-bold hover:opacity-80 w-40"> Invoice </button> diff --git a/src/lib/invoice.js b/src/lib/invoice.js index 5b538178..6ec7d41b 100644 --- a/src/lib/invoice.js +++ b/src/lib/invoice.js @@ -6,7 +6,7 @@ export default async ({ cookies, request }) => { let rates = await get('/rates'); let amount = parseInt(form.get('amount')); - let requester = form.get('requester'); + let request_id = form.get('request_id'); let invoice = { amount, @@ -14,14 +14,14 @@ export default async ({ cookies, request }) => { network: form.get('network'), prompt: form.get('prompt') === 'true', rate: parseFloat(form.get('rate')) || rates[form.get('currency')], - requester + request_id }; let user = { username: form.get('username') }; let { uuid } = await post('/invoice', { invoice, user }, auth(cookies)); - if (requester) return { amount }; + if (request_id) throw redirect(307, `/${user.username}/request/${request_id}`); if (invoice.prompt) { throw redirect(307, `/invoice/${uuid}/tip`); diff --git a/src/lib/store.js b/src/lib/store.js index 3105419c..a273b756 100644 --- a/src/lib/store.js +++ b/src/lib/store.js @@ -31,6 +31,7 @@ const persistLocal = (key, defaultValue) => { return s; }; +export const requestRedirect = writable(); export const loginRedirect = writable(); export const rate = writable(); export const user = writable(); diff --git a/src/routes/(app)/[username]/+page.svelte b/src/routes/(app)/[username]/+page.svelte index c865a081..35606958 100644 --- a/src/routes/(app)/[username]/+page.svelte +++ b/src/routes/(app)/[username]/+page.svelte @@ -18,42 +18,42 @@ {subject.address && subject.address !== 'null' ? subject.address : ''} </div> - <div class="flex flex-wrap gap-2 w-full"> - <div class="w-full flex"> - <a href={`/${subject.username}/receive`} class="mx-auto"> - <button class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-40"> - <div class="mx-auto flex"> - <div style="mr-1">⚡️</div> - <div>Lightning</div> - </div> - </button> - </a> - </div> + <div class="flex flex-wrap gap-2 w-full"> + <div class="w-full flex"> + <a href={`/${subject.username}/receive`} class="mx-auto"> + <button class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-40"> + <div class="mx-auto flex"> + <div style="mr-1">⚡️</div> + <div>Lightning</div> + </div> + </button> + </a> + </div> + + <div class="w-full flex"> + <a href={`/${subject.username}/receive/bitcoin`} class="mx-auto"> + <button class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-40"> + <div class="mx-auto flex"> + <img src="/images/bitcoin.svg" class="w-5 my-auto mr-1" /> + <div>Bitcoin</div> + </div> + </button> + </a> + </div> + {#if user?.username !== subject.username} <div class="w-full flex"> - <a href={`/${subject.username}/receive/bitcoin`} class="mx-auto"> + <a href={`/${subject.username}/request`} class="mx-auto"> <button class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-40"> <div class="mx-auto flex"> - <img src="/images/bitcoin.svg" class="w-5 my-auto mr-1" /> - <div>Bitcoin</div> + <Icon icon="support" style="mr-1" /> + <div>Invoice</div> </div> </button> </a> </div> - - {#if user?.username !== subject.username} - <div class="w-full flex"> - <a href={`/${subject.username}/request`} class="mx-auto"> - <button class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-40"> - <div class="mx-auto flex"> - <Icon icon="support" style="mr-1" /> - <div>Invoice</div> - </div> - </button> - </a> - </div> - {/if} - </div> + {/if} + </div> </div> <div class="w-full flex"> diff --git a/src/routes/(app)/[username]/dashboard/+page.server.js b/src/routes/(app)/[username]/dashboard/+page.server.js new file mode 100644 index 00000000..b283bc9d --- /dev/null +++ b/src/routes/(app)/[username]/dashboard/+page.server.js @@ -0,0 +1,3 @@ +import { auth, get } from '$lib/utils'; + +export let load = async ({ cookies }) => get('/requests', auth(cookies)); diff --git a/src/routes/(app)/[username]/dashboard/+page.svelte b/src/routes/(app)/[username]/dashboard/+page.svelte index d1bd86b7..3cba081d 100644 --- a/src/routes/(app)/[username]/dashboard/+page.svelte +++ b/src/routes/(app)/[username]/dashboard/+page.svelte @@ -1,12 +1,13 @@ <script> import { f, s, post, failure, sat, sats } from '$lib/utils'; import { tick } from 'svelte'; - import { Icon } from '$comp'; + import { Avatar, Icon } from '$comp'; import { rate, selectedRate } from '$lib/store'; import { goto } from '$app/navigation'; import { t } from '$lib/translations'; export let data; + $: ({ invoices, requests } = data); let ease = (t) => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t); let interval, rateInterval; @@ -88,6 +89,65 @@ > </a> </div> + + {#each invoices as { amount, currency, rate, user, uuid }} + <div class="border-b p-2 last:border-b-0 hover:bg-gray-100"> + <div class="flex"> + <div> + <div class="flex"> + <Avatar {user} size={20} /> + <div class="my-auto text-left"> + <p class="ml-1 text-lg break-words">{user.username}</p> + </div> + </div> + </div> + <div class="whitespace-nowrap my-auto mx-auto flex gap-2"> + <div class="font-bold"> + {f(amount * (rate / sats), currency)} + </div> + + <div class="text-secondary">{sat(amount)}</div> + </div> + <div class="flex ml-auto my-auto gap-2"> + <a href={`/invoice/${uuid}`}> + <div class="p-2 border rounded-xl bg-white flex"> + <Icon icon="send" style="w-8 opacity-50 hover:opacity-100" /> + </div> + </a> + <a href={`/invoice/${uuid}`}> + <div class="p-2 border rounded-xl bg-white flex"> + <Icon icon="close" style="w-8 opacity-50 hover:opacity-100" /> + </div> + </a> + </div> + </div> + </div> + {/each} + + <div> + {#each requests as { id, invoice, memo, requester: r }} + <div class="border-b p-2 last:border-b-0 hover:bg-gray-100"> + <div class="flex"> + <div> + <div class="flex"> + <Avatar user={r} size={20} /> + <div class="my-auto text-left"> + <p class="ml-1 text-lg break-words">{r.username}</p> + <p class="ml-1 text-secondary">{memo}</p> + </div> + </div> + </div> + <div class="flex ml-auto my-auto"> + <a href={`/${user.username}/receive/${id}`}> + <div class="p-2 border rounded-xl bg-white flex"> + <Icon icon="numpad" style="w-8 opacity-50 hover:opacity-100" /> + </div> + </a> + </div> + </div> + </div> + {/each} + </div> </div> <div class="flex fixed bottom-4 w-full px-4"> diff --git a/src/routes/(app)/[username]/receive/[...network]/+page.server.js b/src/routes/(app)/[username]/receive/[...network]/+page.server.js deleted file mode 100644 index 1068f273..00000000 --- a/src/routes/(app)/[username]/receive/[...network]/+page.server.js +++ /dev/null @@ -1,5 +0,0 @@ -import invoice from '$lib/invoice'; - -export const actions = { - default: invoice -}; diff --git a/src/routes/(app)/[username]/receive/[...network]/+page.svelte b/src/routes/(app)/[username]/receive/[...network]/+page.svelte deleted file mode 100644 index f19600fd..00000000 --- a/src/routes/(app)/[username]/receive/[...network]/+page.svelte +++ /dev/null @@ -1,79 +0,0 @@ -<script> - import { Avatar, Icon, Numpad } from '$comp'; - import { t } from '$lib/translations'; - import { enhance } from '$app/forms'; - import { page } from '$app/stores'; - import { invoices, request } from '$lib/store'; - import { browser } from '$app/environment'; - import { goto } from '$app/navigation'; - - export let data; - export let form; - let network = 'lightning'; - - if ($page.params.network) ({ network } = $page.params); - - let req; - - $: load($page); - let load = () => { - if ($request) { - req = { ...$request }; - $request = null; - } - }; - - let amount; - - let { currency, username } = data.subject; - let { user } = data; - - $: paid = browser && form && Object.values($invoices).find((inv) => inv.amount === form.amount); - $: paid && goto(`/invoice/${paid.uuid}/paid`); -</script> - -{#if form && req} - <div class="container px-4 mt-20 max-w-xl mx-auto space-y-8"> - <div class="text-center mb-8 space-y-5"> - <p class="text-5xl break-words"> - <span class="text-xl md:text-2xl">Invoice sent to</span> - </p> - - <div class="flex p-1 gap-2 justify-center"> - <Avatar user={req.requester} size={'20'} /> - <p class="text-4xl break-words my-auto">{req.requester.username}</p> - </div> - </div> - <div class="text-center"> - <a href={`/${user.username}/dashboard`} class="mx-auto text-center"> - <button class="bg-black text-white rounded-2xl w-20 py-3 font-bold hover:opacity-80"> - Done - </button> - </a> - </div> - </div> -{:else} - <form method="POST" class="flex justify-center items-center mt-24 mb-3 px-3" use:enhance> - <input type="hidden" name="amount" value={amount} /> - <input type="hidden" name="currency" value={currency} /> - <input type="hidden" name="username" value={username} /> - <input type="hidden" name="network" value={network} /> - - {#if req} - <input type="hidden" name="requester" value={req.requester.username} /> - {/if} - - <input type="hidden" name="prompt" value="true" /> - - <div class="space-y-3 w-[300px] mx-auto"> - <Numpad bind:amount bind:currency /> - <button - type="submit" - class="bg-black text-white rounded-xl w-full h-[48px] flex justify-center items-center font-semibold - opacity-100 hover:opacity-80" - > - {amount ? 'Next' : "Sender's choice"} - </button> - </div> - </form> -{/if} diff --git a/src/routes/(app)/[username]/receive/[...param]/+page.server.js b/src/routes/(app)/[username]/receive/[...param]/+page.server.js new file mode 100644 index 00000000..1e57aa67 --- /dev/null +++ b/src/routes/(app)/[username]/receive/[...param]/+page.server.js @@ -0,0 +1,15 @@ +import { auth, get } from '$lib/utils'; +import invoice from '$lib/invoice'; + +export let load = async ({ cookies, params }) => { + let id = parseInt(params.param); + + let request; + if (id) ({ request } = await get(`/request/${id}`, auth(cookies))); + + return { ...params, request }; +}; + +export const actions = { + default: invoice +}; diff --git a/src/routes/(app)/[username]/receive/[...param]/+page.svelte b/src/routes/(app)/[username]/receive/[...param]/+page.svelte new file mode 100644 index 00000000..cb69b06a --- /dev/null +++ b/src/routes/(app)/[username]/receive/[...param]/+page.svelte @@ -0,0 +1,49 @@ +<script> + import { Icon, Numpad } from '$comp'; + import { t } from '$lib/translations'; + import { enhance } from '$app/forms'; + import { page } from '$app/stores'; + import { invoices, requestRedirect } from '$lib/store'; + import { browser } from '$app/environment'; + import { goto } from '$app/navigation'; + + export let data; + export let form; + let network = 'lightning'; + + if ($page.params.param === 'bitcoin') network = 'bitcoin'; + + let { request } = data; + + let amount; + + let { currency, username } = data.subject; + let { user } = data; + + $: paid = browser && form && Object.values($invoices).find((inv) => inv.amount === form.amount); + $: paid && goto(`/invoice/${paid.uuid}/paid`); +</script> + +<form method="POST" class="flex justify-center items-center mt-24 mb-3 px-3" use:enhance> + <input type="hidden" name="amount" value={amount} /> + <input type="hidden" name="currency" value={currency} /> + <input type="hidden" name="username" value={username} /> + <input type="hidden" name="network" value={network} /> + + {#if request} + <input type="hidden" name="request_id" value={request.id} /> + {/if} + + <input type="hidden" name="prompt" value="true" /> + + <div class="space-y-3 w-[300px] mx-auto"> + <Numpad bind:amount bind:currency /> + <button + type="submit" + class="bg-black text-white rounded-xl w-full h-[48px] flex justify-center items-center font-semibold + opacity-100 hover:opacity-80" + > + {amount ? 'Next' : "Sender's choice"} + </button> + </div> +</form> diff --git a/src/routes/(app)/[username]/request/[id]/+page.server.js b/src/routes/(app)/[username]/request/[id]/+page.server.js new file mode 100644 index 00000000..79db5d42 --- /dev/null +++ b/src/routes/(app)/[username]/request/[id]/+page.server.js @@ -0,0 +1,3 @@ +import { auth, get } from '$lib/utils'; + +export let load = async ({ cookies, params }) => get(`/request/${params.id}`, auth(cookies)); diff --git a/src/routes/(app)/[username]/request/[id]/+page.svelte b/src/routes/(app)/[username]/request/[id]/+page.svelte new file mode 100644 index 00000000..74b037d3 --- /dev/null +++ b/src/routes/(app)/[username]/request/[id]/+page.svelte @@ -0,0 +1,38 @@ +<script> + import { scale } from 'svelte/transition'; + import { Avatar, Icon } from '$comp'; + import { f, sat, sats } from '$lib/utils'; + import { request as req, requestRedirect } from '$lib/store'; + + export let data; + let { request, user } = data; + let { amount, rate, currency } = request.invoice; + let fiat = (amount * rate) / sats; + req.set(); +</script> + +<div class="container px-4 mt-20 max-w-xl mx-auto space-y-8"> + <div class="flex w-full max-w-[200px] mx-auto" in:scale={{ start: 0.5 }}> + <Icon icon="check" style="mx-auto" /> + </div> + + <div class="text-center mb-8 space-y-5"> + <p class="text-5xl break-words"> + <span class="text-xl md:text-2xl">Sent invoice to</span> + </p> + <div class="flex p-1 gap-2 justify-center"> + <Avatar user={request.requester} size={20} /> + <p class="text-4xl break-words my-auto">{request.requester.username}</p> + </div> + <div> + <h2 class="text-2xl md:text-3xl font-semibold"> + {f(fiat, currency)} + </h2> + <h3 class="text-secondary md:text-lg mb-6 mt-1">{sat(amount)}</h3> + </div> + </div> +</div> + +<a href={$requestRedirect || `/${user.username}/dashboard`}> + <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" /> +</a> diff --git a/src/routes/(app)/send/[...text]/+page.svelte b/src/routes/(app)/send/[...text]/+page.svelte index 39f1f530..09b9cb21 100644 --- a/src/routes/(app)/send/[...text]/+page.svelte +++ b/src/routes/(app)/send/[...text]/+page.svelte @@ -1,30 +1,30 @@ - <script> - import { page } from '$app/stores'; - import { fly } from 'svelte/transition'; - import { enhance } from '$app/forms'; - import { tick } from 'svelte'; - import { browser } from '$app/environment'; - import { t } from '$lib/translations'; - import { Avatar, Icon, Spinner } from '$comp'; - import { back, failure } from '$lib/utils'; - import { format, parseISO } from 'date-fns'; +<script> + import { page } from '$app/stores'; + import { fly } from 'svelte/transition'; + import { enhance } from '$app/forms'; + import { tick } from 'svelte'; + import { browser } from '$app/environment'; + import { t } from '$lib/translations'; + import { Avatar, Icon, Spinner } from '$comp'; + import { back, failure } from '$lib/utils'; + import { format, parseISO } from 'date-fns'; - export let data; - export let form; + export let data; + export let form; - let { contacts } = data; - let w; + let { contacts } = data; + let w; - let el, textarea, text; - let placeholder = 'Paste a bitcoin address, lightning invoice or coinos username'; + let el, textarea, text; + let placeholder = 'Paste a bitcoin address, lightning invoice or coinos username'; - $: $page && setTimeout(() => w > 800 && textarea?.focus(), 0); - let keypress = (e) => e.key === 'Enter' && (e.preventDefault() || el.click()); + $: $page && setTimeout(() => w > 800 && textarea?.focus(), 0); + let keypress = (e) => e.key === 'Enter' && (e.preventDefault() || el.click()); - let paste = async () => { - text = await navigator.clipboard.readText(); - }; - </script> + let paste = async () => { + text = await navigator.clipboard.readText(); + }; +</script> <svelte:window bind:innerWidth={w} /> @@ -91,24 +91,31 @@ <div> {#each contacts as c} <div class="border-b p-2 last:border-b-0 hover:bg-gray-100"> - <a href={`/${c.username}/receive`}> - <div class="flex"> - <div> - <div class="flex"> - <Avatar user={c} size={20} disabled={true} /> - <div class="my-auto text-left"> - <p class="ml-1 text-lg break-words">{c.username}</p> - <p class="ml-1 text-secondary"> - {format(parseISO(c.last), 'MMM d')} - </p> - </div> + <div class="flex"> + <div> + <div class="flex"> + <Avatar user={c} size={20} disabled={true} /> + <div class="my-auto text-left"> + <p class="ml-1 text-lg break-words">{c.username}</p> + <p class="ml-1 text-secondary"> + {format(parseISO(c.last), 'MMM d')} + </p> </div> </div> - <div class="my-auto ml-auto p-2 border rounded-xl bg-white"> - <Icon icon="send" style="w-10" /> - </div> </div> - </a> + <div class="flex ml-auto my-auto gap-2"> + <a href={`/${c.username}/request`}> + <div class="my-auto p-2 border rounded-xl bg-white opacity-50 hover:opacity-100"> + <Icon icon="support" style="w-8" /> + </div> + </a> + <a href={`/${c.username}/receive`}> + <div class="my-auto p-2 border rounded-xl bg-white opacity-50 hover:opacity-100"> + <Icon icon="send" style="w-8" /> + </div> + </a> + </div> + </div> </div> {/each} </div> |
