diff options
Diffstat (limited to 'src/routes/(app)/send/bitcoin')
| -rw-r--r-- | src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js | 22 | ||||
| -rw-r--r-- | src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte | 110 |
2 files changed, 107 insertions, 25 deletions
diff --git a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js index 59e9326d..92a754ae 100644 --- a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js +++ b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js @@ -1,23 +1,29 @@ import { fail, redirect } from '@sveltejs/kit'; -import { auth, post } from '$lib/utils'; +import { auth, post, fd } from '$lib/utils'; let seen = {}; export const actions = { default: async ({ cookies, request }) => { try { - let feeRate, subtract; - let { address, amount, pin, ts } = Object.fromEntries(await request.formData()); - + let { address, amount, confirmed, feeRate, subtract, pin, stale, ts } = await await fd( + request + ); if (seen[ts]) return fail(400, { message: 'Duplicate form submission' }); - seen[ts] = 1; - setTimeout(() => delete seen[ts], 30000); - let { tx } = await post( + let estimate = await post( '/bitcoin/fee', { address, amount, feeRate, subtract }, auth(cookies) ); - await post('/bitcoin/send', { pin, tx }, auth(cookies)); + + let { tx } = estimate; + + if (stale || !confirmed) return fail(400, estimate); + + seen[ts] = 1; + setTimeout(() => delete seen[ts], 30000); + + await post('/bitcoin/send', { pin, subtract, tx }, auth(cookies)); } catch (e) { return fail(400, { message: e.message }); } diff --git a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte index 6b216cb3..77d5fde6 100644 --- a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte +++ b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte @@ -1,24 +1,48 @@ <script> + import { tick } from 'svelte'; import { t } from '$lib/translations'; import { enhance } from '$app/forms'; - import { Icon, Numpad, Spinner } from '$comp'; + import { Toggle, Slider, Icon, Numpad, Spinner } from '$comp'; import { page } from '$app/stores'; - import { back } from '$lib/utils'; - import { pin } from '$lib/store'; + import { fiat as toFiat, f, back, s, sat } from '$lib/utils'; + import { pin, selectedRate } from '$lib/store'; export let data; export let form; let { address, amount } = $page.params; - let { currency } = data.user; - let loading, submit; + let { balance, currency } = data.user; + let loading, submit, fiat, confirmed, feeRate, min, max, fee, ourfee, stale, subtract, adjusting; let toggle = () => (loading = true); + let toggleAdjusting = () => (adjusting = !adjusting); $: update(form); let update = () => { - if (form?.message.includes('pin')) $pin = undefined; loading = false; + if (form?.message?.includes('pin')) $pin = undefined; + if (form) ({ min, max, feeRate, fee, ourfee, subtract } = form); + if (!ourfee) ourfee = 0; + if (feeRate) confirmed = true; + stale = false; + }; + + let setMax = () => { + fiat = false; + amount = balance; + subtract = true; + }; + + $: if (amount + fee > balance) subtract = true; + + let timeout; + let handle = (e) => { + stale = true; + feeRate = e.target.value; + clearTimeout(timeout); + timeout = setTimeout(() => { + tick().then(() => submit.click()); + }, 200); }; </script> @@ -32,30 +56,82 @@ </div> {/if} -<div class="container px-4 mt-20 max-w-xl mx-auto"> - <div class="text-center mb-8"> - <h1 class="text-3xl md:text-4xl font-semibold mb-2">{$t('payments.sendTo')}</h1> - <p class="text-lg text-secondary">{address}</p> - </div> - <Numpad bind:amount {currency} {submit} /> +<div class="container px-4 mt-20 max-w-xl mx-auto space-y-5 text-center"> + <h1 class="text-3xl md:text-4xl font-semibold mb-2">{$t('payments.send')}</h1> + + <div class="text-lg text-secondary break-all">{address}</div> + + {#if confirmed} + <div class="text-center"> + <h2 class="text-2xl md:text-3xl font-semibold"> + {f(toFiat(amount, $selectedRate), currency)} + </h2> + <h3 class="text-secondary md:text-lg mb-6 mt-1">⚡️{s(amount)}</h3> + </div> + + <div class="text-center"> + <h2 class="text-secondary mb-5 text-lg">with fee</h2> + + <div class="relative"> + <h2 class="text-xl font-semibold"> + {f(toFiat(fee + ourfee, $selectedRate), currency)} + </h2> + <h3 class="text-secondary mb-6 mt-1">⚡️{s(fee + ourfee)}</h3> + <button class="absolute right-1/4 top-0 text-secondary p-4" on:click={toggleAdjusting} + ><Icon icon="settings" style="opacity-30" /></button + > + </div> + </div> + {:else} + <Numpad bind:amount bind:fiat {currency} {submit} /> + {/if} <form method="POST" use:enhance on:submit={toggle}> <input name="ts" value={Date.now()} type="hidden" /> <input name="address" value={address} type="hidden" /> <input name="amount" value={amount} type="hidden" /> <input name="pin" value={$pin} type="hidden" /> + <input name="confirmed" value={confirmed} type="hidden" /> + <input name="feeRate" value={feeRate} type="hidden" /> + <input name="stale" value={stale} type="hidden" /> + <input name="subtract" value={subtract} type="hidden" /> + + {#if confirmed && adjusting} + <div class="relative mt-10 mb-20"> + <div class="absolute top-5 left-0 text-secondary">Slower (~40 blocks)</div> + <div class="absolute top-5 right-0 text-secondary">Faster (~1 block)</div> + <Slider bind:value={feeRate} {handle} {min} {max} /> + </div> + <div class="flex justify-center gap-2 mb-10"> + <div class="my-auto"> + <Toggle id="subtract" checked={subtract} bind:value={subtract} /> + </div> + <label for="subtract" class="text-secondary text-lg my-auto mb-2" + >Take fee from amount</label + > + </div> + {/if} + + <div class="flex justify-center gap-2"> + {#if !confirmed} + <button + type="button" + class="hover:opacity-80 bg-black text-white rounded-2xl py-3 px-4 mt-2 border font-bold" + on:click|preventDefault={setMax} + on:keydown={setMax}>Max ⚡️{s(balance)}</button + > + {/if} - <div class="flex w-full"> <button bind:this={submit} type="submit" - class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24" + class="opacity-100 hover:opacity-80 rounded-2xl border py-3 font-bold mt-2 bg-black text-white px-4 w-24" disabled={loading} > - {#if loading} - <Spinner /> - {:else} + {#if confirmed} {$t('payments.send')} + {:else} + {$t('payments.next')} {/if} </button> </div> |
