aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsecondl1ght <[email protected]>2022-07-29 21:29:18 -0600
committersecondl1ght <[email protected]>2022-07-29 21:29:18 -0600
commita038ce9711f8ae5945eba073c82aa2aeb2bcf5c3 (patch)
treeab1177d57a100b663ecd28367e686e0f2a6891f7 /src
parent49e7185efd45add123e0dbfcab077772b5ff22f6 (diff)
downloadcoinos-ui-a038ce9711f8ae5945eba073c82aa2aeb2bcf5c3.tar.gz
coinos-ui-a038ce9711f8ae5945eba073c82aa2aeb2bcf5c3.zip
start hooking up invoice screen
Diffstat (limited to 'src')
-rw-r--r--src/components/RegisterOrLogin.svelte7
-rw-r--r--src/hooks/index.js18
-rw-r--r--src/lib/store.js4
-rw-r--r--src/routes/__layout.svelte8
-rw-r--r--src/routes/invoice.svelte253
-rw-r--r--src/routes/receive.svelte22
6 files changed, 273 insertions, 39 deletions
diff --git a/src/components/RegisterOrLogin.svelte b/src/components/RegisterOrLogin.svelte
index 2334741d..15be60cf 100644
--- a/src/components/RegisterOrLogin.svelte
+++ b/src/components/RegisterOrLogin.svelte
@@ -7,7 +7,8 @@
export let page;
- let username = "bob", password = "pw";
+ let username = 'bob',
+ password = 'pw';
let revealPassword = false;
@@ -20,7 +21,7 @@
let r = await post(url, { username, password });
if (!r.token) r = await post('/login', { username, password });
$token = r.token;
- console.log("new token", r.token)
+ console.log('new token', r.token);
auth();
goto('/receive');
};
@@ -43,7 +44,7 @@
required
class="bg-primary p-4 rounded-2xl w-full"
bind:value={username}
- autofocus
+ autofocus
/>
</div>
diff --git a/src/hooks/index.js b/src/hooks/index.js
index 21e56052..6a9c8ec5 100644
--- a/src/hooks/index.js
+++ b/src/hooks/index.js
@@ -1,16 +1,16 @@
-import cookie from "cookie";
+import cookie from 'cookie';
export async function handle({ event, resolve }) {
- let { request: { headers },
- } = event;
+ let {
+ request: { headers }
+ } = event;
- const cookies = cookie.parse(headers.get("cookie") || "");
- let { token } = cookies;
- event.locals = { token };
+ const cookies = cookie.parse(headers.get('cookie') || '');
+ let { token } = cookies;
+ event.locals = { token };
- return resolve(event);
+ return resolve(event);
}
-
export const getSession = ({ locals: { token } }) => {
- return { token };
+ return { token };
};
diff --git a/src/lib/store.js b/src/lib/store.js
index 62232f9d..6f4a9515 100644
--- a/src/lib/store.js
+++ b/src/lib/store.js
@@ -8,8 +8,7 @@ const persisted = (k, i) => {
: i
);
- s.subscribe((v) => browser && sessionStorage.setItem(k, JSON.stringify(v))
- );
+ s.subscribe((v) => browser && sessionStorage.setItem(k, JSON.stringify(v)));
return s;
};
@@ -17,4 +16,5 @@ const persisted = (k, i) => {
export const rate = writable();
export const user = writable();
export const invoiceAmount = writable();
+export const invoiceAmountFiat = writable();
export const token = persisted('token');
diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte
index 1af9679a..6562db18 100644
--- a/src/routes/__layout.svelte
+++ b/src/routes/__layout.svelte
@@ -2,11 +2,11 @@
import '../app.css';
import { onMount } from 'svelte';
import { connect } from '$lib/socket';
- import { token } from "$lib/store";
- import { session } from "$app/stores";
- import { browser } from "$app/env";
+ import { token } from '$lib/store';
+ import { session } from '$app/stores';
+ import { browser } from '$app/env';
- browser && ($token ||= $session.token);
+ browser && ($token ||= $session.token);
onMount(connect);
</script>
diff --git a/src/routes/invoice.svelte b/src/routes/invoice.svelte
index 567aefbb..6dce84fe 100644
--- a/src/routes/invoice.svelte
+++ b/src/routes/invoice.svelte
@@ -1,15 +1,248 @@
<script>
- import { rate, user, invoiceAmount } from '$lib/store';
+ import { rate, user, invoiceAmount, invoiceAmountFiat } from '$lib/store';
+ import { Icon } from '$comp';
+ import { goto } from '$app/navigation';
+ import { onMount } from 'svelte';
+ import QRCodeStyling from 'qr-code-styling';
export let text;
+
+ let tipPercent = 0;
+ const setRate = ($invoiceAmountFiat / $invoiceAmount) * 100000000;
+
+ $: tipAmount = customTipAmount
+ ? customTipAmount < 0
+ ? (customTipAmount = 0)
+ : customTipAmount
+ : ($invoiceAmountFiat / 100) * tipPercent;
+
+ $: tipAmountFormatted = new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency: 'USD'
+ }).format(tipAmount);
+
+ $: tipAmountSats = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(
+ tipAmount / (setRate / 100000000)
+ );
+
+ $: totalAmountSats = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(
+ $invoiceAmount + parseFloat(tipAmountSats.replace(/,/g, ''))
+ );
+
+ $: totalAmountFormatted = new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency: 'USD'
+ }).format($invoiceAmountFiat + tipAmount);
+
+ const invoiceAmountFormatted = new Intl.NumberFormat('en-US', {
+ maximumFractionDigits: 0
+ }).format($invoiceAmount);
+
+ const invoiceAmountFiatFormatted = new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency: 'USD'
+ }).format($invoiceAmountFiat);
+
+ let customTipAmount;
+
+ const tipAmounts = ['None', '10%', '15%', '20%'];
+
+ const handleTipButtonClick = (amount) => {
+ customTipAmount = '';
+ if (amount === 'None') {
+ tipPercent = 0;
+ } else {
+ tipPercent = amount.slice(0, 2);
+ }
+ };
+
+ const handleCustomTipAmount = (e) => {
+ tipPercent = 0;
+ customTipAmount = e.target.value;
+ };
+
+ onMount(() => {
+ const qrCode = new QRCodeStyling({
+ width: window.screen.width < 640 ? 250 : 300,
+ type: 'svg',
+ data: text,
+ image: '/images/invoice.svg',
+ dotsOptions: {
+ type: 'rounded'
+ },
+ imageOptions: {
+ hideBackgroundDots: false
+ }
+ });
+
+ qrCode.append(document.getElementById('qr'));
+ });
</script>
-{#if $user}
- {text}
- {$invoiceAmount}
- {$rate}
- {$user.username}
- {#each $user.payments as p}
- {p.amount}<br />
- {/each}
-{/if}
+<button class="ml-5 md:ml-20 mt-5 md:mt-10" on:click={() => goto('/receive')}>
+ <Icon icon="arrow-left" style="w-10" />
+</button>
+
+<div class="flex justify-center items-center mb-20 mt-[20px] px-3">
+ <div
+ class="block lg:flex justify-center items-center space-y-10 lg:space-y-0 space-x-0 lg:space-x-24"
+ >
+ {#if $invoiceAmount && $invoiceAmountFiat && $rate && text}
+ <!-- tipping section -->
+ <div class="text-center space-y-5">
+ <h1 class="text-4xl font-semibold">Add a tip?</h1>
+
+ <div>
+ <span class="font-semibold mr-1">{tipAmountFormatted}</span><span
+ class="text-secondary font-semibold text-sm">{`(${tipAmountSats} SATS)`}</span
+ >
+ <span class="block text-secondary text-sm">{tipPercent}%</span>
+ </div>
+
+ <input
+ id="slider"
+ type="range"
+ min="0"
+ max="100"
+ value={tipPercent}
+ class="px-2"
+ on:input={(e) => {
+ customTipAmount = '';
+ tipPercent = e.target.value;
+ }}
+ />
+
+ <div>
+ {#each tipAmounts as amount}
+ <button
+ class="bg-primary w-20 py-3 m-1 rounded-2xl font-semibold {tipPercent ===
+ amount.slice(0, 2) ||
+ ((tipPercent === 0 || tipPercent === '0') && amount === 'None')
+ ? '!bg-black text-white'
+ : ''}"
+ on:click={() => handleTipButtonClick(amount)}>{amount}</button
+ >
+ {/each}
+
+ <div class="relative">
+ <span
+ class="absolute top-[20px] left-5 font-semibold {!customTipAmount
+ ? 'opacity-50'
+ : 'opacity-100'}">$</span
+ >
+ <input
+ type="number"
+ min="0"
+ on:input={(e) => handleCustomTipAmount(e)}
+ class="bg-primary rounded-2xl py-3 px-10 font-semibold mt-2 mr-1 w-full md:w-auto"
+ placeholder="Other"
+ />
+ <button
+ class="w-full md:w-auto mt-2 md:mt-0 bg-black text-white font-semibold py-3 px-7 rounded-2xl {!customTipAmount
+ ? 'opacity-50'
+ : 'opacity-100'}"
+ disabled={!customTipAmount}>Done</button
+ >
+ </div>
+ </div>
+ </div>
+
+ <div
+ class="h-[1px] lg:h-[600px] w-full lg:w-[1px] bg-lightgrey rounded-xl"
+ id="section-divider"
+ />
+
+ <!-- invoice section -->
+ <div class="text-center space-y-5">
+ <span class="text-secondary"
+ >Scan to pay <span class="text-black font-semibold">insert name here</span></span
+ >
+
+ <div
+ id="qr"
+ class="border border-lightgrey rounded-3xl lg:p-5 flex justify-center items-center"
+ />
+
+ <div class="px-5 space-y-3">
+ <div class="flex justify-between">
+ <span class="font-semibold text-sm">Invoice</span>
+ <span class="font-semibold text-sm"
+ >{invoiceAmountFiatFormatted}
+ <span class="text-secondary font-normal">{`(${invoiceAmountFormatted} SATS)`}</span
+ ></span
+ >
+ </div>
+
+ <div class="flex justify-between">
+ <span class="font-semibold text-sm">Tip</span>
+ <span class="font-semibold text-sm"
+ >{tipAmountFormatted}
+ <span class="text-secondary font-normal">{`(${tipAmountSats} SATS)`}</span></span
+ >
+ </div>
+
+ <div class="flex justify-between">
+ <span class="font-bold">Total</span>
+ <span class="font-bold"
+ >{totalAmountFormatted}
+ <span class="text-secondary font-normal">{`(${totalAmountSats} SATS)`}</span></span
+ >
+ </div>
+ </div>
+ </div>
+ {:else}
+ <div
+ class="w-[94vw] md:w-[375px] h-[381px] md:h-[269px] animate-pulse bg-gray-400 rounded-xl"
+ />
+ <div
+ class="h-[1px] lg:h-[600px] w-full lg:w-[1px] bg-lightgrey rounded-xl"
+ id="section-divider"
+ />
+ <div
+ class="w-full md:w-[342px] h-[454px] md:h-[494px] animate-pulse bg-gray-400 rounded-xl"
+ />
+ {/if}
+ </div>
+</div>
+
+<style>
+ #slider {
+ -webkit-appearance: none;
+ width: 100%;
+ height: 6px;
+ border-radius: 5px;
+ background: #eaecef;
+ outline: none;
+ }
+
+ #slider:active {
+ background-color: black;
+ }
+
+ #slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 25px;
+ height: 25px;
+ border-radius: 50%;
+ background: black;
+ cursor: pointer;
+ }
+
+ #slider::-moz-range-thumb {
+ width: 25px;
+ height: 25px;
+ border-radius: 50%;
+ background: black;
+ cursor: pointer;
+ }
+
+ input[type='number']::-webkit-inner-spin-button,
+ input[type='number']::-webkit-outer-spin-button {
+ -webkit-appearance: none;
+ }
+
+ input[type='number'] {
+ -moz-appearance: textfield;
+ }
+</style>
diff --git a/src/routes/receive.svelte b/src/routes/receive.svelte
index 338bc06c..095a2ba3 100644
--- a/src/routes/receive.svelte
+++ b/src/routes/receive.svelte
@@ -2,7 +2,7 @@
// in the future we will want to allow users to set their fiat currency in the settings
import { AppHeader, Icon } from '$comp';
import { goto } from '$app/navigation';
- import { invoiceAmount, rate } from '$lib/store';
+ import { invoiceAmount, invoiceAmountFiat, rate } from '$lib/store';
let useFiat = true;
let amountFiat = 0;
@@ -10,6 +10,9 @@
let message = '';
$: $invoiceAmount = parseInt(amountSats === 0 ? amountFiat / ($rate / 100000000) : amountSats);
+ $: $invoiceAmountFiat = parseInt(
+ amountFiat === 0 ? amountSats * ($rate / 100000000) : amountFiat
+ );
$: amountSatsFormatted = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(
amountSats
);
@@ -63,6 +66,7 @@
amountSats = amountSats.slice(0, amountSats.length - 1);
if (amountSats.length === 0) {
amountSats = 0;
+ amountFiat = 0;
}
}
} else if (value !== '<' && parseInt(amountSats + value) > 100000000) {
@@ -77,8 +81,8 @@
<AppHeader />
<!-- step 1 input amount and generate QR code -->
-{#if $rate}
- <div class="flex justify-center items-center mt-20 mb-3 px-3">
+<div class="flex justify-center items-center mt-20 mb-3 px-3">
+ {#if $rate}
<div class="space-y-5">
<!-- amounts -->
<div class="text-center">
@@ -165,11 +169,7 @@
>
</div>
</div>
- </div>
-{:else}
- <div class="px-3">
- <div
- class="w-full md:w-[300px] h-[475px] md:h-[485px] animate-pulse bg-gray-400 mt-20 mb-3 mx-auto rounded-xl"
- />
- </div>
-{/if}
+ {:else}
+ <div class="w-full md:w-[300px] h-[475px] md:h-[485px] animate-pulse bg-gray-400 rounded-xl" />
+ {/if}
+</div>