aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/Tip.svelte198
-rw-r--r--src/lib/invoice.js8
-rw-r--r--src/routes/[username]/receive/+page.svelte3
-rw-r--r--src/routes/invoice/[id]/+page.svelte44
-rw-r--r--src/routes/invoice/[id]/tip/+page.server.js5
-rw-r--r--src/routes/invoice/[id]/tip/+page.svelte261
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>