diff options
| author | Adam Soltys <[email protected]> | 2024-05-17 09:21:45 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2024-05-17 09:21:45 -0700 |
| commit | 7c8cbe1c0b41d223e20823e2896230575a46bad9 (patch) | |
| tree | 14cad0877cb99551645e496f562143caa9f304ae /src | |
| parent | a8bb93d408f82e79dac8c98b6a11049674cbf3e7 (diff) | |
| download | coinos-ui-7c8cbe1c0b41d223e20823e2896230575a46bad9.tar.gz coinos-ui-7c8cbe1c0b41d223e20823e2896230575a46bad9.zip | |
updates from master
Diffstat (limited to 'src')
| -rw-r--r-- | src/routes/(app)/[username]/invoice/[id]/+layout.server.js | 3 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/invoice/[id]/+page.svelte | 141 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/invoice/[id]/memo/+page.server.js | 13 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/invoice/[id]/memo/+page.svelte | 95 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte | 9 | ||||
| -rw-r--r-- | src/routes/(app)/send/+layout.svelte | 4 | ||||
| -rw-r--r-- | src/routes/(app)/send/invoice/[id]/+page.server.js | 38 | ||||
| -rw-r--r-- | src/routes/(app)/send/invoice/[id]/+page.svelte | 110 | ||||
| -rw-r--r-- | src/service-worker.js | 5 |
9 files changed, 291 insertions, 127 deletions
diff --git a/src/routes/(app)/[username]/invoice/[id]/+layout.server.js b/src/routes/(app)/[username]/invoice/[id]/+layout.server.js index 6f9ae4a2..c9920052 100644 --- a/src/routes/(app)/[username]/invoice/[id]/+layout.server.js +++ b/src/routes/(app)/[username]/invoice/[id]/+layout.server.js @@ -13,7 +13,7 @@ export async function load({ depends, params, url, parent }) { if ( user && invoice.uid !== user.id && - !url.pathname.includes("tip") && + !(url.pathname.includes("tip") || url.pathname.includes("memo")) && !options ) { redirect(307, `/send/invoice/${id}`); @@ -34,5 +34,6 @@ export async function load({ depends, params, url, parent }) { } let src = Qr.drawImg(invoice.text || "", { size: 340 }); + return { id, invoice, src }; } diff --git a/src/routes/(app)/[username]/invoice/[id]/+page.svelte b/src/routes/(app)/[username]/invoice/[id]/+page.svelte index 1457025d..1a569039 100644 --- a/src/routes/(app)/[username]/invoice/[id]/+page.svelte +++ b/src/routes/(app)/[username]/invoice/[id]/+page.svelte @@ -1,7 +1,8 @@ <script> - import { enhance } from "$app/forms"; + import { enhance } from "$app/forms"; import { send } from "$lib/socket"; import { + btc, post, back, copy, @@ -15,7 +16,7 @@ } from "$lib/utils"; import { tick, onMount, onDestroy } from "svelte"; import { browser } from "$app/environment"; - import { last } from "$lib/store"; + import { last, showQr } from "$lib/store"; import Avatar from "$comp/Avatar.svelte"; import Icon from "$comp/Icon.svelte"; import Heart from "$comp/Heart.svelte"; @@ -26,6 +27,7 @@ export let data; let ndef; + let showOptions; let reading = async ({ message, serialNumber }) => { let name = serialNumber.replace(/:/g, "-"); @@ -41,16 +43,11 @@ hash, type, rate, - received, - prompt, text, tip, - request_id, user: { username, currency }, } = invoice; - let showQr = true; - let qr; let tipPercent = 0; @@ -61,8 +58,6 @@ hash, type, rate, - received, - prompt, text, tip, user: { username, currency }, @@ -120,12 +115,12 @@ user: { username, currency }, })); - goto(`./${id}`, { invalidateAll: true, noScroll: true }); + goto(`./${id}?options=true`, { invalidateAll: true, noScroll: true }); }; </script> -<div class="invoice container mx-auto max-w-xl px-4 space-y-5"> - {#if showQr} +<div class="invoice container mx-auto max-w-xl px-4 space-y-2"> + {#if $showQr} <div> <a href={[types.bitcoin, types.liquid].includes(invoice.type) @@ -142,6 +137,21 @@ </div> {/if} + {#each invoice.items as i} + <div class="grid grid-cols-12 text-xl"> + <div class="col-span-1 my-auto">{i.quantity}</div> + <div class="mr-auto grow col-span-7 my-auto"> + {i.name} + </div> + <div class="col-span-2 font-semibold text-right my-auto"> + {f(i.price * i.quantity, currency)} + </div> + <div class="col-span-2 text-secondary text-right text-lg my-auto"> + {sat(btc(i.price * i.quantity, rate))} + </div> + </div> + {/each} + {#if amount > 0} <div class="text-center font-bold text-2xl"> <div> @@ -154,7 +164,7 @@ {/if} </div> <div> - <span class="text-secondary font-normal text-lg" + <span class="text-secondary font-normal text-xl" >⚡️{`${s(amount)}`}</span > @@ -167,10 +177,57 @@ </div> {/if} - <div class="flex justify-center gap-2"> + <button + type="button" + class="flex gap-2 text-center break-all rounded-2xl hover:opacity-80 py-5 px-6 w-full mx-auto justify-center border text-xl whitespace-nowrap" + on:click={() => copy(txt)} + > + <Icon icon="copy" style="w-8 my-auto" /> + <div class="my-auto"> + {txt.substr(0, 11)}..{txt.substr(-10)} + </div> + </button> + + <div + class="w-full flex justify-center gap-2 flex-wrap text-secondary text-xl" + > + <a href={link} class="w-full"> + <button + class="w-full flex justify-center rounded-2xl border py-5 px-6 hover:opacity-80" + > + <Icon icon="mobile" style="mr-1 w-8" /> + <div class="my-auto">{$t("payments.openLink")}</div> + </button> + </a> + + <button + class="w-full flex justify-center rounded-2xl border py-5 px-6 hover:opacity-80" + on:click={() => ($showQr = !$showQr)} + > + <Icon icon="qr" style="w-8 mr-1 invert my-auto" /> + <div class="my-auto"> + {$showQr ? $t("payments.hide") : $t("payments.show")} + {$t("payments.qr")} + </div></button + > + + <!-- <button --> + <!-- class="w-full flex justify-center rounded-2xl border py-3 px-5 hover:opacity-80 text-lg" --> + <!-- on:click={() => (showOptions = !showOptions)} --> + <!-- > --> + <!-- <Icon icon="settings" style="w-8 mr-1 my-auto" /> --> + <!-- <div class="my-auto"> --> + <!-- {showOptions ? $t("payments.hide") : $t("payments.show")} --> + <!-- {$t("payments.options")} --> + <!-- </div></button --> + <!-- > --> + </div> + + <div class="flex justify-around text-secondary"> <button - class="hover:bg-neutral-700 my-auto flex gap-1 p-2" - class:bg-neutral-700={type === types.bitcoin} + class="hover:bg-primary my-auto flex gap-1 sm:gap-2 py-3 px-2 sm:px-5 w-full justify-center" + class:bg-primary={type === types.bitcoin} + class:text-black={type === types.bitcoin} on:click={() => setType(types.bitcoin)} > <img src="/images/bitcoin.svg" class="w-8" alt="Bitcoin" /> @@ -178,8 +235,9 @@ </button> <button - class="hover:bg-neutral-700 my-auto flex gap-1 p-2" - class:bg-neutral-700={type === types.liquid} + class="hover:bg-primary my-auto flex gap-1 sm:gap-2 py-3 px-2 sm:px-5 w-full justify-center" + class:bg-primary={type === types.liquid} + class:text-black={type === types.liquid} on:click={() => setType(types.liquid)} > <img src="/images/liquid.svg" class="w-8" alt="Liquid" /> @@ -187,11 +245,12 @@ </button> <button - class="hover:bg-neutral-700 flex gap-1 p-2" - class:bg-neutral-700={type === types.lightning} + class="hover:bg-primary flex gap-1 sm:gap-2 py-3 px-2 sm:px-5 w-full justify-center" + class:bg-primary={type === types.lightning} + class:text-black={type === types.lightning} on:click={() => setType(types.lightning)} > - <div class="bg-black rounded-full w-8 h-8 text-center flex"> + <div class="bg-white rounded-full w-8 h-8 text-center flex"> <div class="m-auto">⚡️</div> </div> <div class="my-auto text-lg">Lightning</div> @@ -200,45 +259,13 @@ {#if type === types.liquid} <div class="flex justify-center"> - <div class="my-auto text-xl text-center"> - We only support <span class="text-teal-500 font-bold">L-BTC</span>, - don't deposit any other assets + <div class="my-auto text-xl text-center text-secondary"> + {$t("payments.onlyLbtc")} + <span class="text-teal-500 font-bold">L-BTC</span>, + {$t("payments.dontDeposit")} </div> </div> {/if} - - <button - type="button" - class="flex gap-2 text-center break-all rounded-full text-white bg-black border hover:opacity-80 p-4 w-full md:w-80 mx-auto text-xl justify-center" - on:click={() => copy(txt)} - > - <Icon icon="copy" style="w-8 my-auto" /> - <div class="my-auto font-semibold"> - {txt.substr(0, 6)} ... {txt.substr(-10)} - </div> - </button> - - <div class="w-full flex justify-center gap-2 flex-wrap"> - <a href={link} class="w-full md:w-auto"> - <button - class="w-full md:w-auto flex justify-center rounded-full border py-3 px-5 hover:opacity-80 font-semibold" - > - <Icon icon="mobile" style="mr-1 w-6 invert" /> - <div class="text-secondary">{$t("payments.openLink")}</div> - </button> - </a> - - <button - class="w-full md:w-auto flex justify-center rounded-full border py-3 px-5 hover:opacity-80 font-semibold" - on:click={() => (showQr = !showQr)} - > - <Icon icon="qr" style="mr-1" /> - <div class="text-secondary"> - {showQr ? $t("payments.hide") : $t("payments.show")} - {$t("payments.qr")} - </div></button - > - </div> </div> <style> diff --git a/src/routes/(app)/[username]/invoice/[id]/memo/+page.server.js b/src/routes/(app)/[username]/invoice/[id]/memo/+page.server.js new file mode 100644 index 00000000..58d56018 --- /dev/null +++ b/src/routes/(app)/[username]/invoice/[id]/memo/+page.server.js @@ -0,0 +1,13 @@ +import invoice from "$lib/invoice"; +import { redirect } from "@sveltejs/kit"; + +export async function load({ parent, params }) { + let { invoice } = await parent(); + if (!invoice.amount) { + redirect(307, `/${params.username}/invoice/${invoice.id}`); + } +} + +export const actions = { + default: invoice, +}; diff --git a/src/routes/(app)/[username]/invoice/[id]/memo/+page.svelte b/src/routes/(app)/[username]/invoice/[id]/memo/+page.svelte new file mode 100644 index 00000000..4d23ff19 --- /dev/null +++ b/src/routes/(app)/[username]/invoice/[id]/memo/+page.svelte @@ -0,0 +1,95 @@ +<script> + import { onMount } from "svelte"; + import { browser } from "$app/environment"; + import { invoice as inv, request } from "$lib/store"; + import { + copy, + select, + f, + sat, + get, + reverseFormat, + s, + sats, + } from "$lib/utils"; + import Icon from "$comp/Icon.svelte"; + import { t } from "$lib/translations"; + import { enhance } from "$app/forms"; + + let submitting; + let submit; + + export let data; + $: refresh(data); + let { invoice, id, user, rates } = data; + let { + amount, + hash, + items, + memoPrompt, + type, + rate, + received, + prompt, + text, + tip, + user: { username, currency }, + } = invoice; + + invoice.memo = user?.username; + + let refresh = (data) => { + $inv = null; + + ({ invoice, id } = data); + ({ + amount, + hash, + items, + memoPrompt, + type, + received, + prompt, + text, + tip, + user: { username }, + } = invoice); + }; +</script> + +<form + method="POST" + use:enhance + class:invisible={submitting} + class="container px-4 max-w-lg mx-auto space-y-5" +> + <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={invoice.rate} /> + <input type="hidden" name="memoPrompt" value={invoice.memoPrompt} /> + <input type="hidden" name="prompt" value={invoice.prompt} /> + <input type="hidden" name="type" value={type} /> + <input type="hidden" name="hash" value={hash} /> + <input type="hidden" name="items" value={JSON.stringify(items)} /> + + <h1 class="text-4xl font-semibold text-center">{$t("invoice.addMemo")}</h1> + <div class="space-y-2"> + <input + use:select + name="memo" + class="w-full p-4 border rounded-xl text-xl" + bind:value={invoice.memo} + autocapitalize="none" + /> + <div class="text-secondary">{invoice.memoPrompt}</div> + </div> + + <button + type="submit" + bind:this={submit} + class="bg-black text-white border rounded-2xl px-6 py-5 w-full font-bold" + > + {$t("payments.next")} + </button> +</form> diff --git a/src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte b/src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte index f253936b..35b24bf1 100644 --- a/src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte +++ b/src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte @@ -61,6 +61,7 @@ let { amount, hash, + items, type, rate, received, @@ -82,6 +83,7 @@ ({ amount, hash, + items, type, received, prompt, @@ -116,8 +118,11 @@ <input type="hidden" name="prompt" value="false" /> <input type="hidden" name="type" value={type} /> <input type="hidden" name="hash" value={hash} /> + <input type="hidden" name="items" value={JSON.stringify(items)} /> - <input type="hidden" name="request_id" value={invoice.request?.id} /> + {#if invoice.memoPrompt} + <input type="hidden" name="memoPrompt" value={invoice.memoPrompt} /> + {/if} <h1 class="text-4xl font-semibold my-8">{$t("invoice.addTipq")}</h1> {#if !showCustomAmount} @@ -199,6 +204,6 @@ } .container button[type="button"] { - @apply w-full mt-2 bg-black text-white border hover:bg-white hover:text-black font-semibold py-5 px-7 rounded-2xl; + @apply w-full mt-2 bg-primary text-black hover:bg-black hover:text-white font-semibold py-5 px-7 rounded-2xl; } </style> diff --git a/src/routes/(app)/send/+layout.svelte b/src/routes/(app)/send/+layout.svelte index 0f412759..9e14fa39 100644 --- a/src/routes/(app)/send/+layout.svelte +++ b/src/routes/(app)/send/+layout.svelte @@ -9,8 +9,8 @@ let cancel = () => goto(`/${user.username}`); </script> -{#if user.haspin && (!$pin || $pin?.length !== 6)} +{#if user?.haspin && (!$pin || $pin?.length !== 6)} <Pin bind:value={$pin} {cancel} /> {/if} -<slot />
\ No newline at end of file +<slot /> diff --git a/src/routes/(app)/send/invoice/[id]/+page.server.js b/src/routes/(app)/send/invoice/[id]/+page.server.js index 88017362..933993a2 100644 --- a/src/routes/(app)/send/invoice/[id]/+page.server.js +++ b/src/routes/(app)/send/invoice/[id]/+page.server.js @@ -2,35 +2,22 @@ import { error, fail, redirect } from "@sveltejs/kit"; import { fd, auth, get, post } from "$lib/utils"; export async function load({ params: { id }, parent, url }) { - let { user } = await parent(); + let { rates, user } = await parent(); - let { - amount, - address, - currency, - hash, - rate, - prompt, - tip, - text, - user: recipient, - } = await get(`/invoice/${id}`); + let invoice = await get(`/invoice/${id}`); - if (prompt && tip === null) - redirect(307, `/${recipient.username}/invoice/${id}/tip`); - if (recipient.username === user.username) + if (invoice.prompt && invoice.tip === null) + redirect(307, `/${invoice.user.username}/invoice/${id}/tip`); + + if (invoice.memoPrompt && invoice.memo === null) + redirect(307, `/${invoice.user.username}/invoice/${id}/memo`); + + if (invoice.user.username === user?.username) error(500, { message: "Cannot send to self" }); - return { - amount, - address, - currency, - hash, - tip, - rate, - payreq: text, - recipient, - }; + if (!user) redirect(307, `/${invoice.user.username}/invoice/${id}`); + + return { invoice, rates, user }; } export const actions = { @@ -41,6 +28,7 @@ export const actions = { await post("/payments", body, auth(cookies)); } catch (e) { + console.log("payment failed", id, e); return fail(400, { message: e.message }); } diff --git a/src/routes/(app)/send/invoice/[id]/+page.svelte b/src/routes/(app)/send/invoice/[id]/+page.svelte index c2163a81..de08ac54 100644 --- a/src/routes/(app)/send/invoice/[id]/+page.svelte +++ b/src/routes/(app)/send/invoice/[id]/+page.svelte @@ -9,22 +9,34 @@ import Numpad from "$comp/Numpad.svelte"; import Spinner from "$comp/Spinner.svelte"; import { page } from "$app/stores"; - import { post, back, f, fiat as toFiat, s, sats, focus } from "$lib/utils"; + import { + btc, + sat, + post, + back, + f, + fiat as toFiat, + s, + sats, + focus, + } from "$lib/utils"; export let data; export let form; - let { address, hash, payreq, recipient, tip, rates, user } = data; + let { invoice, rates, user } = data; + let { address, hash, payreq, user: recipient, tip } = invoice; let { currency } = user; $: reload(data); let reload = (data) => { - ({ address, hash, payreq, recipient, tip, rates, user } = data); + ({ invoice, rates, user } = data); + ({ address, hash, payreq, user: recipient, tip } = invoice); ({ currency } = user); }; - $: rate = data.rate * (rates[user.currency] / rates[data.currency]); + $: rate = invoice.rate * (rates[user.currency] / rates[invoice.currency]); - let amount = form?.amount || data.amount; + let amount = form?.amount || invoice.amount; let a, af, amountFiat = amount * (rate / sats), @@ -35,13 +47,17 @@ amountFiat = af; }; - let loading; - let submit = () => (loading = true); + let submitting; + let submit = () => (submitting = true); let external = async () => { - let invoice = { type: "lightning", amount, currency }; let { id } = await post(`/${recipient.username}/invoice`, { - invoice, + invoice: { + ...invoice, + type: "lightning", + amount, + currency, + }, user: { username: recipient.username }, }); @@ -52,12 +68,12 @@ $: update(form); let update = () => { - if (form?.message.includes("pin")) $pin = undefined; - loading = false; + if (form?.message?.includes("pin")) $pin = undefined; + submitting = false; }; // onMount(async () => { - // if (browser && window.NDEFReader) { + // if (browser && window.NDEFReader && user.nfc) { // try { // let ndef = new NDEFReader(); // await ndef.scan(); @@ -86,12 +102,12 @@ </div> {/if} -<div class="container px-4 max-w-xl mx-auto"> +<div class="container px-4 max-w-xl mx-auto text-center space-y-5"> {#if amount} - <div class="text-center mb-8"> - <h1 class="text-xl md:text-2xl text-secondary mb-2"> - {$t("payments.send")} - </h1> + <h1 class="text-4xl font-bold"> + {$t("payments.send")} + </h1> + <div> <h2 class="text-2xl md:text-3xl font-semibold"> {f(toFiat(amount, rate), currency)} {#if tip} @@ -100,7 +116,7 @@ </span> {/if} </h2> - <h3 class="text-secondary md:text-lg mb-6 mt-1"> + <h3 class="text-secondary md:text-lg mt-1"> ⚡️{s(amount)} {#if tip} @@ -109,26 +125,39 @@ </span> {/if} </h3> + </div> - <h1 class="text-xl md:text-2xl text-secondary mb-2"> - {$t("payments.to")} - </h1> + <h1 class="text-xl md:text-2xl text-secondary"> + {$t("payments.to")} + </h1> - <div class="flex p-1 gap-2 justify-center"> - {#if recipient.username.includes("@classic")} - <img - src="/images/classic.png" - class="w-24 border-4 border-transparent mr-1" - alt="Bitcoin" - /> - {:else} - <Avatar user={recipient} size={"20"} /> - {/if} - <p class="text-4xl break-words my-auto"> - {recipient.username.replace("@classic", "")} - </p> - </div> + <div class="flex p-1 gap-2 justify-center"> + <Avatar user={recipient} size={"20"} /> + <p class="text-4xl break-words my-auto"> + {recipient.username} + </p> </div> + + {#if invoice.items.length} + <h1 class="text-xl md:text-2xl text-secondary"> + {$t("payments.for")} + </h1> + + {#each invoice.items as i} + <div class="grid grid-cols-4 text-xl"> + <div class="mr-auto grow col-span-2"> + <span class="mr-2">{i.quantity}</span> + {i.name} + </div> + <div class="font-semibold text-right"> + {f(i.price * i.quantity, invoice.currency)} + </div> + <div class="text-secondary text-right text-lg my-auto"> + {sat(btc(i.price * i.quantity, invoice.rate))} + </div> + </div> + {/each} + {/if} {:else} <Numpad bind:amount={a} @@ -146,14 +175,15 @@ <input name="username" value={recipient.username} type="hidden" /> <input name="pin" value={$pin} type="hidden" /> - <div class="flex w-full"> + <div class="flex w-full text-xl"> {#if amount} <button use:focus 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-5 font-bold mx-auto mt-2 bg-black text-white px-6 w-40" + disabled={submitting} > - {#if loading} + {#if submitting} <Spinner /> {:else} {$t("payments.send")} @@ -162,7 +192,7 @@ {:else} <button type="button" - 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-5 font-bold mx-auto mt-2 bg-black text-white px-6 w-40" on:click={setAmount} > {$t("payments.next")} @@ -170,7 +200,7 @@ {/if} </div> </form> - <div class="flex my-10"> + <div class="flex my-10 text-xl"> <button class="mx-auto" on:click={external} >{$t("payments.moreOptions")}</button > diff --git a/src/service-worker.js b/src/service-worker.js new file mode 100644 index 00000000..51cbd1f8 --- /dev/null +++ b/src/service-worker.js @@ -0,0 +1,5 @@ +import { build, files, version } from "$service-worker"; + +self.addEventListener("install", (event) => {}); +self.addEventListener("activate", (event) => {}); + self.addEventListener("fetch", (event) => {}); |
