aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lib/store.js41
-rw-r--r--src/lib/utils.js1
-rw-r--r--src/locales/en.json1
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.svelte96
-rw-r--r--src/routes/(app)/[username]/invoice/[id]/+page.svelte126
-rw-r--r--src/routes/(app)/[username]/items/create/+page.server.js3
-rw-r--r--static/icons/copy.svg4
-rw-r--r--static/icons/mobile.svg8
8 files changed, 173 insertions, 107 deletions
diff --git a/src/lib/store.js b/src/lib/store.js
index c27e7f6c..3bec45b9 100644
--- a/src/lib/store.js
+++ b/src/lib/store.js
@@ -1,7 +1,7 @@
import { get, writable } from "svelte/store";
import { browser } from "$app/environment";
-const persistSession = (key, defaultValue) => {
+let persistSession = (key, defaultValue) => {
let s = writable(
browser &&
sessionStorage.getItem(key) &&
@@ -17,7 +17,7 @@ const persistSession = (key, defaultValue) => {
return s;
};
-const persistLocal = (key, defaultValue) => {
+let persistLocal = (key, defaultValue) => {
let s = writable(
browser &&
localStorage.getItem(key) &&
@@ -38,21 +38,22 @@ const persistLocal = (key, defaultValue) => {
return s;
};
-export const avatar = writable();
-export const banner = writable();
-export const event = writable();
-export const events = writable({});
-export const invoice = writable();
-export const last = writable();
-export const loginRedirect = writable();
-export const decrypted = persistLocal("decrypted", {});
-export const newPayment = persistLocal("newPayment");
-export const password = writable();
-export const passwordPrompt = writable();
-export const pin = persistSession("pin");
-export const rate = writable();
-export const request = writable();
-export const requestRedirect = writable();
-export const token = persistSession("token");
-export const user = writable();
-export const ndef = writable();
+export let avatar = writable();
+export let banner = writable();
+export let event = writable();
+export let events = writable({});
+export let invoice = writable();
+export let last = writable();
+export let loginRedirect = writable();
+export let decrypted = persistLocal("decrypted", {});
+export let newPayment = persistLocal("newPayment");
+export let password = writable();
+export let passwordPrompt = writable();
+export let pin = persistSession("pin");
+export let rate = writable();
+export let request = writable();
+export let requestRedirect = writable();
+export let token = persistSession("token");
+export let user = writable();
+export let ndef = writable();
+ export let showQr = persistLocal("showQr");
diff --git a/src/lib/utils.js b/src/lib/utils.js
index d3ad0da3..9f9fc042 100644
--- a/src/lib/utils.js
+++ b/src/lib/utils.js
@@ -49,7 +49,6 @@ export let post = async (url, body, headers, timeout = 8000) => {
};
let controller = new AbortController();
- console.log(controller, timeout);
let id = setTimeout(() => {
controller.abort(new Error("Operation timed out"));
}, timeout);
diff --git a/src/locales/en.json b/src/locales/en.json
index 1ad0c931..0915d98c 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -285,6 +285,7 @@
}
},
"payments": {
+ "options": "options",
"bump": "Bump fee",
"networkFee": "Network fee",
"platformFee": "Platform fee",
diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte
index 95dd41a4..2a0f94bb 100644
--- a/src/routes/(app)/[username]/(profile)/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/+page.svelte
@@ -1,5 +1,5 @@
<script>
- import { invalidate } from "$app/navigation";
+ import { goto, invalidate } from "$app/navigation";
import { btc, f, sat, post } from "$lib/utils";
import { onMount } from "svelte";
import Icon from "$comp/Icon.svelte";
@@ -11,14 +11,18 @@
import { fade } from "svelte/transition";
import { cubicIn } from "svelte/easing";
import { flip } from "svelte/animate";
- const flipDurationMs = 200;
- const dropTargetStyle = {};
export let data;
+
+ let flipDurationMs = 200;
+ let dropTargetStyle = {};
+
let { subject, user, items, rates } = data;
+ $: ({ currency } = subject);
+ $: rate = rates[currency];
$: refresh(data);
let refresh = (d) => ({ items, subject, user, rates } = d);
- $: dragDisabled = subject?.id !== user?.id
+ $: dragDisabled = subject?.id !== user?.id;
let deleting;
$: total = items.reduce((a, b) => a + b.price * b.quantity, 0);
@@ -26,9 +30,7 @@
deleting = item;
};
- let consider = (e) => {
- items = e.detail.items;
- };
+ let consider = (e) => (items = e.detail.items);
let finalize = async (e) => {
items = e.detail.items;
@@ -37,6 +39,25 @@
invalidate("app:items");
}
};
+
+ let checkout = async () => {
+ let invoice = {
+ type: "lightning",
+ amount: btc(total, rate),
+ currency,
+ items: items.filter((i) => i.quantity > 0),
+ rate,
+ };
+
+ let { id } = await post(`/${subject.username}/invoice`, {
+ invoice,
+ user: { username: subject.username },
+ });
+
+ goto(`/${subject.username}/invoice/${id}?options=true`, {
+ invalidateAll: true,
+ });
+ };
</script>
{#if deleting}
@@ -46,7 +67,7 @@
<div class="space-y-5">
{#if user?.id === subject.id}
<div class="flex justify-center lg:justify-start">
- <Balance {user} rate={rates[user.currency]} />
+ <Balance {user} {rate} />
</div>
{#if !user.balance}
@@ -62,6 +83,7 @@
>
<a href={`/send`} class="w-full">
<button
+ type="button"
class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex w-full bg-primary"
>
<div class="mx-auto flex">
@@ -103,12 +125,14 @@
<div
class="grid grid-cols-1 md:grid-cols-2 gap-4"
+ class:pb-20={total > 0}
use:dndzone={{ items, flipDurationMs, dropTargetStyle, dragDisabled }}
on:consider={consider}
on:finalize={finalize}
>
{#each items as i (i.id)}
- <div
+ <button
+ type="button"
class="w-full text-center cursor-pointer hover:opacity-80"
on:click={() => i.quantity++}
>
@@ -132,7 +156,7 @@
{/if}
{#if user?.username === subject.username}
<div class="flex gap-2 justify-center absolute top-2 right-2">
- <div on:click|stopPropagation={() => {}}>
+ <button type="button" on:click|stopPropagation={() => {}}>
<a href={`/${user.username}/items/${i.id}`}>
<button
class="bg-black rounded-full w-12 h-12 bg-opacity-40 hover:bg-opacity-100"
@@ -140,7 +164,7 @@
<Icon icon="edit" style="w-8 mx-auto invert" />
</button>
</a>
- </div>
+ </button>
<button
class="bg-black rounded-full w-12 h-12 bg-opacity-40 hover:bg-opacity-100"
on:click|stopPropagation={() => del(i)}
@@ -156,20 +180,22 @@
<div class="text-2xl break-words">{i.name}</div>
<div class="flex gap-2">
<div class="font-semibold">
- {f(i.price, subject.currency)}
+ {f(i.price, currency)}
</div>
<div class="text-secondary">
- {sat(btc(i.price, rates[subject.currency]))}
+ {sat(btc(i.price, rate))}
</div>
</div>
</div>
<div class="flex gap-1 ml-auto mb-auto justify-around tabular-nums">
{#if i.quantity}
- <div
- on:click|stopPropagation={() => i.quantity > 0 && i.quantity--}
- >
- <Icon icon="minus" style="w-10 min-w-10" />
- </div>
+ <button
+ type="button"
+ on:click|stopPropagation={() =>
+ i.quantity > 0 && i.quantity--}
+ >
+ <Icon icon="minus" style="w-10 min-w-10" />
+ </button>
{/if}
<div class="text-2xl my-auto text-center">
{#if i.quantity}{i.quantity}{:else}&mdash;{/if}
@@ -177,30 +203,24 @@
</div>
</div>
</div>
- </div>
+ </button>
{/each}
</div>
{#if total > 0}
- <div>
- <a
- href={`/send/${subject.username}/${total}/${subject.currency}`}
- class="w-full"
+ <button
+ class="rounded-2xl py-5 px-6 font-bold hover:opacity-80 flex bg-black text-white fixed bottom-16 w-96 right-0"
+ on:click={checkout}
>
- <button
- class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex w-full bg-black text-white"
- >
- <div class="mx-auto flex">
- <div class="my-auto text-2xl">
- {$t("user.dashboard.checkout")}
- {f(total, subject.currency)}
- <span class="text-base">
- {sat(btc(total, rates[subject.currency]), subject.currency)}
- </span>
- </div>
- </div>
- </button>
- </a>
- </div>
+ <div class="mx-auto flex">
+ <div class="my-auto text-2xl">
+ {$t("user.dashboard.checkout")}
+ {f(total, currency)}
+ <span class="text-base">
+ {sat(btc(total, rate), currency)}
+ </span>
+ </div>
+ </div>
+ </button>
{/if}
</div>
diff --git a/src/routes/(app)/[username]/invoice/[id]/+page.svelte b/src/routes/(app)/[username]/invoice/[id]/+page.svelte
index cd67744f..3451cf58 100644
--- a/src/routes/(app)/[username]/invoice/[id]/+page.svelte
+++ b/src/routes/(app)/[username]/invoice/[id]/+page.svelte
@@ -2,6 +2,7 @@
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, "-");
@@ -49,7 +51,6 @@
user: { username, currency },
} = invoice;
- let showQr = true;
let qr;
let tipPercent = 0;
@@ -92,6 +93,7 @@
let subbed;
onMount(async () => {
+ $showQr = $showQr && 1
if (browser) {
last.subscribe((v) => {
if (!v || subbed) return;
@@ -125,7 +127,7 @@
</script>
<div class="invoice container mx-auto max-w-xl px-4 space-y-2">
- {#if showQr}
+ {#if $showQr === 1}
<div>
<a
href={[types.bitcoin, types.liquid].includes(invoice.type)
@@ -142,6 +144,20 @@
</div>
{/if}
+ {#each invoice.items as i}
+ <div class="flex tabular-nums">
+ <div class="mr-auto"><span class="mr-2">{i.quantity}</span> {i.name}</div>
+ <div class="flex gap-2">
+ <div class="font-semibold">
+ {f(i.price * i.quantity, currency)}
+ </div>
+ <div class="text-secondary">
+ {sat(btc(i.price * i.quantity, rate))}
+ </div>
+ </div>
+ </div>
+ {/each}
+
{#if amount > 0}
<div class="text-center font-bold text-2xl">
<div>
@@ -167,9 +183,55 @@
</div>
{/if}
- <div class="flex justify-center text-secondary">
+
+ <button
+ type="button"
+ class="flex gap-2 text-center break-all text-lg rounded-2xl hover:opacity-80 p-4 w-full mx-auto justify-center border bg-black text-white text-xl"
+ on:click={() => copy(txt)}
+ >
+ <Icon icon="copy" style="w-8 my-auto invert" />
+ <div class="my-auto font-bold">
+ {txt.substr(0, 11)}..{txt.substr(-11)}
+ </div>
+ </button>
+
+ <div class="w-full flex justify-center gap-2 flex-wrap text-secondary">
+ <a href={link} class="w-full">
+ <button
+ class="w-full flex justify-center rounded-2xl border py-3 px-5 hover:opacity-80 text-lg"
+ >
+ <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-3 px-5 hover:opacity-80 text-lg"
+ on:click={() => ($showQr = $showQr ? false : 2)}
+ >
+ <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-primary my-auto flex gap-1 sm:gap-2 py-3 px-2 sm:px-5 rounded-full"
+ 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)}
@@ -179,7 +241,7 @@
</button>
<button
- class="hover:bg-primary my-auto flex gap-1 sm:gap-2 py-3 px-2 sm:px-5 rounded-full"
+ 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)}
@@ -189,7 +251,7 @@
</button>
<button
- class="hover:bg-primary flex gap-1 sm:gap-2 py-3 px-2 sm:px-5 rounded-full"
+ 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)}
@@ -204,45 +266,29 @@
{#if type === types.liquid}
<div class="flex justify-center">
<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.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 text-lg rounded-full hover:opacity-80 p-4 w-full mx-auto justify-center border text-black text-xl"
- on:click={() => copy(txt)}
- >
- <Icon icon="copy" style="w-8 my-auto font-semibold" />
- <div class="my-auto">
- {txt.substr(0, 11)}..{txt.substr(-11)}
- </div>
- </button>
-
-
- <div class="w-full flex justify-center gap-2 flex-wrap text-secondary">
- <a href={link} class="w-full">
- <button
- class="w-full flex justify-center rounded-full border py-3 px-5 hover:opacity-80 text-lg"
+ {#if $showQr === 2}
+ <div>
+ <a
+ href={[types.bitcoin, types.liquid].includes(invoice.type)
+ ? invoice.text
+ : "lightning:" + invoice.text}
>
- <Icon icon="mobile" style="mr-1 w-6" />
- <div>{$t("payments.openLink")}</div>
- </button>
- </a>
-
- <button
- class="w-full flex justify-center rounded-full border py-3 px-5 hover:opacity-80 text-lg"
- on:click={() => (showQr = !showQr)}
- >
- <Icon icon="qr" style="mr-1 invert" />
- <div>
- {showQr ? $t("payments.hide") : $t("payments.show")}
- {$t("payments.qr")}
- </div></button
- >
- </div>
+ <img
+ {src}
+ class="mx-auto z-10 shadow-inner rounded-2xl"
+ bind:this={qr}
+ alt={txt}
+ />
+ </a>
+ </div>
+ {/if}
</div>
<style>
diff --git a/src/routes/(app)/[username]/items/create/+page.server.js b/src/routes/(app)/[username]/items/create/+page.server.js
index 6740f5f9..8cb48399 100644
--- a/src/routes/(app)/[username]/items/create/+page.server.js
+++ b/src/routes/(app)/[username]/items/create/+page.server.js
@@ -6,10 +6,7 @@ export const actions = {
let form = await fd(request);
try {
await post("/items", form, auth(cookies));
-
- console.log("YEAH");
} catch (e) {
- console.log("E", e)
return fail(400, { message: e.message });
}
diff --git a/static/icons/copy.svg b/static/icons/copy.svg
index 90375679..6d67a021 100644
--- a/static/icons/copy.svg
+++ b/static/icons/copy.svg
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M16 12.9V17.1C16 20.6 14.6 22 11.1 22H6.9C3.4 22 2 20.6 2 17.1V12.9C2 9.4 3.4 8 6.9 8H11.1C14.6 8 16 9.4 16 12.9Z" stroke="#292D32" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path opacity="0.4" d="M22 6.9V11.1C22 14.6 20.6 16 17.1 16H16V12.9C16 9.4 14.6 8 11.1 8H8V6.9C8 3.4 9.4 2 12.9 2H17.1C20.6 2 22 3.4 22 6.9Z" stroke="#292D32" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M21 8C21 6.34315 19.6569 5 18 5H10C8.34315 5 7 6.34315 7 8V20C7 21.6569 8.34315 23 10 23H18C19.6569 23 21 21.6569 21 20V8ZM19 8C19 7.44772 18.5523 7 18 7H10C9.44772 7 9 7.44772 9 8V20C9 20.5523 9.44772 21 10 21H18C18.5523 21 19 20.5523 19 20V8Z" fill="#0F0F0F"/>
+<path d="M6 3H16C16.5523 3 17 2.55228 17 2C17 1.44772 16.5523 1 16 1H6C4.34315 1 3 2.34315 3 4V18C3 18.5523 3.44772 19 4 19C4.55228 19 5 18.5523 5 18V4C5 3.44772 5.44772 3 6 3Z" fill="#0F0F0F"/>
</svg> \ No newline at end of file
diff --git a/static/icons/mobile.svg b/static/icons/mobile.svg
index e23e8233..71c82e4c 100644
--- a/static/icons/mobile.svg
+++ b/static/icons/mobile.svg
@@ -1,3 +1,5 @@
-<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M12 18H12.01M8 21H16C17.1046 21 18 20.1046 18 19V5C18 3.89543 17.1046 3 16 3H8C6.89543 3 6 3.89543 6 5V19C6 20.1046 6.89543 21 8 21Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
-</svg>
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12 18C11.4477 18 11 18.4477 11 19C11 19.5523 11.4477 20 12 20C12.5523 20 13 19.5523 13 19C13 18.4477 12.5523 18 12 18Z" fill="#0F0F0F"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19 4C19 2.34315 17.6569 1 16 1H8C6.34315 1 5 2.34315 5 4V20C5 21.6569 6.34315 23 8 23H16C17.6569 23 19 21.6569 19 20V4ZM17 4C17 3.44772 16.5523 3 16 3H8C7.44772 3 7 3.44772 7 4V15H17V4ZM7 20V17H17V20C17 20.5523 16.5523 21 16 21H8C7.44772 21 7 20.5523 7 20Z" fill="#0F0F0F"/>
+</svg> \ No newline at end of file