aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2024-11-08 15:19:04 -0800
committerAdam Soltys <[email protected]>2024-11-08 15:19:04 -0800
commit84fb4b209a2650b2a93f3208ee9a23a44d8283c7 (patch)
tree8687566bd8eac12de99439d448773d8ce5dc34b4
parent32fcc461cd43c8907df5e306d520bb671307eab1 (diff)
downloadcoinos-ui-84fb4b209a2650b2a93f3208ee9a23a44d8283c7.tar.gz
coinos-ui-84fb4b209a2650b2a93f3208ee9a23a44d8283c7.zip
svelte 5 fixups
-rwxr-xr-xbun.lockbbin169919 -> 183761 bytes
-rw-r--r--package.json2
-rw-r--r--src/components/Avatar.svelte22
-rw-r--r--src/components/InvoiceData.svelte2
-rw-r--r--src/components/LocationsMap.svelte47
-rw-r--r--src/components/Numpad.svelte55
-rw-r--r--src/components/Pin.svelte50
-rw-r--r--src/lib/settings.ts31
-rw-r--r--src/lib/store.ts1
-rw-r--r--src/routes/(app)/invoice/[id]/+layout.server.ts1
-rw-r--r--src/routes/(app)/invoice/[id]/+page.svelte63
-rw-r--r--src/routes/(app)/send/[...text]/+page.svelte27
-rw-r--r--src/routes/(app)/settings/+layout.server.ts10
-rw-r--r--src/routes/(app)/settings/+layout.svelte (renamed from src/routes/(app)/settings/[...tab]/+page.svelte)50
-rw-r--r--src/routes/(app)/settings/+page.svelte0
-rw-r--r--src/routes/(app)/settings/[...tab]/+page.server.ts36
-rw-r--r--src/routes/(app)/settings/account/+page.server.ts5
-rw-r--r--src/routes/(app)/settings/account/+page.svelte (renamed from src/routes/(app)/settings/[...tab]/_account.svelte)31
-rw-r--r--src/routes/(app)/settings/preferences/+page.server.ts5
-rw-r--r--src/routes/(app)/settings/preferences/+page.svelte (renamed from src/routes/(app)/settings/[...tab]/_pos.svelte)30
-rw-r--r--src/routes/(app)/settings/security/+page.server.ts5
-rw-r--r--src/routes/(app)/settings/security/+page.svelte (renamed from src/routes/(app)/settings/[...tab]/_security.svelte)31
-rw-r--r--src/routes/(app)/settings/shopify/+page.svelte (renamed from src/routes/(app)/settings/[...tab]/_shopify.svelte)0
-rw-r--r--src/routes/+layout.ts6
24 files changed, 232 insertions, 278 deletions
diff --git a/bun.lockb b/bun.lockb
index 53a83cd7..701a46b0 100755
--- a/bun.lockb
+++ b/bun.lockb
Binary files differ
diff --git a/package.json b/package.json
index d5086c64..03277460 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
"@scure/bip39": "^1.2.1",
"@scure/btc-signer": "^1.3.2",
"@sveltejs/adapter-node": "^2.0.0",
- "@sveltejs/kit": "^2.5.27",
+ "@sveltejs/kit": "^2.8.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"@tailwindcss/line-clamp": "^0.4.2",
"@types/uuid": "^10.0.0",
diff --git a/src/components/Avatar.svelte b/src/components/Avatar.svelte
index 3007c3e7..04f346b1 100644
--- a/src/components/Avatar.svelte
+++ b/src/components/Avatar.svelte
@@ -1,5 +1,5 @@
<script lang="ts">
- import { run } from 'svelte/legacy';
+ import { run } from "svelte/legacy";
import { avatar, banner } from "$lib/store";
import Icon from "$comp/Icon.svelte";
@@ -17,8 +17,12 @@
let link = $derived(user?.anon ? `/${user.pubkey}` : `/${user.username}`);
let base = "/api/public";
- let profile = $derived(user?.profile ? `${base}/${user.profile}.webp` : user?.picture);
- let fallback = $derived(`${base}/punks/` + punk(user.pubkey || user.id || "aa"));
+ let profile = $derived(
+ user?.profile ? `${base}/${user.profile}.webp` : user?.picture,
+ );
+ let fallback = $derived(
+ `${base}/punks/` + punk(user.pubkey || user.id || "aa"),
+ );
let tmp = $derived($avatar?.id && $avatar.id === user.id && $avatar.src);
let src;
run(() => {
@@ -28,7 +32,7 @@
let handle = () => (src = fallback);
</script>
-<a href={link} class:pointer-events-none={disabled}>
+{#snippet body()}
<div
class="w-{s} h-{s} rounded-full border-4 border-base-100 overflow-hidden bg-gradient-to-r from-primary to-gradient flex justify-center items-center"
>
@@ -39,4 +43,12 @@
onerror={handle}
/>
</div>
-</a>
+{/snippet}
+
+{#if disabled}
+ {@render body()}
+{:else}
+ <a href={link} class:pointer-events-none={disabled}>
+ {@render body()}
+ </a>
+{/if}
diff --git a/src/components/InvoiceData.svelte b/src/components/InvoiceData.svelte
index da1ea5d9..2298d150 100644
--- a/src/components/InvoiceData.svelte
+++ b/src/components/InvoiceData.svelte
@@ -4,7 +4,6 @@
showQr,
src,
link,
- qr = $bindable(),
txt,
invoice,
amount,
@@ -21,7 +20,6 @@
<img
{src}
class="mx-auto z-10 max-w-[360px] border-4 border-white"
- bind:this={qr}
alt={txt}
/>
</a>
diff --git a/src/components/LocationsMap.svelte b/src/components/LocationsMap.svelte
index b04b8e98..36223b75 100644
--- a/src/components/LocationsMap.svelte
+++ b/src/components/LocationsMap.svelte
@@ -1,7 +1,6 @@
<script>
import { tick, onMount, onDestroy, mount } from "svelte";
import { browser } from "$app/environment";
- import Icon from "$comp/Icon.svelte";
import Popup from "$comp/Popup.svelte";
import { back } from "$lib/utils";
@@ -9,7 +8,8 @@
let { locations } = $props();
let map;
- let mapContainer = $state(), mapWrapper = $state();
+ let mapContainer = $state(),
+ mapWrapper = $state();
let markers = [];
let search = $state();
let clearSearch = (e) => (search = "");
@@ -49,7 +49,6 @@
}
};
-
let scroll = () => {
const el = document.querySelector(".selected");
if (el)
@@ -197,9 +196,9 @@
let popupContainer = document.createElement("div");
mount(Popup, {
- target: popupContainer,
- props: { tags },
- });
+ target: popupContainer,
+ props: { tags },
+ });
let marker = new maplibre.Marker({ color: "#F7931A", scale: 0.65 })
.setLngLat([lon, lat])
@@ -247,11 +246,13 @@
});
onDestroy(() => map && map.remove());
- let list = $derived(search
- ? markers.filter((m) =>
- m.tags.name.toLowerCase().includes(search.toLowerCase()),
- )
- : inview);
+ let list = $derived(
+ search
+ ? markers.filter((m) =>
+ m.tags.name.toLowerCase().includes(search.toLowerCase()),
+ )
+ : inview,
+ );
</script>
<div class="w-screen h-dvh flex" id="map" bind:this={mapWrapper}>
@@ -259,29 +260,29 @@
id="map-container"
class="mx-auto h-full w-full z-0"
bind:this={mapContainer}
-></div>
+ ></div>
<div class="absolute flex top-2 left-2 gap-2">
<button
- class="rounded-full border-2 border-black bg-base-100 w-10 h-10"
+ class="rounded-full border-2 border-black bg-base-100 w-16 h-16 flex items-center justify-center bg-opacity-80"
onclick={back}
>
- <Icon icon="arrow-left" style="w-4 m-auto" />
+ <iconify-icon icon="ph:arrow-left-bold" width="24"></iconify-icon>
</button>
</div>
<div class="relative">
{#if !showList}
<div
- class="space-y-1 text-sm w-[300px] max-w-[calc(100vw*0.5)] h-[300px] max-h-[calc(100vh*0.4)] overflow-y-scroll p-4 bg-opacity-90 bg-white absolute right-4 bottom-8 rounded-2xl shadow text-ellipsis overflow-x-hidden"
+ class="space-y-1 text-sm w-[300px] max-w-[calc(100vw*0.5)] h-[300px] max-h-[calc(100vh*0.4)] overflow-y-scroll p-4 bg-opacity-90 bg-base-100 absolute right-4 bottom-8 rounded-2xl shadow text-ellipsis overflow-x-hidden"
>
- <div class="relative mb-4 sticky top-0 bg-white z-10">
+ <div class="relative mb-4 sticky top-0 bg-base-100 z-10">
<input bind:value={search} class="w-full" />
<div class="flex gap-1 absolute right-6 top-3">
{#if search}
<button onclick={clearSearch}>
- <Icon icon="close" style="w-8" />
+ <iconify-icon icon="ph:x-bold" width="32"></iconify-icon>
</button>
{:else}
- <Icon icon="search" style="w-8" />
+ <iconify-icon icon="ph:search-bold" width="32"></iconify-icon>
{/if}
</div>
</div>
@@ -300,19 +301,19 @@
{/if}
<div class="absolute flex top-2 right-2 gap-2">
<button
- class="rounded-full border-2 border-black bg-white w-10 h-10"
+ class="rounded-full border-2 border-primary bg-base-100 w-16 h-16 flex items-center justify-center bg-opacity-80"
onclick={toggleList}
>
- <Icon icon="list" style="w-4 m-auto" />
+ <iconify-icon icon="ph:list-bold" width="24"></iconify-icon>
</button>
<button
- class="rounded-full border-2 border-black bg-white w-10 h-10"
+ class="rounded-full border-2 border-primary bg-base-100 w-16 h-16 flex items-center justify-center bg-opacity-80"
onclick={toggle}
>
{#if timeout}
- <Icon icon="pause" style="w-4 m-auto" />
+ <iconify-icon icon="ph:pause-bold" width="24"></iconify-icon>
{:else}
- <Icon icon="play" style="w-4 m-auto" />
+ <iconify-icon icon="ph:play-bold" width="24"></iconify-icon>
{/if}
</button>
</div>
diff --git a/src/components/Numpad.svelte b/src/components/Numpad.svelte
index 03c7c48f..07d1ef4c 100644
--- a/src/components/Numpad.svelte
+++ b/src/components/Numpad.svelte
@@ -1,33 +1,21 @@
-<script lang="ts">
- import { run, preventDefault, stopPropagation } from "svelte/legacy";
-
+<script>
import Left from "$comp/Left.svelte";
import Icon from "$comp/Icon.svelte";
import { f, s, focus, warning, sat, sats } from "$lib/utils";
import { t } from "$lib/translations";
- interface Props {
- amount: any;
- currency: any;
- element: any;
- fiat?: any;
- rate: any;
- submit?: any;
- amountFiat?: number;
- }
-
- let {
- amount = $bindable(),
+ export let amount,
currency,
- element = $bindable(),
- fiat = $bindable(!amount),
- rate = $bindable(),
- submit = undefined,
- amountFiat = $bindable(0),
- }: Props = $props();
+ element,
+ fiat = !amount,
+ rate,
+ submit = undefined;
+
+ export let amountFiat = 0;
let arrow = "<";
+ $: update(amount, amountFiat);
let update = (a, f) => {
if (fiat) {
amount = Math.round(f / (rate / sats));
@@ -114,6 +102,8 @@
}
};
+ $: html = fiat ? amountFiat : s(amount);
+
let prev = "";
let input = (e) => {
@@ -203,13 +193,6 @@
}
fiat = !fiat;
};
- run(() => {
- update(amount, amountFiat);
- });
- let html;
- run(() => {
- html = fiat ? amountFiat : s(amount);
- });
</script>
<div class="flex justify-center items-center">
@@ -225,17 +208,17 @@
use:focus
contenteditable
bind:innerHTML={html}
- onfocus={select}
- onblur={blur}
- oninput={input}
- onkeydown={keydown}
+ on:focus={select}
+ on:blur={blur}
+ on:input={input}
+ on:keydown={keydown}
class="outline-none my-auto"
bind:this={element}
- ></div>
+ />
</div>
<div
class="flex items-center justify-center text-2xl cursor-pointer"
- onclick={stopPropagation(preventDefault(swap))}
+ on:click|preventDefault|stopPropagation={swap}
>
{fiat ? "⚡️" + s(amount) : f(amountFiat, currency)}
</div>
@@ -247,7 +230,7 @@
<button
type="button"
class="btn"
- onclick={stopPropagation(preventDefault(() => handleInput(value)))}
+ on:click|preventDefault|stopPropagation={() => handleInput(value)}
>
<Left />
</button>
@@ -255,7 +238,7 @@
<button
type="button"
class="btn"
- onclick={stopPropagation(preventDefault(() => handleInput(value)))}
+ on:click|preventDefault|stopPropagation={() => handleInput(value)}
>{value}</button
>
{/if}
diff --git a/src/components/Pin.svelte b/src/components/Pin.svelte
index da53577e..fb17c612 100644
--- a/src/components/Pin.svelte
+++ b/src/components/Pin.svelte
@@ -1,36 +1,28 @@
-<script lang="ts">
- import { run, createBubbler, preventDefault } from "svelte/legacy";
-
- const bubble = createBubbler();
+<script>
import { t } from "$lib/translations";
import { post, success, fail, focus, setCookie } from "$lib/utils";
import Pinpad from "$comp/Pinpad.svelte";
import { pin } from "$lib/store";
import { onMount } from "svelte";
- interface Props {
- value?: string;
- title?: any;
- cancel?: any;
- notify?: boolean;
- }
-
- let {
- value = $bindable(""),
- title = $t("user.settings.verifyPIN"),
- cancel = () => (p = ""),
- notify = true,
- }: Props = $props();
+ export let value = "";
+ export let title = $t("user.settings.verifyPIN");
+ export let cancel = () => (p = "");
+ export let notify = true;
- let p = $state("");
+ let p = "";
+ $: update(value);
let update = (value) => (p = value);
- let loaded = $state();
+ $: p.toString().length > 5 && (value = p.toString());
+
+ let loaded;
onMount(() => setTimeout(() => (loaded = true), 500));
- let locktime = $state(30 * 60);
+ let locktime = 30 * 60;
+ $: loaded && checkPin(p);
let checkPin = async (p) => {
if (p?.length !== 6) return;
let result;
@@ -49,21 +41,12 @@
value = "";
}
};
- run(() => {
- p.toString().length > 5 && (value = p.toString());
- });
- run(() => {
- update(value);
- });
- run(() => {
- loaded && checkPin(p);
- });
</script>
{#if loaded}
<div
class="fixed bg-base-100 bg-opacity-90 inset-0 h-full w-full z-50 cursor-default"
- onclick={preventDefault(bubble("click"))}
+ on:click|preventDefault
>
<div
class="mx-auto p-5 border shadow-lg rounded-md bg-base-100 space-y-5 max-w-lg"
@@ -75,7 +58,10 @@
<label for="locktime" class="font-bold"
>{$t("user.settings.rememberFor")}</label
>
- <select name="locktime" bind:value={locktime}>
+ <select
+ name="locktime"
+ bind:value={locktime}
+ >
<option value={30}>30 {$t("user.settings.seconds")}</option>
<option value={5 * 60}>5 {$t("user.settings.minutes")}</option>
<option value={10 * 60}>10 {$t("user.settings.minutes")}</option>
@@ -86,7 +72,7 @@
</div>
<div class="w-full flex">
- <button class="btn" onclick={preventDefault(cancel)}>
+ <button class="btn" on:click|preventDefault={cancel}>
<div class="my-auto">Cancel</div>
</button>
</div>
diff --git a/src/lib/settings.ts b/src/lib/settings.ts
new file mode 100644
index 00000000..b78d83fe
--- /dev/null
+++ b/src/lib/settings.ts
@@ -0,0 +1,31 @@
+import { auth, fd, get, post } from "$lib/utils";
+import { fail } from "@sveltejs/kit";
+
+type Error = {
+ message: string;
+};
+
+export default async ({ cookies, request }) => {
+ const form = await fd(request);
+
+ if (form.tab === "pos") {
+ form.notify = form.notify === "on";
+ form.push = form.push === "on";
+ form.nip5 = form.nip5 === "on";
+ form.prompt = form.prompt === "on";
+ form.autowithdraw = form.autowithdraw === "on";
+ }
+
+ let user = { ...(await get("/me", auth(cookies))), ...form };
+
+ try {
+ ({ user } = await post("/user", user, auth(cookies)));
+ } catch (e) {
+ const { message } = e as Error;
+ return fail(400, { message });
+ }
+
+ if (user.language) cookies.set("lang", user.language, { path: "/" });
+
+ return { user, success: true };
+};
diff --git a/src/lib/store.ts b/src/lib/store.ts
index 5c7a0957..e294329e 100644
--- a/src/lib/store.ts
+++ b/src/lib/store.ts
@@ -61,3 +61,4 @@ export const theme = writable("light");
export const token = persistSession("token");
export const ndef = writable();
export const showQr = persistLocal("showQr", true);
+export const save = writable();
diff --git a/src/routes/(app)/invoice/[id]/+layout.server.ts b/src/routes/(app)/invoice/[id]/+layout.server.ts
index e428d9fe..9b667c7b 100644
--- a/src/routes/(app)/invoice/[id]/+layout.server.ts
+++ b/src/routes/(app)/invoice/[id]/+layout.server.ts
@@ -35,6 +35,7 @@ export async function load({ cookies, depends, params, url, parent }) {
}
}
+ console.log("REDRAW", invoice?.text);
const src = Qr.drawImg(invoice?.text || "", { size: 500 });
const theme = cookies.get("theme");
return { id, invoice, subject, src, user, token, theme };
diff --git a/src/routes/(app)/invoice/[id]/+page.svelte b/src/routes/(app)/invoice/[id]/+page.svelte
index fef43e4d..ded42016 100644
--- a/src/routes/(app)/invoice/[id]/+page.svelte
+++ b/src/routes/(app)/invoice/[id]/+page.svelte
@@ -26,7 +26,8 @@
let readingerror = (e) => console.log("nfc error", e);
- let { invoice, id, subject, user, src } = $state(data);
+ let { invoice, id, subject, user } = $state(data);
+ let src = $derived(data.src);
let {
aid,
amount,
@@ -38,32 +39,15 @@
user: { username, currency },
} = $state(invoice);
- let qr = $state();
- let tipPercent = 0;
+ let tipPercent = $derived(tip ? (tip / amount) * 100 : 0);
- let refresh = async (data) => {
- ({ invoice, id, user, src } = data);
- ({
- aid,
- amount,
- hash,
- type,
- rate,
- text,
- tip,
- user: { username, currency },
- } = invoice);
-
- if (browser && !subbed[id])
- send("subscribe", invoice)
- .then(() => (subbed[id] = true))
- .catch((e) => {
- console.log("failed to subscribe to invoice notifications", invoice);
- console.log(e);
- });
-
- tipPercent = (tip / amount) * 100;
- };
+ // if (browser && !subbed[id])
+ // send("subscribe", invoice)
+ // .then(() => (subbed[id] = true))
+ // .catch((e) => {
+ // console.log("failed to subscribe to invoice notifications", invoice);
+ // console.log(e);
+ // });
let subbed = {};
@@ -90,6 +74,7 @@
};
let setAmount = $state(async () => {
+ console.log("LALA", newAmount);
if (typeof $amountPrompt === "undefined") $amountPrompt = true;
settingAmount = false;
amount = newAmount;
@@ -104,6 +89,7 @@
if (subject?.prompt) url += "/tip";
else url += "?options=true";
+ console.log("GO", url);
goto(url, { invalidateAll: true, noScroll: true });
});
@@ -112,31 +98,20 @@
let toggleAmount = $state(() => (settingAmount = !settingAmount));
let fiat = $state(true);
let submit = $state();
- run(() => {
- refresh(data);
- });
- let amountFiat;
- run(() => {
- amountFiat = parseFloat(((amount * rate) / sats).toFixed(2));
- });
+ let amountFiat = $derived(parseFloat(((amount * rate) / sats).toFixed(2)));
let tipAmount = $derived(((tip * rate) / sats).toFixed(2));
- let link;
- run(() => {
- link = [types.bitcoin, types.liquid].includes(type)
- ? text
- : `lightning:${text}`;
- });
- let txt;
- run(() => {
- txt = [types.bitcoin, types.liquid].includes(type) ? hash : text;
- });
+ let link = $derived(
+ [types.bitcoin, types.liquid].includes(type) ? text : `lightning:${text}`,
+ );
+ let txt = $derived(
+ [types.bitcoin, types.liquid].includes(type) ? hash : text,
+ );
</script>
<div class="invoice container mx-auto max-w-xl px-4 space-y-2">
<InvoiceData
{src}
{link}
- {qr}
{txt}
{invoice}
{amount}
diff --git a/src/routes/(app)/send/[...text]/+page.svelte b/src/routes/(app)/send/[...text]/+page.svelte
index 7ec76bc9..6b93b94b 100644
--- a/src/routes/(app)/send/[...text]/+page.svelte
+++ b/src/routes/(app)/send/[...text]/+page.svelte
@@ -1,5 +1,5 @@
<script>
- import { run } from 'svelte/legacy';
+ import { run } from "svelte/legacy";
import { page } from "$app/stores";
import { fly } from "svelte/transition";
@@ -17,7 +17,10 @@
let { contacts } = data;
- let el = $state(), text = $state(), pasted = $state(), w = $state();
+ let el = $state(),
+ text = $state(),
+ pasted = $state(),
+ w = $state();
let keypress = (e) =>
e.key === "Enter" ? e.preventDefault() || el.click() : (pasted = false);
@@ -60,7 +63,7 @@
bind:value={text}
onpaste={() => (pasted = true)}
autocapitalize="none"
-></textarea>
+ ></textarea>
<div class="flex gap-2">
<a href="/scan" class="contents">
@@ -95,18 +98,12 @@
{$t("user.send.contacts")}
</h1>
<div>
- {#each contacts as c}
- <a href={`/pay/${c.username}`}>
- <div class="border-b p-2 last:border-b-0 hover:bg-base-200">
- <div class="flex">
- <div>
- <div class="flex">
- <Avatar user={c} size={20} disabled={true} />
- <div class="my-auto text-left">
- <p class="ml-1 text-lg break-words">{c.username}</p>
- </div>
- </div>
- </div>
+ {#each contacts as c, i}
+ <a href={`/pay/${c.username}`} class="contents">
+ <div class="flex hover:bg-base-200 p-2">
+ <Avatar user={c} size={20} disabled={true} />
+ <div class="my-auto text-left">
+ <p class="ml-1 text-lg break-words">{c.username}</p>
</div>
</div>
</a>
diff --git a/src/routes/(app)/settings/+layout.server.ts b/src/routes/(app)/settings/+layout.server.ts
new file mode 100644
index 00000000..cd63d4f0
--- /dev/null
+++ b/src/routes/(app)/settings/+layout.server.ts
@@ -0,0 +1,10 @@
+import { auth, get } from "$lib/utils";
+import { redirect } from "@sveltejs/kit";
+
+export async function load({ cookies, params, url }) {
+ const tab = url.pathname.split("/").filter(Boolean).pop();
+ if (tab === "settings") redirect(307, `${url.pathname}/account`);
+ params.cookies = cookies.getAll();
+ const subscriptions = await get("/subscriptions", auth(cookies));
+ return { cookies: cookies.getAll(), subscriptions, tab };
+}
diff --git a/src/routes/(app)/settings/[...tab]/+page.svelte b/src/routes/(app)/settings/+layout.svelte
index 5b9e048d..edf256ab 100644
--- a/src/routes/(app)/settings/[...tab]/+page.svelte
+++ b/src/routes/(app)/settings/+layout.svelte
@@ -1,5 +1,6 @@
<script>
- import { run, preventDefault } from 'svelte/legacy';
+ import { run, preventDefault } from "svelte/legacy";
+ import { setContext } from "svelte";
import { browser } from "$app/environment";
import { onMount, tick } from "svelte";
@@ -11,33 +12,20 @@
import Pin from "$comp/Pin.svelte";
import { loading, t } from "$lib/translations";
import { fail, auth, post, sleep, warning, success } from "$lib/utils";
- import { avatar, banner, password, pin } from "$lib/store";
+ import { avatar, banner, password, pin, save } from "$lib/store";
import { upload } from "$lib/upload";
import { page } from "$app/stores";
import { sign, send, reEncryptEntropy, setNsec } from "$lib/nostr";
import { invalidateAll } from "$app/navigation";
-
- import Account from "./_account.svelte";
- import Pos from "./_pos.svelte";
- import Security from "./_security.svelte";
- import Shopify from "./_shopify.svelte";
-
import { PUBLIC_COINOS_URL } from "$env/static/public";
let { data, form } = $props();
- let submit = $state();
let formElement = $state();
- let { user, token, rates, tab, cookies, subscriptions } = $state(data);
- let prev;
-
- let update = () => {
- ({ user, token, rates, tab, subscriptions } = data);
- prev = { ...user };
- };
-
-
+ let { user, token, rates, cookies, subscriptions } = $state(data);
+ let { tab } = $derived(data);
+ let prev = $derived({ ...user });
let justUpdated;
let throttledSuccess = () => {
@@ -48,20 +36,16 @@
}
};
-
-
let tabs = [
- { name: "account", key: "ACCOUNT", comp: Account },
- { name: "pos", key: "POINT_OF_SALE", comp: Pos },
- { name: "security", key: "SECURITY", comp: Security },
- { name: "shopify", key: "SHOPIFY", comp: Shopify },
+ { name: "account", key: "ACCOUNT" },
+ { name: "preferences", key: "POINT_OF_SALE" },
+ { name: "security", key: "SECURITY" },
+ { name: "shopify", key: "SHOPIFY" },
];
-
let { address, id, username } = user;
let submitting = $state();
-
async function handleSubmit() {
try {
submitting = true;
@@ -182,9 +166,6 @@
submitting = false;
}
run(() => {
- update(data);
- });
- run(() => {
form?.user && ({ user } = form);
});
run(() => {
@@ -199,13 +180,10 @@
$pin = "";
}
});
- let { comp } = $derived(tabs.find((t) => t.name === tab));
run(() => {
if (!$loading && $page.url.searchParams.get("verified"))
success($t("user.settings.verified"));
});
-
- const SvelteComponent = $derived(comp);
</script>
{#if user.haspin && $pin?.length !== 6}
@@ -227,9 +205,7 @@
{$t("user.settings.header")}
</h1>
- <div
- class="flex justify-around items-center border-b pb-3"
- >
+ <div class="flex justify-around items-center border-b pb-3">
{#each tabs.filter((t) => t.name !== "shopify") as { name, key }}
<a href={`/settings/${name}`}>
<button
@@ -244,14 +220,14 @@
</div>
</div>
- <SvelteComponent {user} {rates} {submit} {subscriptions} />
+ <slot />
</div>
<div
class="z-10 fixed bottom-0 bg-base-100 shadow border-t border-t-8 border-accent w-full flex justify-center items-center py-3"
>
<button
- bind:this={submit}
+ bind:this={$save}
type="submit"
class="btn !w-auto"
class:bg-base-200={submitting}
diff --git a/src/routes/(app)/settings/+page.svelte b/src/routes/(app)/settings/+page.svelte
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/routes/(app)/settings/+page.svelte
diff --git a/src/routes/(app)/settings/[...tab]/+page.server.ts b/src/routes/(app)/settings/[...tab]/+page.server.ts
deleted file mode 100644
index 39533426..00000000
--- a/src/routes/(app)/settings/[...tab]/+page.server.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { auth, fd, get, post } from "$lib/utils";
-import { fail, redirect } from "@sveltejs/kit";
-
-export async function load({ cookies, params, url }) {
- if (url.pathname.endsWith("settings"))
- redirect(307, `${url.pathname}/account`);
- params.cookies = cookies.getAll();
- const subscriptions = await get("/subscriptions", auth(cookies));
- return { cookies: cookies.getAll(), subscriptions, tab: params.tab };
-}
-
-export const actions = {
- default: async ({ cookies, request }) => {
- const form = await fd(request);
-
- if (form.tab === "pos") {
- form.notify = form.notify === "on";
- form.push = form.push === "on";
- form.nip5 = form.nip5 === "on";
- form.prompt = form.prompt === "on";
- form.autowithdraw = form.autowithdraw === "on";
- }
-
- let user = { ...(await get("/me", auth(cookies))), ...form };
-
- try {
- ({ user } = await post("/user", user, auth(cookies)));
- } catch (e: any) {
- return fail(400, { message: e.message });
- }
-
- if (user.language) cookies.set("lang", user.language, { path: "/" });
-
- return { user, success: true };
- },
-};
diff --git a/src/routes/(app)/settings/account/+page.server.ts b/src/routes/(app)/settings/account/+page.server.ts
new file mode 100644
index 00000000..4145935e
--- /dev/null
+++ b/src/routes/(app)/settings/account/+page.server.ts
@@ -0,0 +1,5 @@
+import updateUser from "$lib/settings";
+
+export const actions = {
+ default: updateUser,
+};
diff --git a/src/routes/(app)/settings/[...tab]/_account.svelte b/src/routes/(app)/settings/account/+page.svelte
index 54249265..77860948 100644
--- a/src/routes/(app)/settings/[...tab]/_account.svelte
+++ b/src/routes/(app)/settings/account/+page.svelte
@@ -4,11 +4,14 @@
import { t } from "$lib/translations";
import { page } from "$app/stores";
- let { user = $bindable(), rates, submit } = $props();
-
+ let { data } = $props();
+ let { user, rates } = $state(data);
let { id } = user;
- let avatarFile, avatarInput = $state(), bannerFile, bannerInput = $state();
+ let avatarFile,
+ avatarInput = $state(),
+ bannerFile,
+ bannerInput = $state();
let selectAvatar = () => avatarInput.click();
let selectBanner = () => bannerInput.click();
@@ -45,8 +48,6 @@
reader.readAsDataURL(file);
};
- if (!user.display) user.display = user.username;
-
let url = $derived(`${$page.url.host}/${user.username}`);
let full = $derived(`${$page.url.protocol}//${url}`);
let addr = $derived(`${user.username}@${$page.url.host}`);
@@ -79,17 +80,11 @@
>
<input type="text" name="email" class="clean" bind:value={user.email} />
{#if user.verified}
- <iconify-icon
- icon="ph:check-bold"
- class="text-success ml-auto"
- width="32"
-></iconify-icon>
+ <iconify-icon icon="ph:check-bold" class="text-success ml-auto" width="32"
+ ></iconify-icon>
{:else if user.email}
- <iconify-icon
- icon="ph:clock-bold"
- class="text-warning ml-auto"
- width="32"
-></iconify-icon>
+ <iconify-icon icon="ph:clock-bold" class="text-warning ml-auto" width="32"
+ ></iconify-icon>
{/if}
</label>
</div>
@@ -116,7 +111,7 @@
onclick={selectAvatar}
onkeydown={selectAvatar}
>
- <Icon icon="logo-symbol-white" style="mx-auto" />
+ <iconify-icon icon="coinos:symbol" class="mx-auto"></iconify-icon>
<iconify-icon icon="ph:floppy-disk-bold" width="32"></iconify-icon>
</div>
{/if}
@@ -161,7 +156,7 @@
onclick={selectBanner}
onkeydown={selectBanner}
alt="Banner"
-></div>
+ ></div>
{/if}
<button
@@ -191,5 +186,5 @@
name="address"
bind:value={user.address}
placeholder={$t("user.settings.aboutPlaceholder")}
-></textarea>
+ ></textarea>
</div>
diff --git a/src/routes/(app)/settings/preferences/+page.server.ts b/src/routes/(app)/settings/preferences/+page.server.ts
new file mode 100644
index 00000000..4145935e
--- /dev/null
+++ b/src/routes/(app)/settings/preferences/+page.server.ts
@@ -0,0 +1,5 @@
+import updateUser from "$lib/settings";
+
+export const actions = {
+ default: updateUser,
+};
diff --git a/src/routes/(app)/settings/[...tab]/_pos.svelte b/src/routes/(app)/settings/preferences/+page.svelte
index 3f68eebc..762d25f5 100644
--- a/src/routes/(app)/settings/[...tab]/_pos.svelte
+++ b/src/routes/(app)/settings/preferences/+page.svelte
@@ -1,5 +1,5 @@
<script>
- import { run } from 'svelte/legacy';
+ import { run } from "svelte/legacy";
import { onMount, tick } from "svelte";
import { browser } from "$app/environment";
@@ -11,20 +11,19 @@
import { page } from "$app/stores";
import { PUBLIC_VAPID_PUBKEY } from "$env/static/public";
- let {
- user = $bindable(),
- rates,
- submit,
- subscriptions
- } = $props();
-
+ let { data } = $props();
+ let user = $derived(data.user);
+ let rates = $derived(data.rates);
let { currency } = $state(user);
let rate = $state(rates[currency]);
let fiats = Object.keys(rates).sort((a, b) => a.localeCompare(b));
let keypress = (e) => e.key === "Enter" && (e.preventDefault() || el.click());
- let editingReserve = $state(), editingThreshold = $state(), doneReserve = $state(), doneThreshold;
+ let editingReserve = $state(),
+ editingThreshold = $state(),
+ doneReserve = $state(),
+ doneThreshold;
let doneEditing = () => {
editingReserve = false;
editingThreshold = false;
@@ -44,9 +43,12 @@
if (!user.threshold) user.threshold = 1000000;
if (!user.reserve) user.reserve = 100000;
- let reserveEl = $state(), thresholdEl = $state();
+ let reserveEl = $state(),
+ thresholdEl = $state();
- let push = $state(), pm, subscription;
+ let push = $state(),
+ pm,
+ subscription;
onMount(async () => {
if (!browser) return;
@@ -104,7 +106,7 @@
<label for="currency" class="font-bold block mb-1"
>{$t("user.settings.localCurrency")}</label
>
- <select name="currency" value={user.currency}>
+ <select name="currency" value={currency}>
{#each fiats as fiat}
<option value={fiat}>{fiat}</option>
{/each}
@@ -168,7 +170,7 @@
onkeypress={keypress}
class="w-full p-4 border rounded-xl h-48"
bind:value={user.destination}
-></textarea>
+ ></textarea>
</div>
<div>
@@ -222,7 +224,7 @@
</h1>
<Numpad
bind:amount={user.threshold}
- bind:currency
+ {currency}
bind:rate
bind:submit={doneReserve}
bind:element={thresholdEl}
diff --git a/src/routes/(app)/settings/security/+page.server.ts b/src/routes/(app)/settings/security/+page.server.ts
new file mode 100644
index 00000000..4145935e
--- /dev/null
+++ b/src/routes/(app)/settings/security/+page.server.ts
@@ -0,0 +1,5 @@
+import updateUser from "$lib/settings";
+
+export const actions = {
+ default: updateUser,
+};
diff --git a/src/routes/(app)/settings/[...tab]/_security.svelte b/src/routes/(app)/settings/security/+page.svelte
index 8bb8a3fa..d3f27ee6 100644
--- a/src/routes/(app)/settings/[...tab]/_security.svelte
+++ b/src/routes/(app)/settings/security/+page.svelte
@@ -1,5 +1,6 @@
<script>
- import { run } from 'svelte/legacy';
+ import { run } from "svelte/legacy";
+ import { getContext } from "svelte";
import { getMnemonic, getNsec } from "$lib/nostr";
import { tick } from "svelte";
@@ -8,11 +9,13 @@
import Icon from "$comp/Icon.svelte";
import Qr from "$comp/Qr.svelte";
import { copy, post, success, fail } from "$lib/utils";
- import { pin as current } from "$lib/store";
+ import { save, pin as current } from "$lib/store";
import { invalidate } from "$app/navigation";
import { page } from "$app/stores";
- let { user = $bindable(), rates, submit } = $props();
+ let { data } = $props();
+ let { user, rates } = $state(data);
+ let { id } = user;
let confirming2fa = $state(),
disabling2fa = $state(),
@@ -24,14 +27,14 @@
old,
otp = $state(),
password = $state(),
- pin = $state(),
+ pin = $state(""),
revealPassword = $state(),
revealSeed,
revealNsec = $state(),
token = $state(""),
setting2fa = $state(),
settingPin = $state(),
- verify = $state();
+ verify = $state("");
let toggleImporting = () => {
revealNsec = false;
@@ -67,7 +70,7 @@
$current = pin;
pin = "";
verify = "";
- submit.click();
+ $save.click();
settingPin = false;
verifying = false;
} else {
@@ -95,7 +98,7 @@
try {
disablingPin = true;
await tick();
- submit.click();
+ $save.click();
} catch (e) {
console.log(e);
fail("Failed to disable pin");
@@ -138,6 +141,7 @@
cancel();
}
} catch (e) {
+ console.log(e);
fail("Failed to enable 2FA, try again");
}
};
@@ -155,10 +159,7 @@
fail("Failed to disable 2FA, try again");
}
};
- let verifying;
- run(() => {
- verifying = pin?.length > 5;
- });
+ let verifying = $derived(pin?.length > 5);
run(() => {
verify && checkPin(pin);
});
@@ -170,6 +171,7 @@
});
</script>
+{console.log("SAVE", $save)}
<input type="hidden" name="newpin" value={disablingPin ? "delete" : pin} />
<input type="hidden" name="confirm" bind:value={password} />
@@ -191,7 +193,7 @@
<iconify-icon
icon={revealPassword ? "ph:eye-bold" : "ph:eye-slash-bold"}
width="32"
-></iconify-icon>
+ ></iconify-icon>
</button>
</div>
@@ -214,12 +216,11 @@
notify={false}
/>
{:else}
- <button type="button" class="btn" onclick={togglePin}
- >
+ <button type="button" class="btn" onclick={togglePin}>
<iconify-icon
icon={user.haspin ? "ph:lock-key-open-bold" : "ph:lock-key-bold"}
width="32"
-></iconify-icon>
+ ></iconify-icon>
{user.haspin
? $t("user.settings.disablePIN")
: $t("user.settings.enablePIN")}</button
diff --git a/src/routes/(app)/settings/[...tab]/_shopify.svelte b/src/routes/(app)/settings/shopify/+page.svelte
index cc746c49..cc746c49 100644
--- a/src/routes/(app)/settings/[...tab]/_shopify.svelte
+++ b/src/routes/(app)/settings/shopify/+page.svelte
diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts
index ee0ed70c..0de57279 100644
--- a/src/routes/+layout.ts
+++ b/src/routes/+layout.ts
@@ -47,3 +47,9 @@ addIcon("coinos:smile", {
width: 112,
height: 108,
});
+
+addIcon("coinos:symbol", {
+ body: `<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M36 72C55.8823 72 72 55.8823 72 36C72 16.1177 55.8823 0 36 0C16.1177 0 0 16.1177 0 36C0 55.8823 16.1177 72 36 72ZM36 67.2C53.2313 67.2 67.2 53.2313 67.2 36C67.2 18.7687 53.2313 4.8 36 4.8C18.7687 4.8 4.8 18.7687 4.8 36C4.8 53.2313 18.7687 67.2 36 67.2Z" fill="white"/><path fill-rule="evenodd" clip-rule="evenodd" d="M36.001 12.7998C36.0009 12.7998 36.0008 12.7998 36.0008 12.7998C23.1878 12.7998 12.8008 23.1868 12.8008 35.9998C12.8008 48.8128 23.1878 59.1998 36.0008 59.1998C36.0008 59.1998 36.0009 59.1998 36.001 59.1998V12.7998Z" fill="white"/></svg>`,
+ width: 72,
+ height: 72,
+});