aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/components/Request.svelte7
-rw-r--r--src/lib/invoice.js6
-rw-r--r--src/lib/store.js1
-rw-r--r--src/routes/(app)/[username]/+page.svelte56
-rw-r--r--src/routes/(app)/[username]/dashboard/+page.server.js3
-rw-r--r--src/routes/(app)/[username]/dashboard/+page.svelte62
-rw-r--r--src/routes/(app)/[username]/receive/[...network]/+page.server.js5
-rw-r--r--src/routes/(app)/[username]/receive/[...network]/+page.svelte79
-rw-r--r--src/routes/(app)/[username]/receive/[...param]/+page.server.js15
-rw-r--r--src/routes/(app)/[username]/receive/[...param]/+page.svelte49
-rw-r--r--src/routes/(app)/[username]/request/[id]/+page.server.js3
-rw-r--r--src/routes/(app)/[username]/request/[id]/+page.svelte38
-rw-r--r--src/routes/(app)/send/[...text]/+page.svelte81
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>