diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/Tip.svelte | 198 | ||||
| -rw-r--r-- | src/lib/invoice.js | 8 | ||||
| -rw-r--r-- | src/routes/[username]/receive/+page.svelte | 3 | ||||
| -rw-r--r-- | src/routes/invoice/[id]/+page.svelte | 44 | ||||
| -rw-r--r-- | src/routes/invoice/[id]/tip/+page.server.js | 5 | ||||
| -rw-r--r-- | src/routes/invoice/[id]/tip/+page.svelte | 261 |
6 files changed, 385 insertions, 134 deletions
diff --git a/src/components/Tip.svelte b/src/components/Tip.svelte index 6af1e00a..c915da80 100644 --- a/src/components/Tip.svelte +++ b/src/components/Tip.svelte @@ -6,9 +6,9 @@ import { tick } from 'svelte'; import { goto } from '$app/navigation'; - export let amountFiat, showMobileTip, tipPercent, tipAmount, amount, currency, tip, username; + export let amountFiat, showMobileTip, tipPercent, tipAmount, amount, currency, tip, username, rate; - const tipAmounts = ['None', '10%', '15%', '20%']; + const tipAmounts = ['No thanks', '10%', '15%', '20%']; let showCustomAmount; let customInput; @@ -19,10 +19,12 @@ tipPercent = (customTipAmount / amountFiat) * 100; showMobileTip = false; showCustomAmount = false; - update(); } }; + let active = (amount) => + !customTipAmount && Math.round(tipPercent) === parseInt(amount.slice(0, 2)); + const handleCustomTipAmount = (e) => { tipPercent = 0; customTipAmount = e.target.value; @@ -40,116 +42,126 @@ timeout = setTimeout(update, 800); }; - const handleTipButtonClick = (amount) => { - customTipAmount = ''; - if (customInput) { - customInput.value = ''; - } - - if (amount === 'None') { + let submitting; + const handleTipButtonClick = async (amount) => { + submitting = true; + if (amount === 'No thanks') { tipPercent = 0; } else { tipPercent = parseInt(amount.slice(0, 2)); } - showMobileTip = false; - update(); + + await tick(); + submit.click(); }; let submit; let update = async () => { await tick(); - submit.click(); + //submit.click(); }; </script> -<div - class="w-[100%] md:w-auto absolute {showMobileTip - ? 'bottom-0' - : '-top-full'} bg-white p-6 md:p-0 rounded-t-3xl md:rounded-none left-0 md:static text-center space-y-5" -> - <form method="POST" use:enhance> - <input type="hidden" name="amount" value={amount} /> - <input type="hidden" name="tip" value={tip} /> - <input type="hidden" name="username" value={username} /> - <input type="hidden" name="prompt" value="true" /> +<form method="POST" use:enhance class:invisible={submitting}> + <input type="hidden" name="amount" value={amount} /> + <input type="hidden" name="tip" value={tip} /> + <input type="hidden" name="username" value={username} /> + <input type="hidden" name="rate" value={rate} /> + <input type="hidden" name="prompt" value="false" /> - <h1 class="hidden md:block text-4xl font-semibold mb-2">{$t('invoice.addTipq')}</h1> - {#if !showCustomAmount} - <div class="mb-2"> - <span class="font-semibold mr-1">{f(tipAmount, currency)}</span> - <span class="text-secondary font-semibold text-sm">{`(${s(tip)} SAT)`}</span> - <div class="text-secondary text-sm mb-2">{Math.round(tipPercent)}%</div> - </div> + <h1 class="text-4xl font-semibold my-8">{$t('invoice.addTipq')}</h1> + {#if !showCustomAmount} + <div class="my-2 text-xl">{Math.round(tipPercent)}%</div> - <input - id="slider" - type="range" - min="0" - max="100" - value={tipPercent} - class="px-0 py-0 mb-4" - style="--bgPercent: {tipPercent}%;" - on:input={handleSlide} - on:change={() => (showMobileTip = false)} - /> - {#each tipAmounts as amount} - <button - type="button" - class="bg-black text-white md:text-black md:bg-primary md:hover:bg-black md:hover:text-white w-16 md:w-20 py-3 m-1 rounded-2xl font-semibold {customTipAmount - ? '' - : Math.round(tipPercent) === parseInt(amount.slice(0, 2)) || - (!tipPercent && amount === 'None') - ? '!bg-black !text-white' - : ''}" - on:click={() => handleTipButtonClick(amount)}>{amount}</button - > - {/each} + <input + id="slider" + type="range" + min="0" + max="100" + value={tipPercent} + class="px-0 py-0 my-5" + style="--bgPercent: {tipPercent}%;" + on:input={handleSlide} + on:change={() => (showMobileTip = false)} + /> + <div class="flex mb-8"> <div> - <button - type="button" - class="w-full md:w-auto mt-2 bg-black md:bg-primary text-white md:text-black md:hover:bg-black md:hover:text-white font-semibold py-3 px-7 rounded-2xl" - on:click={() => (showCustomAmount = true)}>Custom amount</button + <span class="text-sm font-semibold mr-1"> + {f(amountFiat, currency)} + <span class="tect-secondary text-xl">+{f(tipAmount, currency)}</span></span > </div> - {/if} - <button type="submit" bind:this={submit} /> - </form> + <div class="ml-auto"> + <span class="text-secondary font-semibold text-sm">{s(amount)}</span> + <span class="text-secondary font-semibold text-lg">+{s(tip)} SAT</span> + </div> + </div> - {#if showCustomAmount} - <form on:submit|preventDefault={apply}> - <div class="relative"> - <span - class="absolute top-[18px] left-5 font-semibold {!customTipAmount - ? 'opacity-50' - : 'opacity-100'}">$</span + {#if tip} + <div> + <button + type="button" + class="w-full text-2xl mt-2 bg-primary text-black hover:bg-black hover:text-white font-semibold py-3 px-7 rounded-2xl" + on:click={() => submit.click()}>Next</button > - <input - bind:this={customInput} - type="number" - step="0.01" - on:input={(e) => handleCustomTipAmount(e)} - class="pl-10" - placeholder="Other" - /> <button - type="submit" - class="w-full mt-2 bg-black text-white font-semibold py-3 px-7 rounded-2xl {!customTipAmount - ? 'opacity-50' - : 'opacity-100 hover:opacity-80'}" - disabled={!customTipAmount}>Apply</button + type="button" + class="w-full text-2xl mt-2 bg-primary text-black hover:bg-black hover:text-white font-semibold py-3 px-7 rounded-2xl" + on:click={() => (tipPercent = 0)}>Reset</button > - <!-- found missing translation ---> </div> - </form> + {:else} + {#each tipAmounts as amount} + <button + type="button" + class:active={active(amount, tipPercent)} + class="w-full text-2xl text-black bg-primary hover:bg-black hover:text-white py-3 m-1 rounded-2xl font-semibold" + on:click={() => handleTipButtonClick(amount)}>{amount}</button + > + {/each} + {/if} + + <div> + <button + type="button" + class="w-full text-2xl mt-2 bg-primary text-black hover:bg-black hover:text-white font-semibold py-3 px-7 rounded-2xl" + on:click={() => (showCustomAmount = true)}>Custom</button + > + </div> {/if} -</div> -<div - class="hidden md:block h-[1px] lg:h-[600px] w-full lg:w-[1px] bg-lightgrey rounded-xl" - id="section-divider" -/> + <button type="submit" bind:this={submit} /> +</form> + +{#if showCustomAmount} + <form on:submit|preventDefault={apply}> + <div class="relative"> + <span + class="absolute top-[18px] left-5 font-semibold {!customTipAmount + ? 'opacity-50' + : 'opacity-100'}">$</span + > + <input + bind:this={customInput} + type="number" + step="0.01" + on:input={(e) => handleCustomTipAmount(e)} + class="pl-10" + placeholder="Custom amount" + autofocus + /> + <button + type="submit" + class="w-full mt-2 bg-black text-white font-semibold py-3 px-7 rounded-2xl {!customTipAmount + ? 'opacity-50' + : 'opacity-100 hover:opacity-80'}" + disabled={!customTipAmount}>Apply</button + > + </div> + </form> +{/if} <style> #slider { @@ -174,18 +186,22 @@ #slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; - width: 20px; - height: 20px; + width: 30px; + height: 30px; border-radius: 50%; background: black; cursor: pointer; } #slider::-moz-range-thumb { - width: 20px; - height: 20px; + width: 30px; + height: 30px; border-radius: 50%; background: black; cursor: pointer; } + + .active { + @apply !bg-black !text-white; + } </style> diff --git a/src/lib/invoice.js b/src/lib/invoice.js index 1212cbe3..e8a5f615 100644 --- a/src/lib/invoice.js +++ b/src/lib/invoice.js @@ -11,12 +11,16 @@ export default async ({ cookies, request }) => { tip: parseInt(form.get('tip')), network: 'lightning', prompt: form.get('prompt') === 'true', - rate: rates[form.get('currency')] + rate: parseFloat(form.get('rate')) || rates[form.get('currency')] }; let user = { username: form.get('username') }; let { uuid } = await post('/invoice', { invoice, user }, auth(cookies)); - throw redirect(307, `/invoice/${uuid}`); + if (invoice.prompt) { + throw redirect(307, `/invoice/${uuid}/tip`); + } else { + throw redirect(307, `/invoice/${uuid}`); + } }; diff --git a/src/routes/[username]/receive/+page.svelte b/src/routes/[username]/receive/+page.svelte index 7b0562d5..7425740a 100644 --- a/src/routes/[username]/receive/+page.svelte +++ b/src/routes/[username]/receive/+page.svelte @@ -24,8 +24,7 @@ : 'opacity-100 hover:opacity-80'}" disabled={!amount} > - <Icon icon="qr" style="mr-2" /> - {$t('user.receive.showQR')} + Next </button> </div> </form> diff --git a/src/routes/invoice/[id]/+page.svelte b/src/routes/invoice/[id]/+page.svelte index 84b03593..7dd6d51c 100644 --- a/src/routes/invoice/[id]/+page.svelte +++ b/src/routes/invoice/[id]/+page.svelte @@ -1,5 +1,5 @@ <script> - import { copy, f, get, reverseFormat, s, sats } from '$lib/utils'; + import { back, copy, f, get, reverseFormat, s, sats } from '$lib/utils'; import { browser } from '$app/environment'; import { invoices } from '$lib/store'; import { Icon, Heart, Image, Qr, Tip } from '$comp'; @@ -19,8 +19,6 @@ user: { username, currency } } = invoice; - let showMobileTip = false; - let qr; let tipPercent = 0; @@ -41,26 +39,20 @@ tipPercent = (tip / amount) * 100; }; - $: tip = Math.round((amount / 100) * tipPercent); - $invoices[id] = { amount, id, rate, received, text, tip, username }; $: browser && $invoices[id]?.received >= $invoices[id]?.amount && goto(($invoices[id].memo === 'launch' && '/launch/purchase') || `/invoice/${id}/paid`); $: amountFiat = parseFloat(((amount * rate) / sats).toFixed(2)); - $: tipAmount = ((tip * rate) / sats).toFixed(2); - - $: invoiceAmountFiatFormatted = f(amountFiat, currency); </script> -<div class:full-shadow={showMobileTip}> +<div> <button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80" class:invisible={!user} - disabled={showMobileTip} - on:click={() => goto(`/${username}/receive`)} + on:click={back} > <Icon icon="arrow-left" style="w-10" /> </button> @@ -69,40 +61,14 @@ <div class="block lg:flex justify-center items-center space-y-10 lg:space-y-0 space-x-0 lg:space-x-24" > - {#if invoice.prompt} - <Tip - bind:amountFiat - bind:showMobileTip - bind:tipPercent - bind:tipAmount - bind:amount - bind:tip - bind:currency - bind:username - /> - {/if} - - <!-- invoice section --> <div class="text-center space-y-5"> - <button - class="block md:hidden bg-black {showMobileTip - ? 'text-white/50' - : 'text-white'} rounded-xl px-6 py-3 block text-sm font-semibold mx-auto flex justify-center items-center" - on:click={() => (showMobileTip = true)} - disabled={showMobileTip} - > - <Heart /> - {$t('invoice.addTip')} - </button> - - <!-- found missing translation ---> <span class="text-secondary block text-2xl break-all" >Scan to pay <a href="/{username}" class="text-black font-semibold hover:opacity-80" >{username}</a ></span > - <Qr {text} image={'/images/invoice.svg'} disabled={showMobileTip} bind:qr /> + <Qr {text} image={'/images/invoice.svg'} bind:qr /> <div> <button @@ -117,7 +83,7 @@ <div class="flex justify-between"> <span class="font-semibold text-sm">{$t('invoice.invoice')}</span> <span class="font-semibold text-sm" - >{invoiceAmountFiatFormatted} + >{f(amountFiat, currency)} <span class="text-secondary font-normal">{`(${s(amount)} SAT)`}</span></span > </div> diff --git a/src/routes/invoice/[id]/tip/+page.server.js b/src/routes/invoice/[id]/tip/+page.server.js new file mode 100644 index 00000000..1068f273 --- /dev/null +++ b/src/routes/invoice/[id]/tip/+page.server.js @@ -0,0 +1,5 @@ +import invoice from '$lib/invoice'; + +export const actions = { + default: invoice +}; diff --git a/src/routes/invoice/[id]/tip/+page.svelte b/src/routes/invoice/[id]/tip/+page.svelte new file mode 100644 index 00000000..26eeba31 --- /dev/null +++ b/src/routes/invoice/[id]/tip/+page.svelte @@ -0,0 +1,261 @@ +<script> + import { browser } from '$app/environment'; + import { invoices } from '$lib/store'; + import { copy, f, get, reverseFormat, s, sats } from '$lib/utils'; + import { Icon, Tip } from '$comp'; + import { goto } from '$app/navigation'; + import { t } from '$lib/translations'; + import { enhance } from '$app/forms'; + import { tick } from 'svelte'; + + const tipAmounts = ['No thank you', '10%', '15%', '20%']; + let showCustomAmount; + + let customInput; + let customTipAmount; + + let apply = async () => { + if (customTipAmount > 0) { + tipPercent = (customTipAmount / amountFiat) * 100; + showCustomAmount = false; + } + }; + + let active = (amount) => + !customTipAmount && Math.round(tipPercent) === parseInt(amount.slice(0, 2)); + + const handleCustomTipAmount = (e) => { + tipPercent = 0; + customTipAmount = e.target.value; + }; + + let timeout; + const handleSlide = (e) => { + customTipAmount = ''; + if (customInput) { + customInput.value = ''; + } + tipPercent = e.target.value; + + clearTimeout(timeout); + timeout = setTimeout(update, 800); + }; + + let submitting; + const handleTipButtonClick = async (amount) => { + submitting = true; + if (amount === 'No thank you') { + tipPercent = 0; + } else { + tipPercent = parseInt(amount.slice(0, 2)); + } + + await tick(); + submit.click(); + }; + + let submit; + let update = async () => { + await tick(); + //submit.click(); + }; + + export let data; + $: refresh(data); + let { invoice, id, user } = data; + let { + amount, + rate, + received, + prompt, + text, + tip, + user: { username, currency } + } = invoice; + + let qr; + let tipPercent = 0; + + let fullscreen; + + let refresh = (data) => { + ({ invoice, id } = data); + ({ + amount, + rate, + received, + prompt, + text, + tip, + user: { username, currency } + } = invoice); + + tipPercent = (tip / amount) * 100; + }; + + $: tip = Math.round((amount / 100) * tipPercent); + + $invoices[id] = { amount, id, rate, received, text, tip, username }; + $: browser && + $invoices[id]?.received >= $invoices[id]?.amount && + goto(($invoices[id].memo === 'launch' && '/launch/purchase') || `/invoice/${id}/paid`); + + $: amountFiat = parseFloat(((amount * rate) / sats).toFixed(2)); + + $: tipAmount = ((tip * rate) / sats).toFixed(2); + + $: invoiceAmountFiatFormatted = f(amountFiat, currency); +</script> + +<button + class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80" + class:invisible={!user} + on:click={() => goto(`/${username}/receive`)} +> + <Icon icon="arrow-left" style="w-10" /> +</button> + +<div class="container px-4 max-w-lg text-center mx-auto"> + <form method="POST" use:enhance class:invisible={submitting}> + <input type="hidden" name="amount" value={amount} /> + <input type="hidden" name="tip" value={tip} /> + <input type="hidden" name="username" value={username} /> + <input type="hidden" name="rate" value={rate} /> + <input type="hidden" name="prompt" value="false" /> + + <h1 class="text-4xl font-semibold my-8">{$t('invoice.addTipq')}</h1> + {#if !showCustomAmount} + <div class="my-2 text-xl">{Math.round(tipPercent)}%</div> + + <input + id="slider" + type="range" + min="0" + max="100" + value={tipPercent} + class="px-0 py-0 my-5" + style="--bgPercent: {tipPercent}%;" + on:input={handleSlide} + /> + + <div class="flex mb-8"> + <div> + <span class="text-sm font-semibold mr-1"> + {f(amountFiat, currency)} + <span class="tect-secondary text-xl">+{f(tipAmount, currency)}</span></span + > + </div> + + <div class="ml-auto"> + <span class="text-secondary font-semibold text-sm">{s(amount)}</span> + <span class="text-secondary font-semibold text-lg">+{s(tip)} SAT</span> + </div> + </div> + + {#if tip} + <div> + <button + type="button" + class="w-full text-2xl mt-2 bg-primary text-black hover:bg-black hover:text-white font-semibold py-3 px-7 rounded-2xl" + on:click={() => submit.click()}>Next</button + > + <button + type="button" + class="w-full text-2xl mt-2 bg-primary text-black hover:bg-black hover:text-white font-semibold py-3 px-7 rounded-2xl" + on:click={() => (tipPercent = 0)}>Reset</button + > + </div> + {:else} + {#each tipAmounts as amount} + <button + type="button" + class:active={active(amount, tipPercent)} + class="w-full text-2xl text-black bg-primary hover:bg-black hover:text-white py-3 m-1 rounded-2xl font-semibold" + on:click={() => handleTipButtonClick(amount)}>{amount}</button + > + {/each} + {/if} + + <div> + <button + type="button" + class="w-full text-2xl mt-2 bg-primary text-black hover:bg-black hover:text-white font-semibold py-3 px-7 rounded-2xl" + on:click={() => (showCustomAmount = true)}>Custom</button + > + </div> + {/if} + + <button type="submit" bind:this={submit} /> + </form> + + {#if showCustomAmount} + <form on:submit|preventDefault={apply}> + <div class="relative"> + <span + class="absolute top-[18px] left-5 font-semibold {!customTipAmount + ? 'opacity-50' + : 'opacity-100'}">$</span + > + <input + bind:this={customInput} + type="number" + step="0.01" + on:input={(e) => handleCustomTipAmount(e)} + class="pl-10" + placeholder="Custom amount" + autofocus + /> + <button + type="submit" + class="w-full mt-2 bg-black text-white font-semibold py-3 px-7 rounded-2xl {!customTipAmount + ? 'opacity-50' + : 'opacity-100 hover:opacity-80'}" + disabled={!customTipAmount}>Apply</button + > + </div> + </form> + {/if} +</div> + +<style> + #slider { + -webkit-appearance: none; + width: 100%; + height: 6px; + border-radius: 5px; + background: linear-gradient( + to right, + black 0%, + black var(--bgPercent), + #f5f7fa var(--bgPercent), + #f5f7fa 100% + ); + outline: none; + } + + #slider:active { + background-color: black; + } + + #slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 30px; + height: 30px; + border-radius: 50%; + background: black; + cursor: pointer; + } + + #slider::-moz-range-thumb { + width: 30px; + height: 30px; + border-radius: 50%; + background: black; + cursor: pointer; + } + + .active { + @apply !bg-black !text-white; + } +</style> |
