aboutsummaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2024-09-04 18:59:30 -0700
committerAdam Soltys <[email protected]>2024-09-17 09:18:32 -0700
commitc5ef18aec5de023732eb6b86f768f206462fea93 (patch)
tree8f8eb333344d430eed1b22b9f13e9c7228d06ac5 /src/components
parent55297eeaae86fee264f6fc05c95bfc703be1056f (diff)
downloadcoinos-ui-c5ef18aec5de023732eb6b86f768f206462fea93.tar.gz
coinos-ui-c5ef18aec5de023732eb6b86f768f206462fea93.zip
wip
Diffstat (limited to 'src/components')
-rw-r--r--src/components/About.svelte5
-rw-r--r--src/components/Account.svelte53
-rw-r--r--src/components/Balance.svelte6
-rw-r--r--src/components/Items.svelte209
4 files changed, 268 insertions, 5 deletions
diff --git a/src/components/About.svelte b/src/components/About.svelte
index 1e60db72..01ff4c7c 100644
--- a/src/components/About.svelte
+++ b/src/components/About.svelte
@@ -91,14 +91,15 @@
{@html $t("about.locations.description")}
</p>
<!-- <LocationsMap {locations} /> -->
+
<a href="/map">
<div class="relative">
<img src="/map.png" class="mx-auto" />
<button
- class="bg-black text-white border rounded-full px-6 py-2 font-bold hover:opacity-80 absolute left-1/2 top-1/2 text-2xl"
+ class="bg-black text-white border border-4 rounded-full px-6 py-2 font-bold hover:bg-secondary absolute left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 text-2xl"
>
- View map
+ {$t("about.locations.viewMap")}
</button>
</div>
</a>
diff --git a/src/components/Account.svelte b/src/components/Account.svelte
new file mode 100644
index 00000000..5286c4d3
--- /dev/null
+++ b/src/components/Account.svelte
@@ -0,0 +1,53 @@
+<script>
+ import Icon from "$comp/Icon.svelte";
+ import Balance from "$comp/Balance.svelte";
+ import { t } from "$lib/translations";
+
+ export let user, rate, account;
+ let { balance, setup, type } = account;
+</script>
+
+<div class="border rounded-xl shadow p-4 space-y-5">
+ <div class="flex justify-center lg:justify-start">
+ <Balance {balance} {user} {rate} />
+ <div class="ml-auto text-secondary">{type}</div>
+ </div>
+
+ <div class="flex flex-wrap gap-3 justify-center w-full text-xl">
+ {#if setup}
+ <a href={`/send`} class="contents grow">
+ <button
+ type="button"
+ class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex bg-primary grow"
+ >
+ <div class="mx-auto flex gap-2">
+ <Icon icon="send" style="w-8" />
+ <div class="my-auto">{$t("user.dashboard.send")}</div>
+ </div>
+ </button>
+ </a>
+
+ <a href={`/${user.username}/receive`} class="contents">
+ <button
+ class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex bg-primary grow"
+ >
+ <div class="mx-auto flex gap-2">
+ <Icon icon="receive" style="w-8" />
+ <div class="my-auto">{$t("user.dashboard.receive")}</div>
+ </div>
+ </button>
+ </a>
+ {:else}
+ <a href={`/${user.username}/account/setup`} class="contents">
+ <button
+ class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex bg-primary grow"
+ >
+ <div class="mx-auto flex gap-2">
+ <Icon icon="settings" style="w-8" />
+ <div class="my-auto">{$t("user.dashboard.setup")}</div>
+ </div>
+ </button>
+ </a>
+ {/if}
+ </div>
+</div>
diff --git a/src/components/Balance.svelte b/src/components/Balance.svelte
index 4d5ac6bd..51c56555 100644
--- a/src/components/Balance.svelte
+++ b/src/components/Balance.svelte
@@ -6,7 +6,7 @@
import { t } from "$lib/translations";
let show;
- export let user, rate;
+ export let user, rate, balance;
</script>
{#if show && user.haspin && $pin?.length !== 6}
@@ -27,12 +27,12 @@
{#if isNaN(rate)}
<div class="text-gray-200">&mdash;</div>
{:else}
- {f((user.balance * rate) / sats, user.currency)}
+ {f((balance * rate) / sats, user.currency)}
{/if}
</div>
<div class="text-secondary text-2xl">
- ⚡️{s(user.balance)}
+ ⚡️{s(balance)}
</div>
{/if}
</div>
diff --git a/src/components/Items.svelte b/src/components/Items.svelte
new file mode 100644
index 00000000..f449215c
--- /dev/null
+++ b/src/components/Items.svelte
@@ -0,0 +1,209 @@
+<script>
+ import { btc, f, sat, post } from "$lib/utils";
+ import { browser } from "$app/environment";
+ import { goto, invalidate } from "$app/navigation";
+ import { fade } from "svelte/transition";
+ import { cubicIn } from "svelte/easing";
+ import { flip } from "svelte/animate";
+ import {
+ dndzone,
+ SHADOW_ITEM_MARKER_PROPERTY_NAME,
+ SOURCES,
+ TRIGGERS,
+ } from "svelte-dnd-action";
+ import DeleteItem from "$comp/DeleteItem.svelte";
+ import Icon from "$comp/Icon.svelte";
+
+ export let subject, user, items, total, rate, currency;
+
+ let flipDurationMs = 200;
+ let dropTargetStyle = {};
+
+ let dragDisabled =
+ subject?.id !== user?.id || (browser && window.innerWidth < 768);
+
+ let deleting;
+ let del = (item) => {
+ deleting = item;
+ };
+
+ function handleConsider(e) {
+ const {
+ items: newItems,
+ info: { source, trigger },
+ } = e.detail;
+ items = newItems;
+ if (source === SOURCES.KEYBOARD && trigger === TRIGGERS.DRAG_STOPPED)
+ reset();
+ }
+
+ async function handleFinalize(e) {
+ const {
+ items: newItems,
+ info: { source },
+ } = e.detail;
+ items = newItems;
+ if (source === SOURCES.POINTER) reset();
+
+ if (subject?.id === user?.id) {
+ await post("/api/items/sort", { items });
+ invalidate("app:items");
+ }
+ }
+
+ function startDrag(e) {
+ e.preventDefault();
+ dragDisabled = false;
+ }
+
+ function handleKeyDown(e) {
+ if ((e.key === "Enter" || e.key === " ") && dragDisabled)
+ dragDisabled = false;
+ }
+
+ $: if (total > 0 && browser) {
+ let payButton = document.querySelector("#payButton");
+ if (payButton) payButton.addEventListener("click", checkout);
+ }
+
+ let checkout = async () => {
+ let invoice = {
+ type: "lightning",
+ amount: btc(total, rate),
+ currency,
+ items: items.filter((i) => i.quantity > 0),
+ prompt: subject.prompt,
+ memoPrompt: subject.memoPrompt,
+ rate,
+ };
+
+ let { id } = await post(`/${subject.username}/invoice`, {
+ invoice,
+ user: { username: subject.username },
+ });
+
+ if (invoice.memoPrompt) {
+ return goto(`/${subject.username}/invoice/${id}/memo`);
+ }
+ goto(`/${subject.username}/invoice/${id}`);
+ };
+
+ let reset = () =>
+ (dragDisabled =
+ user?.username !== subject?.username || window.innerWidth < 768);
+</script>
+
+<svelte:window on:resize={reset} />
+
+{#if deleting}
+ <DeleteItem bind:item={deleting} />
+{/if}
+
+{#if items.length}
+ <h2 class="text-2xl font-bold">Select items</h2>
+{/if}
+<div
+ class="grid sm:grid-cols-2 gap-4 pb-20"
+ use:dndzone={{ items, flipDurationMs, dropTargetStyle, dragDisabled }}
+ on:consider={handleConsider}
+ on:finalize={handleFinalize}
+>
+ {#each items as i (i.id)}
+ <button
+ type="button"
+ class="w-full text-center cursor-pointer hover:opacity-80 mb-auto"
+ on:click={() => i.quantity++}
+ >
+ <div class="relative w-full h-64 overflow-hidden rounded-xl">
+ {#if i[SHADOW_ITEM_MARKER_PROPERTY_NAME]}
+ <div
+ in:fade={{ duration: 200, easing: cubicIn }}
+ class="custom-shadow-item"
+ />
+ {:else if i.image}
+ <img
+ src={`/api/public/${i.image}.webp`}
+ alt={i.name}
+ class="mx-auto object-cover h-full w-full"
+ />
+ {:else}
+ <div
+ class="bg-gradient-to-r from-primary to-gradient mb-4 cursor-pointer hover:opacity-80 w-full h-full"
+ alt="Banner"
+ />
+ {/if}
+ {#if user?.username === subject.username}
+ <div class="flex gap-2 justify-center absolute top-2 right-2">
+ <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"
+ >
+ <Icon icon="edit" style="w-8 mx-auto invert" />
+ </button>
+ </a>
+ </button>
+ <button
+ class="bg-black rounded-full w-12 h-12 bg-opacity-40 hover:bg-opacity-100"
+ on:click|stopPropagation={() => del(i)}
+ >
+ <Icon icon="trash" style="w-8 mx-auto invert" />
+ </button>
+ <button
+ type="button"
+ on:click|stopPropagation={() => {}}
+ class="sm:hidden"
+ on:mousedown={startDrag}
+ on:touchstart={startDrag}
+ on:keydown={handleKeyDown}
+ >
+ <a href={`/${user.username}/items/${i.id}`}>
+ <button
+ class="bg-black rounded-full w-12 h-12 bg-opacity-40 hover:bg-opacity-100"
+ >
+ <Icon icon="move" style="w-8 mx-auto invert" />
+ </button>
+ </a>
+ </button>
+ </div>
+ {/if}
+ </div>
+ <div class="bg-white rounded-xl py-2">
+ <div class="flex text-left gap-2">
+ <div class="overflow-hidden space-y-1">
+ <div class="text-2xl break-words">{i.name}</div>
+ <div class="flex gap-2">
+ <div class="font-semibold text-lg">
+ {f(i.price, currency)}
+ </div>
+ <div class="text-secondary my-auto">
+ {sat(btc(i.price, rate))}
+ </div>
+ </div>
+ </div>
+ <div class="flex gap-1 ml-auto mb-auto justify-around tabular-nums">
+ {#if i.quantity}
+ <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}
+ </div>
+ {#if i.quantity}
+ <button
+ type="button"
+ on:click|stopPropagation={() => i.quantity++}
+ >
+ <Icon icon="plus" style="w-10 min-w-10" />
+ </button>
+ {/if}
+ </div>
+ </div>
+ </div>
+ </button>
+ {/each}
+</div>