aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/components/AppHeader.svelte4
-rw-r--r--src/components/Avatar.svelte4
-rw-r--r--src/components/ItemForm.svelte108
-rw-r--r--src/lib/nostr.js2
-rw-r--r--src/lib/store.js1
-rw-r--r--src/lib/utils.js1
-rw-r--r--src/locales/en.json9
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.svelte176
-rw-r--r--src/routes/(app)/[username]/items/[id]/+page.server.js6
-rw-r--r--src/routes/(app)/[username]/items/[id]/+page.svelte7
-rw-r--r--src/routes/(app)/[username]/items/create/+page.svelte14
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/_account.svelte25
-rw-r--r--src/routes/(app)/payment/[id]/bump/+server.js2
-rw-r--r--src/routes/(app)/send/user/[username]/[...amount]/+page.server.js7
-rw-r--r--static/icons/edit.svg7
-rw-r--r--static/icons/trash.svg4
16 files changed, 312 insertions, 65 deletions
diff --git a/src/components/AppHeader.svelte b/src/components/AppHeader.svelte
index a2103cf5..72f10d26 100644
--- a/src/components/AppHeader.svelte
+++ b/src/components/AppHeader.svelte
@@ -1,6 +1,6 @@
<script>
import OutClick from "svelte-outclick";
- import { banner, newPayment, colorTheme } from "$lib/store";
+ import { banner, newPayment } from "$lib/store";
import { goto } from "$app/navigation";
import Avatar from "$comp/Avatar.svelte";
import Icon from "$comp/Icon.svelte";
@@ -42,7 +42,7 @@
{#if !$loading}
<header
- class="bg-gradient-to-r {$colorTheme} h-[175px] w-full relative bg-cover mb-20"
+ class="bg-gradient-to-r from-primary to-gradient h-[175px] w-full relative bg-cover mb-20"
style:background-image={bg}
>
<nav class="flex justify-end items-center space-x-4 p-5">
diff --git a/src/components/Avatar.svelte b/src/components/Avatar.svelte
index f13404bd..227200d7 100644
--- a/src/components/Avatar.svelte
+++ b/src/components/Avatar.svelte
@@ -1,5 +1,5 @@
<script>
- import { avatar, banner, colorTheme } from "$lib/store";
+ import { avatar, banner } from "$lib/store";
import Icon from "$comp/Icon.svelte";
import { punk } from "$lib/utils";
@@ -20,7 +20,7 @@
<a href={link} class:pointer-events-none={disabled}>
<div
- class="w-{s} h-{s} rounded-full border-4 border-white overflow-hidden bg-gradient-to-r {$colorTheme} flex justify-center items-center"
+ class="w-{s} h-{s} rounded-full border-4 border-white overflow-hidden bg-gradient-to-r from-primary to-gradient flex justify-center items-center"
>
<img
{src}
diff --git a/src/components/ItemForm.svelte b/src/components/ItemForm.svelte
new file mode 100644
index 00000000..044020b7
--- /dev/null
+++ b/src/components/ItemForm.svelte
@@ -0,0 +1,108 @@
+<script>
+ import { t } from "$lib/translations";
+ import { upload } from "$lib/upload";
+ export let item, token;
+
+ let fileInput, formElement, file;
+ let select = () => fileInput.click();
+ let src;
+
+ let tooLarge = {};
+ let handleFile = async ({ target }, type) => {
+ tooLarge[type] = false;
+ file = target.files[0];
+ if (!file) return;
+
+ if (file.size > 10000000) return (tooLarge[type] = true);
+
+ var reader = new FileReader();
+ reader.onload = async (e) => {
+ src = e.target.result;
+ };
+
+ reader.readAsDataURL(file);
+ };
+
+ async function handleSubmit() {
+ try {
+ submitting = true;
+ let data = new FormData(formElement);
+
+ if (src) {
+ try {
+ let { hash } = JSON.parse(
+ await upload(file, "item", progress, token)
+ );
+
+ data.set("image", hash);
+
+ await fetch(`/api/public/${hash}.webp`, {
+ cache: "reload",
+ mode: "no-cors",
+ });
+ } catch (e) {
+ console.log("problem upsubmitting avatar", e);
+ }
+ }
+
+ const response = await fetch(formElement.action, {
+ method: "POST",
+ body: data,
+ });
+
+ const result = deserialize(await response.text());
+
+ applyAction(result);
+ } catch (e) {
+ console.log(e);
+ fail("Something went wrong");
+ }
+
+ submitting = false;
+ }
+</script>
+
+<form
+ method="POST"
+ class="space-y-5"
+ on:submit|preventDefault={handleSubmit}
+ bind:this={formElement}
+>
+ <div>
+ <label for="name" class="font-bold mb-1 block">{$t("items.name")}</label>
+ <input name="name" bind:value={item.name} />
+ </div>
+ <div>
+ <label for="img" class="font-bold mb-1 block">{$t("items.image")}</label>
+ <div class="relative w-full h-64">
+ {#if item.hash}
+ <img
+ src={`/api/public/${item.hash}.webp`}
+ alt={item.name}
+ class="mx-auto object-cover h-full w-full"
+ />
+ {:else if src}
+ <img {src} alt={item.name} class="mx-auto object-cover h-full w-full" />
+ {:else}
+ <div
+ class="bg-gradient-to-r from-primary to-gradient w-full h-48 mb-4 cursor-pointer hover:opacity-80"
+ on:click={select}
+ on:keydown={select}
+ alt="Banner"
+ />
+ {/if}
+ <input
+ type="file"
+ class="hidden"
+ bind:this={fileInput}
+ on:change={(e) => handleFile(e, "item")}
+ />
+ </div>
+ </div>
+
+ <button
+ class="rounded-full py-5 px-6 font-bold hover:opacity-80 flex bg-black text-white text-2xl"
+ >
+ {$t("items.submit")}
+ </button>
+</form>
diff --git a/src/lib/nostr.js b/src/lib/nostr.js
index 5de08199..5209418a 100644
--- a/src/lib/nostr.js
+++ b/src/lib/nostr.js
@@ -90,7 +90,7 @@ export let getPrivateKey = async (user) => {
if (browser) {
k = localStorage.getItem("nsec");
if (k) {
- return nip19.decode(k).data;
+ return nip19.decode(k).data;
}
}
diff --git a/src/lib/store.js b/src/lib/store.js
index ee60058c..c27e7f6c 100644
--- a/src/lib/store.js
+++ b/src/lib/store.js
@@ -40,7 +40,6 @@ const persistLocal = (key, defaultValue) => {
export const avatar = writable();
export const banner = writable();
-export const colorTheme = writable("from-primary to-gradient");
export const event = writable();
export const events = writable({});
export const invoice = writable();
diff --git a/src/lib/utils.js b/src/lib/utils.js
index d1096cd4..53437f56 100644
--- a/src/lib/utils.js
+++ b/src/lib/utils.js
@@ -172,6 +172,7 @@ export let auth = (cookies) => ({
authorization: `Bearer ${cookies.get("token")}`,
});
+export let btc = (fiat, rate) => Math.round((fiat * sats) / rate);
export let fiat = (amount, rate) => (amount * rate) / sats;
export let fd = async (req) => {
let obj = Object.fromEntries(await req.formData());
diff --git a/src/locales/en.json b/src/locales/en.json
index 4fad56d6..24433202 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -178,6 +178,7 @@
"this": "This",
"lightningAddress": "Lightning address",
"dashboard": {
+ "checkout": "Checkout",
"go": "Go",
"dashboard": "Dashboard",
"accountBalance": "Account Balance",
@@ -349,5 +350,11 @@
"print": "Print",
"onlyLbtc": "We only support",
"dontDeposit": "don't deposit other assets yet"
- }
+ },
+ "items": {
+ "add": "Add item",
+ "name": "Name",
+ "submit": "Submit",
+ "image": "Image"
+ }
}
diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte
index 420cc5fa..bb98ddfe 100644
--- a/src/routes/(app)/[username]/(profile)/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/+page.svelte
@@ -1,4 +1,5 @@
<script>
+ import { btc, f } from "$lib/utils";
import { onMount } from "svelte";
import Icon from "$comp/Icon.svelte";
import Balance from "$comp/Balance.svelte";
@@ -8,46 +9,161 @@
let { subject, user, rates } = data;
$: refresh(data);
let refresh = (d) => ({ subject, user, rates } = d);
+
+ let items = [
+ {
+ id: 1,
+ name: "Full Pour",
+ price: 9,
+ quantity: 0,
+ hash: "fa3a9d22eb24b33ae2dee8d7566c1ce25d1c76e18b46d54437621d3e34a3fbc9",
+ },
+ {
+ id: 2,
+ name: "Half Pour",
+ price: 6,
+ quantity: 0,
+ hash: "a79c7b921a0dde39076201ae5f24505d5faa5f12f0cfd3b9d86cbc9c77ab0bd1",
+ },
+ {
+ id: 3,
+ name: "Taster",
+ price: 3,
+ quantity: 0,
+ hash: "9d8fe68d068f7271cf08d4cb9b033966984549c1bcd827f7516e66e5b537f502",
+ },
+ {
+ id: 4,
+ name: "4-packs",
+ price: 18,
+ quantity: 0,
+ hash: "13cf773ae51607e670c2a7c640aa5a1dc8b288554026de08370ed3da44e59493",
+ },
+ ];
+
+ $: total = items.reduce((a, b) => a + b.price * b.quantity, 0);
+ let del = () => {};
</script>
<div class="space-y-5">
{#if user?.id === subject.id}
- <div class="space-y-5">
- <div class="flex justify-center lg:justify-start">
- <Balance {user} rate={rates[user.currency]} />
+ <div class="flex justify-center lg:justify-start">
+ <Balance {user} rate={rates[user.currency]} />
+ </div>
+
+ {#if !user.balance}
+ <div class="mb-8">
+ <p class="text-secondary text-lg">
+ {$t("user.welcome")}
+ </p>
</div>
+ {/if}
- {#if !user.balance}
- <div class="mb-8">
- <p class="text-secondary text-lg">
- {$t("user.welcome")}
- </p>
- </div>
- {/if}
+ <div
+ class="flex flex-wrap sm:flex-nowrap gap-3 justify-center w-full text-xl"
+ >
+ <a href={`/send`} class="w-full">
+ <button
+ class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex w-full bg-primary"
+ >
+ <div class="mx-auto flex">
+ <Icon icon="send" style="my-auto h-6 mr-2" />
+ <div class="my-auto">{$t("user.dashboard.send")}</div>
+ </div>
+ </button>
+ </a>
- <div class="flex flex-wrap sm:flex-nowrap gap-3 justify-center w-full text-xl">
- <a href={`/send`} class="w-full">
- <button
- class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex w-full bg-primary"
- >
- <div class="mx-auto flex">
- <Icon icon="send" style="my-auto h-6 mr-2" />
- <div class="my-auto">{$t("user.dashboard.send")}</div>
- </div>
- </button>
- </a>
+ <a href={`/${user.username}/receive`} class="w-full">
+ <button
+ class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex w-full bg-primary"
+ >
+ <div class="mx-auto flex">
+ <Icon icon="numpad" style="my-auto h-6 mr-2" />
+ <div class="my-auto">{$t("user.dashboard.receive")}</div>
+ </div>
+ </button>
+ </a>
+ </div>
+ {/if}
- <a href={`/${user.username}/receive`} class="w-full">
- <button
- class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex w-full bg-primary"
- >
- <div class="mx-auto flex">
- <Icon icon="numpad" style="my-auto h-6 mr-2" />
- <div class="my-auto">{$t("user.dashboard.receive")}</div>
+ <div class="grid grid-cols-2 gap-4">
+ {#each items as i}
+ <div
+ class="w-full text-center cursor-pointer hover:opacity-80"
+ on:click={() => i.quantity++}
+ >
+ <div class="relative w-full h-64 overflow-hidden rounded-xl">
+ <img
+ src={`/api/public/${i.hash}.webp`}
+ alt={i.name}
+ class="mx-auto object-cover h-full w-full"
+ />
+ {#if user?.username === subject.username}
+ <div class="flex gap-2 justify-center absolute top-2 right-2">
+ <div
+ on:click|stopPropagation={() => i.quantity > 0 && i.quantity--}
+ >
+ <a href={`/${user.username}/items/${i.id}`}>
+ <button class="bg-white rounded-xl">
+ <Icon icon="edit" style="w-10" />
+ </button>
+ </a>
+ </div>
+ <button class="bg-white rounded-xl">
+ <Icon icon="trash" style="w-10" on:click={() => del(i)} />
+ </button>
</div>
- </button>
- </a>
+ {/if}
+ </div>
+ <div class="bg-white rounded-xl p-2">
+ <div class="text-2xl">
+ {i.name} &mdash; {f(i.price, subject.currency)}
+ </div>
+ <div class="flex gap-2 justify-center">
+ <div
+ on:click|stopPropagation={() => i.quantity > 0 && i.quantity--}
+ >
+ <Icon icon="minus" style="w-10" />
+ </div>
+ <div class="text-2xl my-auto">{i.quantity}</div>
+ <Icon icon="plus" style="w-10" />
+ </div>
+ </div>
</div>
+ {/each}
+ </div>
+ {#if total > 0}
+ <div>
+ <a
+ href={`/send/${subject.username}/${total}/${subject.currency}`}
+ class="w-full"
+ >
+ <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">
+ {f(total, subject.currency)}
+ {$t("user.dashboard.checkout")}
+ </div>
+ </div>
+ </button>
+ </a>
+ </div>
+ {/if}
+ {#if user?.username === subject.username}
+ <div>
+ <a href={`/${user.username}/items/create`} class="w-full">
+ <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("items.add")}
+ </div>
+ </div>
+ </button>
+ </a>
</div>
{/if}
</div>
diff --git a/src/routes/(app)/[username]/items/[id]/+page.server.js b/src/routes/(app)/[username]/items/[id]/+page.server.js
new file mode 100644
index 00000000..e1fe2efd
--- /dev/null
+++ b/src/routes/(app)/[username]/items/[id]/+page.server.js
@@ -0,0 +1,6 @@
+import { get } from "$lib/utils";
+export let load = async ({ cookies, params: { id } }) => {
+ let item = await get(`/item/${id}`);
+ console.log("item", item)
+ return { item };
+};
diff --git a/src/routes/(app)/[username]/items/[id]/+page.svelte b/src/routes/(app)/[username]/items/[id]/+page.svelte
new file mode 100644
index 00000000..5a19e892
--- /dev/null
+++ b/src/routes/(app)/[username]/items/[id]/+page.svelte
@@ -0,0 +1,7 @@
+<script>
+ import ItemForm from "$comp/ItemForm.svelte";
+ export let data;
+ let { item } = data;
+</script>
+
+<ItemForm {item} />
diff --git a/src/routes/(app)/[username]/items/create/+page.svelte b/src/routes/(app)/[username]/items/create/+page.svelte
new file mode 100644
index 00000000..c9aeae1d
--- /dev/null
+++ b/src/routes/(app)/[username]/items/create/+page.svelte
@@ -0,0 +1,14 @@
+<script>
+ import { t } from "$lib/translations";
+ import ItemForm from "$comp/ItemForm.svelte";
+ export let data;
+ let { token } = data;
+ let item = {};
+</script>
+
+<div class="container max-w-xl mx-auto">
+ <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10">
+ {$t("items.add")}
+ </h1>
+ <ItemForm {item} {token} />
+</div>
diff --git a/src/routes/(app)/[username]/settings/[...tab]/_account.svelte b/src/routes/(app)/[username]/settings/[...tab]/_account.svelte
index 15ee7b0c..8e3323c7 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/_account.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/_account.svelte
@@ -1,6 +1,6 @@
<script>
import { tick } from "svelte";
- import { colorTheme, avatar, banner } from "$lib/store";
+ import { avatar, banner } from "$lib/store";
import Icon from "$comp/Icon.svelte";
import { t } from "$lib/translations";
import { page } from "$app/stores";
@@ -9,27 +9,8 @@
let { id } = user;
- let selectedTheme = 1;
let avatarFile, avatarInput, bannerFile, bannerInput;
- let colorThemes = [
- { theme: 1, color1: "from-[#F5F7FA]", color2: "to-[#C3CFE2]" },
- { theme: 2, color1: "from-[#FDFCFB]", color2: "to-[#E2D1C3]" },
- { theme: 3, color1: "from-[#E6E9F0]", color2: "to-[#EEF1F5]" },
- { theme: 4, color1: "from-[#D5D4D0]", color2: "to-[#EEEEEC]" },
- { theme: 5, color1: "from-[#F3E7E9]", color2: "to-[#E3EEFF]" },
- {
- theme: 6,
- color1: "from-[#F5F7FA]",
- color2: "via-[#EAF2FF] to-[#DEDFFF]",
- },
- ];
-
- let handleThemeClick = (colors) => {
- selectedTheme = colors.theme;
- $colorTheme = colors.color1 + " " + colors.color2;
- };
-
let selectAvatar = () => avatarInput.click();
let selectBanner = () => bannerInput.click();
@@ -127,7 +108,7 @@
</div>
{:else}
<div
- class="rounded-full border-4 border-white p-4 bg-gradient-to-r {$colorTheme} w-24 my-auto hover:opacity-80 cursor-pointer"
+ class="rounded-full border-4 border-white p-4 bg-gradient-to-r from-primary to-gradient w-24 my-auto hover:opacity-80 cursor-pointer"
on:click={selectAvatar}
on:keydown={selectAvatar}
>
@@ -171,7 +152,7 @@
/>
{:else}
<div
- class="bg-gradient-to-r {$colorTheme} w-full h-48 mb-4 cursor-pointer hover:opacity-80"
+ class="bg-gradient-to-r from-primary to-gradient w-full h-48 mb-4 cursor-pointer hover:opacity-80"
on:click={selectBanner}
on:keydown={selectBanner}
alt="Banner"
diff --git a/src/routes/(app)/payment/[id]/bump/+server.js b/src/routes/(app)/payment/[id]/bump/+server.js
index 766f537f..513198b0 100644
--- a/src/routes/(app)/payment/[id]/bump/+server.js
+++ b/src/routes/(app)/payment/[id]/bump/+server.js
@@ -7,6 +7,6 @@ export async function POST({ cookies, request }) {
let res = await post("/replace", body, auth(cookies));
return json(res);
} catch (e) {
- error(400, e.message);
+ error(400, e.message);
}
}
diff --git a/src/routes/(app)/send/user/[username]/[...amount]/+page.server.js b/src/routes/(app)/send/user/[username]/[...amount]/+page.server.js
index fe004408..884e3eee 100644
--- a/src/routes/(app)/send/user/[username]/[...amount]/+page.server.js
+++ b/src/routes/(app)/send/user/[username]/[...amount]/+page.server.js
@@ -8,16 +8,13 @@ export async function load({ cookies, params, parent }) {
let { username } = params;
let [amount, currency] = params.amount.split("/");
- if (subject.username === user?.username)
- error(500, { message: "Cannot send to self" });
-
let rate;
if (amount) {
rate = rates[currency ? currency.toUpperCase() : subject.currency];
if (!rate) error(500, "Invalid currency symbol");
if (currency) amount = (amount * sats) / rate;
- let { hash } = await post(
+ let { id } = await post(
"/invoice",
{
invoice: {
@@ -32,7 +29,7 @@ export async function load({ cookies, params, parent }) {
auth(cookies)
);
- redirect(307, `/${username}/invoice/${hash}`);
+ redirect(307, `/${username}/invoice/${id}`);
}
return { amount, rate, subject };
diff --git a/static/icons/edit.svg b/static/icons/edit.svg
new file mode 100644
index 00000000..6f9ec535
--- /dev/null
+++ b/static/icons/edit.svg
@@ -0,0 +1,7 @@
+<?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">
+<g id="Edit / Edit_Pencil_01">
+<path id="Vector" d="M12 8.00012L4 16.0001V20.0001L8 20.0001L16 12.0001M12 8.00012L14.8686 5.13146L14.8704 5.12976C15.2652 4.73488 15.463 4.53709 15.691 4.46301C15.8919 4.39775 16.1082 4.39775 16.3091 4.46301C16.5369 4.53704 16.7345 4.7346 17.1288 5.12892L18.8686 6.86872C19.2646 7.26474 19.4627 7.46284 19.5369 7.69117C19.6022 7.89201 19.6021 8.10835 19.5369 8.3092C19.4628 8.53736 19.265 8.73516 18.8695 9.13061L18.8686 9.13146L16 12.0001M12 8.00012L16 12.0001" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+</svg> \ No newline at end of file
diff --git a/static/icons/trash.svg b/static/icons/trash.svg
new file mode 100644
index 00000000..8686bc51
--- /dev/null
+++ b/static/icons/trash.svg
@@ -0,0 +1,4 @@
+<?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="M4 6H20M16 6L15.7294 5.18807C15.4671 4.40125 15.3359 4.00784 15.0927 3.71698C14.8779 3.46013 14.6021 3.26132 14.2905 3.13878C13.9376 3 13.523 3 12.6936 3H11.3064C10.477 3 10.0624 3 9.70951 3.13878C9.39792 3.26132 9.12208 3.46013 8.90729 3.71698C8.66405 4.00784 8.53292 4.40125 8.27064 5.18807L8 6M18 6V16.2C18 17.8802 18 18.7202 17.673 19.362C17.3854 19.9265 16.9265 20.3854 16.362 20.673C15.7202 21 14.8802 21 13.2 21H10.8C9.11984 21 8.27976 21 7.63803 20.673C7.07354 20.3854 6.6146 19.9265 6.32698 19.362C6 18.7202 6 17.8802 6 16.2V6M14 10V17M10 10V17" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg> \ No newline at end of file