diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/AppHeader.svelte | 4 | ||||
| -rw-r--r-- | src/components/Avatar.svelte | 4 | ||||
| -rw-r--r-- | src/components/ItemForm.svelte | 108 | ||||
| -rw-r--r-- | src/lib/nostr.js | 2 | ||||
| -rw-r--r-- | src/lib/store.js | 1 | ||||
| -rw-r--r-- | src/lib/utils.js | 1 | ||||
| -rw-r--r-- | src/locales/en.json | 9 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+page.svelte | 176 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/items/[id]/+page.server.js | 6 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/items/[id]/+page.svelte | 7 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/items/create/+page.svelte | 14 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/settings/[...tab]/_account.svelte | 25 | ||||
| -rw-r--r-- | src/routes/(app)/payment/[id]/bump/+server.js | 2 | ||||
| -rw-r--r-- | src/routes/(app)/send/user/[username]/[...amount]/+page.server.js | 7 |
14 files changed, 301 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} — {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 }; |
