aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2023-12-01 20:04:13 -0800
committerAdam Soltys <[email protected]>2023-12-01 20:04:13 -0800
commit216a600224fde22ce76b367557a02094ec693886 (patch)
treed1189bbaa39db204633523132a7d4767b952717d /src/routes
parent7d9bc1f54dfd04172e8d6d43ebc2efd6f407e833 (diff)
downloadcoinos-ui-216a600224fde22ce76b367557a02094ec693886.tar.gz
coinos-ui-216a600224fde22ce76b367557a02094ec693886.zip
prettier
Diffstat (limited to 'src/routes')
-rw-r--r--src/routes/(app)/+error.svelte58
-rw-r--r--src/routes/(app)/+layout.server.js72
-rw-r--r--src/routes/(app)/+layout.svelte255
-rw-r--r--src/routes/(app)/[username]/(profile)/+layout.server.js36
-rw-r--r--src/routes/(app)/[username]/(profile)/+layout.svelte428
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.server.js52
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.svelte416
-rw-r--r--src/routes/(app)/[username]/(profile)/event/[id]/+page.server.js18
-rw-r--r--src/routes/(app)/[username]/(profile)/event/[id]/+page.svelte6
-rw-r--r--src/routes/(app)/[username]/(profile)/followers/+page.svelte81
-rw-r--r--src/routes/(app)/[username]/(profile)/follows/+page.svelte78
-rw-r--r--src/routes/(app)/[username]/(profile)/request/+page.server.js22
-rw-r--r--src/routes/(app)/[username]/(profile)/request/+page.svelte50
-rw-r--r--src/routes/(app)/[username]/(profile)/request/sent/+page.svelte21
-rw-r--r--src/routes/(app)/[username]/+layout.server.js18
-rw-r--r--src/routes/(app)/[username]/+layout.svelte82
-rw-r--r--src/routes/(app)/[username]/address/+page.server.js14
-rw-r--r--src/routes/(app)/[username]/card/[serial]/+page.server.js22
-rw-r--r--src/routes/(app)/[username]/card/[serial]/+page.svelte60
-rw-r--r--src/routes/(app)/[username]/funds/+page.svelte73
-rw-r--r--src/routes/(app)/[username]/generate/+page.svelte90
-rw-r--r--src/routes/(app)/[username]/generate/+server.js16
-rw-r--r--src/routes/(app)/[username]/invoice/+server.js8
-rw-r--r--src/routes/(app)/[username]/invoice/[id]/+layout.server.js52
-rw-r--r--src/routes/(app)/[username]/invoice/[id]/+page.server.js4
-rw-r--r--src/routes/(app)/[username]/invoice/[id]/+page.svelte388
-rw-r--r--src/routes/(app)/[username]/invoice/[id]/paid/+page.svelte108
-rw-r--r--src/routes/(app)/[username]/invoice/[id]/tip/+page.server.js11
-rw-r--r--src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte326
-rw-r--r--src/routes/(app)/[username]/invoice/sent/+page.svelte8
-rw-r--r--src/routes/(app)/[username]/map/+page.server.js12
-rw-r--r--src/routes/(app)/[username]/map/+page.svelte6
-rw-r--r--src/routes/(app)/[username]/messages/+page.server.js31
-rw-r--r--src/routes/(app)/[username]/messages/+page.svelte295
-rw-r--r--src/routes/(app)/[username]/payments/[...page]/+page.server.js30
-rw-r--r--src/routes/(app)/[username]/payments/[...page]/+page.svelte464
-rw-r--r--src/routes/(app)/[username]/payments/[...page]/+server.js22
-rw-r--r--src/routes/(app)/[username]/receive/[...param]/+page.server.js14
-rw-r--r--src/routes/(app)/[username]/receive/[...param]/+page.svelte76
-rw-r--r--src/routes/(app)/[username]/request/[id]/+page.server.js35
-rw-r--r--src/routes/(app)/[username]/request/[id]/+page.svelte85
-rw-r--r--src/routes/(app)/[username]/reset/+server.js14
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/+page.server.js31
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/+page.svelte332
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/_account.svelte302
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/_pos.svelte84
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/_security.svelte509
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/_shopify.svelte129
-rw-r--r--src/routes/(app)/[username]/shopify.js/+server.js14
-rw-r--r--src/routes/(app)/admin/+layout.server.js14
-rw-r--r--src/routes/(app)/admin/+layout.svelte8
-rw-r--r--src/routes/(app)/admin/+page.server.js14
-rw-r--r--src/routes/(app)/admin/+page.svelte34
-rw-r--r--src/routes/(app)/buy/+page.server.js43
-rw-r--r--src/routes/(app)/buy/+page.svelte230
-rw-r--r--src/routes/(app)/disable2fa/+server.js18
-rw-r--r--src/routes/(app)/enable2fa/+server.js8
-rw-r--r--src/routes/(app)/events/+server.js6
-rw-r--r--src/routes/(app)/fund/[id]/+page.server.js28
-rw-r--r--src/routes/(app)/fund/[id]/+page.svelte256
-rw-r--r--src/routes/(app)/fund/[id]/sweep/+page.server.js17
-rw-r--r--src/routes/(app)/fund/[id]/sweep/+page.svelte54
-rw-r--r--src/routes/(app)/fund/[id]/withdraw/+page.server.js26
-rw-r--r--src/routes/(app)/fund/[id]/withdraw/+page.svelte82
-rw-r--r--src/routes/(app)/fund/[id]/withdraw/+server.js6
-rw-r--r--src/routes/(app)/launch/+page.server.js6
-rw-r--r--src/routes/(app)/launch/+page.svelte187
-rw-r--r--src/routes/(app)/launch/purchase/+page.server.js68
-rw-r--r--src/routes/(app)/launch/purchase/+page.svelte114
-rw-r--r--src/routes/(app)/launch/purchase/card/+page.server.js72
-rw-r--r--src/routes/(app)/launch/purchase/card/+page.svelte134
-rw-r--r--src/routes/(app)/launch/thanks/[asset]/+page.svelte40
-rw-r--r--src/routes/(app)/launch/ticket/+page.server.js14
-rw-r--r--src/routes/(app)/launch/ticket/+page.svelte56
-rw-r--r--src/routes/(app)/ln/[lnurl]/+page.server.js96
-rw-r--r--src/routes/(app)/ln/[lnurl]/+page.svelte14
-rw-r--r--src/routes/(app)/ln/withdrawal/sent/+page.svelte31
-rw-r--r--src/routes/(app)/locations/+server.js20
-rw-r--r--src/routes/(app)/login/+page.server.js36
-rw-r--r--src/routes/(app)/login/+page.svelte20
-rw-r--r--src/routes/(app)/logout/+page.server.js12
-rw-r--r--src/routes/(app)/otpsecret/+server.js26
-rw-r--r--src/routes/(app)/password/+server.js8
-rw-r--r--src/routes/(app)/payment/[id]/+page.server.js6
-rw-r--r--src/routes/(app)/payment/[id]/+page.svelte289
-rw-r--r--src/routes/(app)/payment/[id]/plain/+page.server.js6
-rw-r--r--src/routes/(app)/payment/[id]/plain/+page.svelte132
-rw-r--r--src/routes/(app)/payment/[id]/print/+server.js8
-rw-r--r--src/routes/(app)/pin/+server.js8
-rw-r--r--src/routes/(app)/qr/[text]/+page.server.js8
-rw-r--r--src/routes/(app)/qr/[text]/+page.svelte18
-rw-r--r--src/routes/(app)/redeem/[redeemcode]/+page.server.js24
-rw-r--r--src/routes/(app)/redeem/[redeemcode]/+page.svelte4
-rw-r--r--src/routes/(app)/register/+page.server.js57
-rw-r--r--src/routes/(app)/register/+page.svelte510
-rw-r--r--src/routes/(app)/scan/+page.svelte63
-rw-r--r--src/routes/(app)/send/+layout.svelte14
-rw-r--r--src/routes/(app)/send/[...text]/+page.server.js150
-rw-r--r--src/routes/(app)/send/[...text]/+page.svelte212
-rw-r--r--src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js45
-rw-r--r--src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte247
-rw-r--r--src/routes/(app)/send/fund/[name]/[...amount]/+page.server.js24
-rw-r--r--src/routes/(app)/send/fund/[name]/[...amount]/+page.svelte82
-rw-r--r--src/routes/(app)/send/invoice/[id]/+page.server.js65
-rw-r--r--src/routes/(app)/send/invoice/[id]/+page.svelte272
-rw-r--r--src/routes/(app)/send/lightning/[payreq]/+page.server.js30
-rw-r--r--src/routes/(app)/send/lightning/[payreq]/+page.svelte181
-rw-r--r--src/routes/(app)/send/user/[username]/[...amount]/+page.server.js67
-rw-r--r--src/routes/(app)/send/user/[username]/[...amount]/+page.svelte166
-rw-r--r--src/routes/(app)/sent/+page.server.js6
-rw-r--r--src/routes/(app)/sent/+page.svelte73
-rw-r--r--src/routes/(app)/support/+page.svelte214
-rw-r--r--src/routes/(app)/support/+server.js8
-rw-r--r--src/routes/(app)/withdraw/+server.js11
-rw-r--r--src/routes/(static)/+layout.server.js22
-rw-r--r--src/routes/(static)/+layout.svelte10
-rw-r--r--src/routes/(static)/bounties/+page.svelte174
-rw-r--r--src/routes/(static)/docs/+page.server.js4
-rw-r--r--src/routes/(static)/docs/+page.svelte201
-rw-r--r--src/routes/+layout.server.js26
-rw-r--r--src/routes/+layout.svelte57
-rw-r--r--src/routes/+page.server.js2
-rw-r--r--src/routes/+page.svelte182
-rw-r--r--src/routes/about/+server.js4
-rw-r--r--src/routes/map/+page.server.js12
-rw-r--r--src/routes/map/+page.svelte22
126 files changed, 5666 insertions, 5206 deletions
diff --git a/src/routes/(app)/+error.svelte b/src/routes/(app)/+error.svelte
index 36726e26..ed0bec3e 100644
--- a/src/routes/(app)/+error.svelte
+++ b/src/routes/(app)/+error.svelte
@@ -1,38 +1,38 @@
<script>
- import { back } from '$lib/utils';
- import { Icon } from '$comp';
- import { page } from '$app/stores';
+ import { back } from "$lib/utils";
+ import { Icon } from "$comp";
+ import { page } from "$app/stores";
- export let data;
- let { user } = data;
+ export let data;
+ let { user } = data;
- let link = user ? `/${user.username}` : '/';
+ let link = user ? `/${user.username}` : "/";
</script>
<div class="container px-4 max-w-xl mx-auto mt-10 space-y-5">
- <div class="w-full flex">
- <a href="/" class="mx-auto">
- <Icon icon="logo" />
- </a>
- </div>
+ <div class="w-full flex">
+ <a href="/" class="mx-auto">
+ <Icon icon="logo" />
+ </a>
+ </div>
- <div class="flex justify-center items-center">
- <div class="shadow-xl rounded-3xl px-10 pt-5 pb-10 space-y-5 w-full mx-5">
- <div class="relative">
- <h1 class="text-2xl md:text-3xl font-semibold text-center">Error</h1>
- </div>
- <p class="text-secondary text-center">Something went wrong</p>
- <p class="text-secondary text-center">{$page.error.message}</p>
+ <div class="flex justify-center items-center">
+ <div class="shadow-xl rounded-3xl px-10 pt-5 pb-10 space-y-5 w-full mx-5">
+ <div class="relative">
+ <h1 class="text-2xl md:text-3xl font-semibold text-center">Error</h1>
+ </div>
+ <p class="text-secondary text-center">Something went wrong</p>
+ <p class="text-secondary text-center">{$page.error.message}</p>
- <div class="flex justify-center py-24">
- <a href={link}>
- <button
- class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 text-xl"
- >
- Back
- </button>
- </a>
- </div>
- </div>
- </div>
+ <div class="flex justify-center py-24">
+ <a href={link}>
+ <button
+ class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 text-xl"
+ >
+ Back
+ </button>
+ </a>
+ </div>
+ </div>
+ </div>
</div>
diff --git a/src/routes/(app)/+layout.server.js b/src/routes/(app)/+layout.server.js
index e80e1e64..9fcf6411 100644
--- a/src/routes/(app)/+layout.server.js
+++ b/src/routes/(app)/+layout.server.js
@@ -1,44 +1,48 @@
-import { error, redirect } from '@sveltejs/kit';
-import { get, post, auth, protectedRoutes } from '$lib/utils';
-import { PUBLIC_BTC as btc } from '$env/static/public';
+import { error, redirect } from "@sveltejs/kit";
+import { get, post, auth, protectedRoutes } from "$lib/utils";
+import { PUBLIC_BTC as btc } from "$env/static/public";
export async function load({ cookies, request, url, params }) {
- let { pathname } = url;
- let token = cookies.get('token');
- let rate,
- rates = { USD: 1 };
+ let { pathname } = url;
+ let token = cookies.get("token");
+ let rate,
+ rates = { USD: 1 };
- let user;
- if (token) {
- try {
- user = await get('/me', auth(cookies));
- } catch (e) {}
- }
+ let user;
+ if (token) {
+ try {
+ user = await get("/me", auth(cookies));
+ } catch (e) {}
+ }
- if (user?.needsMigration) {
- throw redirect(307, `/migrate`);
- }
+ if (user?.needsMigration) {
+ throw redirect(307, `/migrate`);
+ }
- if (user && ['/login', '/register'].includes(pathname) && request.method === 'GET') {
- throw redirect(307, `/${user.username}`);
- }
+ if (
+ user &&
+ ["/login", "/register"].includes(pathname) &&
+ request.method === "GET"
+ ) {
+ throw redirect(307, `/${user.username}`);
+ }
- if (
- protectedRoutes.find((p) => pathname.match(p)) &&
- (!user || user.username !== params.username)
- ) {
- throw redirect(307, '/login');
- }
+ if (
+ protectedRoutes.find((p) => pathname.match(p)) &&
+ (!user || user.username !== params.username)
+ ) {
+ throw redirect(307, "/login");
+ }
- if (user && !user.pubkey && !pathname.includes('generate'))
- throw redirect(307, `/${user.username}/generate`);
+ if (user && !user.pubkey && !pathname.includes("generate"))
+ throw redirect(307, `/${user.username}/generate`);
- try {
- rate = await get('/rate');
- rates = await get('/rates');
- } catch (e) {
- console.log(e);
- }
+ try {
+ rate = await get("/rate");
+ rates = await get("/rates");
+ } catch (e) {
+ console.log(e);
+ }
- return { user, token, rate, rates };
+ return { user, token, rate, rates };
}
diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte
index 51ade9c7..c5a312e1 100644
--- a/src/routes/(app)/+layout.svelte
+++ b/src/routes/(app)/+layout.svelte
@@ -1,167 +1,174 @@
<script>
- import { SvelteToast } from '@zerodevx/svelte-toast';
- import { onDestroy, onMount } from 'svelte';
- import { close, connect, send } from '$lib/socket';
- import { last, invoice, request, ndef, passwordPrompt, pin } from '$lib/store';
- import { page } from '$app/stores';
- import { browser } from '$app/environment';
- import { goto } from '$app/navigation';
- import { LoadingSplash, Invoice, Password, Request } from '$comp';
- import { warning, protectedRoutes } from '$lib/utils';
- import { t, locale } from '$lib/translations';
- import { onNavigate } from '$app/navigation';
+ import { SvelteToast } from "@zerodevx/svelte-toast";
+ import { onDestroy, onMount } from "svelte";
+ import { close, connect, send } from "$lib/socket";
+ import {
+ last,
+ invoice,
+ request,
+ ndef,
+ passwordPrompt,
+ pin,
+ } from "$lib/store";
+ import { page } from "$app/stores";
+ import { browser } from "$app/environment";
+ import { goto } from "$app/navigation";
+ import { LoadingSplash, Invoice, Password, Request } from "$comp";
+ import { warning, protectedRoutes } from "$lib/utils";
+ import { t, locale } from "$lib/translations";
+ import { onNavigate } from "$app/navigation";
- export let data;
+ export let data;
- onNavigate((navigation) => {
- if (!document.startViewTransition) return;
+ onNavigate((navigation) => {
+ if (!document.startViewTransition) return;
- return new Promise((resolve) => {
- document.startViewTransition(async () => {
- resolve();
- await navigation.complete;
- });
- });
- });
+ return new Promise((resolve) => {
+ document.startViewTransition(async () => {
+ resolve();
+ await navigation.complete;
+ });
+ });
+ });
- let { rate, user, subject, token, rates } = data;
+ let { rate, user, subject, token, rates } = data;
- $: update(data);
- let update = (data) => {
- ({ rate, user, subject, token, rates } = data);
- };
+ $: update(data);
+ let update = (data) => {
+ ({ rate, user, subject, token, rates } = data);
+ };
- onMount(async () => {
- let localeLocalStorageKey = 'sveltekit-i18n-locale';
- let localStorageLocale = localStorage.getItem(localeLocalStorageKey);
- if (localStorageLocale) locale.set(localStorageLocale);
+ onMount(async () => {
+ let localeLocalStorageKey = "sveltekit-i18n-locale";
+ let localStorageLocale = localStorage.getItem(localeLocalStorageKey);
+ if (localStorageLocale) locale.set(localStorageLocale);
- locale.subscribe((lng) => {
- if (lng) localStorage.setItem(localeLocalStorageKey, lng);
- });
+ locale.subscribe((lng) => {
+ if (lng) localStorage.setItem(localeLocalStorageKey, lng);
+ });
- if (browser) {
- checkSocket();
- let locktime = user && user.locktime ? user.locktime : 300;
- expireTimer = setTimeout(expirePin, locktime * 1000);
+ if (browser) {
+ checkSocket();
+ let locktime = user && user.locktime ? user.locktime : 300;
+ expireTimer = setTimeout(expirePin, locktime * 1000);
- // if (window.NDEFReader) {
- // try {
- // $ndef = new NDEFReader();
- // await $ndef.scan();
- //
- // $ndef.addEventListener('readingerror', (e) => {
- // console.log('nfc error', e);
- // });
- //
- // $ndef.addEventListener('reading', ({ message, serialNumber }) => {
- // goto(`/${user.username}/card/${serialNumber.replace(/:/g, '-')}`);
- // });
- // } catch (error) {
- // console.log('Argh! ' + error);
- // }
- // }
- }
- });
+ // if (window.NDEFReader) {
+ // try {
+ // $ndef = new NDEFReader();
+ // await $ndef.scan();
+ //
+ // $ndef.addEventListener('readingerror', (e) => {
+ // console.log('nfc error', e);
+ // });
+ //
+ // $ndef.addEventListener('reading', ({ message, serialNumber }) => {
+ // goto(`/${user.username}/card/${serialNumber.replace(/:/g, '-')}`);
+ // });
+ // } catch (error) {
+ // console.log('Argh! ' + error);
+ // }
+ // }
+ }
+ });
- $: browser && connect(token);
+ $: browser && connect(token);
- let lost,
- checkTimer,
- expireTimer,
- counter = 0;
+ let lost,
+ checkTimer,
+ expireTimer,
+ counter = 0;
- let checkSocket = () => {
- counter++;
- lost = Date.now() - $last > 30000;
- if (lost) connect(token);
- if (counter > 5) {
- send('heartbeat', token);
- counter = 0;
- }
+ let checkSocket = () => {
+ counter++;
+ lost = Date.now() - $last > 30000;
+ if (lost) connect(token);
+ if (counter > 5) {
+ send("heartbeat", token);
+ counter = 0;
+ }
- checkTimer = setTimeout(checkSocket, 1000);
- };
+ checkTimer = setTimeout(checkSocket, 1000);
+ };
- let expirePin = () => {
- $pin = null;
- expireTimer = setTimeout(expirePin, user.locktime * 1000 || 300000);
- };
+ let expirePin = () => {
+ $pin = null;
+ expireTimer = setTimeout(expirePin, user.locktime * 1000 || 300000);
+ };
- onDestroy(() => {
- if (browser) {
- close();
- clearTimeout(checkTimer);
- clearTimeout(expireTimer);
- }
- });
+ onDestroy(() => {
+ if (browser) {
+ close();
+ clearTimeout(checkTimer);
+ clearTimeout(expireTimer);
+ }
+ });
</script>
{#if browser && $passwordPrompt}
- <Password {user} />
+ <Password {user} />
{/if}
<svelte:head>
- {#if subject}
- <title>coinos - {subject.username}</title>
- <meta name="lightning" content={`lnurlp:${subject.username}@coinos.io`} />
- {:else}
- <title>coinos</title>
- {/if}
+ {#if subject}
+ <title>coinos - {subject.username}</title>
+ <meta name="lightning" content={`lnurlp:${subject.username}@coinos.io`} />
+ {:else}
+ <title>coinos</title>
+ {/if}
- {#if subject?.profile}
- <meta name="og:image" content={`/api/public/${subject.profile}.webp`} />
- {:else}
- <meta property="og:image" content="/icons/logo.svg" />
- {/if}
+ {#if subject?.profile}
+ <meta name="og:image" content={`/api/public/${subject.profile}.webp`} />
+ {:else}
+ <meta property="og:image" content="/icons/logo.svg" />
+ {/if}
</svelte:head>
<SvelteToast options={{ reversed: true, intro: { y: 192 } }} />
<main class="pb-20">
- <slot />
+ <slot />
</main>
{#if $invoice}
- <Invoice {user} />
+ <Invoice {user} />
{/if}
{#if $request}
- <Request {user} />
+ <Request {user} />
{/if}
<style>
- @keyframes fade-in {
- from {
- opacity: 0;
- }
- }
+ @keyframes fade-in {
+ from {
+ opacity: 0;
+ }
+ }
- @keyframes fade-out {
- to {
- opacity: 0;
- }
- }
+ @keyframes fade-out {
+ to {
+ opacity: 0;
+ }
+ }
- @keyframes slide-from-right {
- from {
- transform: translateX(60px);
- }
- }
+ @keyframes slide-from-right {
+ from {
+ transform: translateX(60px);
+ }
+ }
- @keyframes slide-to-left {
- to {
- transform: translateX(-60px);
- }
- }
+ @keyframes slide-to-left {
+ to {
+ transform: translateX(-60px);
+ }
+ }
- :root::view-transition-old(root) {
- animation: 280ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
- 180ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
- }
+ :root::view-transition-old(root) {
+ animation: 280ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
+ 180ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
+ }
- :root::view-transition-new(root) {
- animation: 280ms cubic-bezier(0, 0, 0.2, 1) 80ms both fade-in,
- 180ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
- }
+ :root::view-transition-new(root) {
+ animation: 280ms cubic-bezier(0, 0, 0.2, 1) 80ms both fade-in,
+ 180ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
+ }
</style>
diff --git a/src/routes/(app)/[username]/(profile)/+layout.server.js b/src/routes/(app)/[username]/(profile)/+layout.server.js
index f4f2ca0a..a508de92 100644
--- a/src/routes/(app)/[username]/(profile)/+layout.server.js
+++ b/src/routes/(app)/[username]/(profile)/+layout.server.js
@@ -1,26 +1,26 @@
-import { get } from '$lib/utils';
+import { get } from "$lib/utils";
export async function load({ parent }) {
- let { subject, user } = await parent();
+ let { subject, user } = await parent();
- subject.follows = [];
- subject.followers = [];
+ subject.follows = [];
+ subject.followers = [];
- try {
- subject.follows = await get(`/${subject.pubkey}/follows`);
- } catch (e) {
- console.log('problem fetching follows', e);
- }
+ try {
+ subject.follows = await get(`/${subject.pubkey}/follows`);
+ } catch (e) {
+ console.log("problem fetching follows", e);
+ }
- try {
- subject.followers = await get(`/${subject.pubkey}/followers`);
- } catch (e) {
- console.log('problem fetching followers', e);
- }
+ try {
+ subject.followers = await get(`/${subject.pubkey}/followers`);
+ } catch (e) {
+ console.log("problem fetching followers", e);
+ }
- if (user) {
- user.follows = await get(`/${user.pubkey}/follows?tagsonly=true`);
- }
+ if (user) {
+ user.follows = await get(`/${user.pubkey}/follows?tagsonly=true`);
+ }
- return { subject, user };
+ return { subject, user };
}
diff --git a/src/routes/(app)/[username]/(profile)/+layout.svelte b/src/routes/(app)/[username]/(profile)/+layout.svelte
index 55c22bdb..6b166e2c 100644
--- a/src/routes/(app)/[username]/(profile)/+layout.svelte
+++ b/src/routes/(app)/[username]/(profile)/+layout.svelte
@@ -1,236 +1,246 @@
<script>
- import { hexToUint8Array } from 'uint8array-extras';
- import { copy, f, s, sats } from '$lib/utils';
- import { animatedRate } from '$lib/store';
- import { Icon } from '$comp';
- import { t } from '$lib/translations';
- import { sign, send } from '$lib/nostr';
- import { bech32m } from '@scure/base';
- import { page } from '$app/stores';
+ import { hexToUint8Array } from "uint8array-extras";
+ import { copy, f, s, sats } from "$lib/utils";
+ import { animatedRate } from "$lib/store";
+ import { Icon } from "$comp";
+ import { t } from "$lib/translations";
+ import { sign, send } from "$lib/nostr";
+ import { bech32m } from "@scure/base";
+ import { page } from "$app/stores";
- export let data;
+ export let data;
- let { encode, toWords } = bech32m;
- let events, user, subject, rates, src, text;
+ let { encode, toWords } = bech32m;
+ let events, user, subject, rates, src, text;
- $: ({ events, user, rates, subject, src, text } = data);
- $: ({ currency, username: n, display } = subject);
+ $: ({ events, user, rates, subject, src, text } = data);
+ $: ({ currency, username: n, display } = subject);
- $: username = n.length > 60 ? n.substr(0, 6) : display || n;
- $: npub = bech32m.encode('npub', toWords(hexToUint8Array(subject.pubkey)), 180);
- $: lnaddr = `${n}@${$page.url.host}`;
- $: profile = `${$page.url.host}/${n}`;
+ $: username = n.length > 60 ? n.substr(0, 6) : display || n;
+ $: npub = bech32m.encode(
+ "npub",
+ toWords(hexToUint8Array(subject.pubkey)),
+ 180
+ );
+ $: lnaddr = `${n}@${$page.url.host}`;
+ $: profile = `${$page.url.host}/${n}`;
- let follow = async () => {
- user.follows.push(['p', subject.pubkey, 'wss://nostr.coinos.io', subject.username]);
- update();
- };
+ let follow = async () => {
+ user.follows.push([
+ "p",
+ subject.pubkey,
+ "wss://nostr.coinos.io",
+ subject.username,
+ ]);
+ update();
+ };
- let unfollow = async () => {
- user.follows.splice(
- user.follows.findIndex((t) => t[1] === subject.pubkey),
- 1
- );
- update();
- };
+ let unfollow = async () => {
+ user.follows.splice(
+ user.follows.findIndex((t) => t[1] === subject.pubkey),
+ 1
+ );
+ update();
+ };
- let update = async () => {
- let event = {
- pubkey: user.pubkey,
- created_at: Math.floor(Date.now() / 1000),
- kind: 3,
- content: '',
- tags: user.follows
- };
+ let update = async () => {
+ let event = {
+ pubkey: user.pubkey,
+ created_at: Math.floor(Date.now() / 1000),
+ kind: 3,
+ content: "",
+ tags: user.follows,
+ };
- await sign({ event, user });
- await send(event);
+ await sign({ event, user });
+ await send(event);
- user.follows = user.follows;
- };
+ user.follows = user.follows;
+ };
- $: following = !!user?.follows.find((t) => t.includes(subject.pubkey));
+ $: following = !!user?.follows.find((t) => t.includes(subject.pubkey));
- let showBio;
- let toggleBio = () => (showBio = !showBio);
+ let showBio;
+ let toggleBio = () => (showBio = !showBio);
- let showDetails;
- let toggleDetails = () => (showDetails = !showDetails);
+ let showDetails;
+ let toggleDetails = () => (showDetails = !showDetails);
</script>
<div class="container mx-auto w-full px-4 flex flex-wrap lg:flex-nowrap">
- <div class="hidden lg:block lg:w-[300px]" />
- <div class="lg:w-[300px] lg:absolute space-y-5 left-20 mx-auto max-w-sm">
- <div class="flex text-3xl font-bold text-center mx-auto justify-center gap-2">
- <div class="my-auto">{display || username}</div>
- <button on:click={toggleDetails}>
- <Icon icon="stats" style="w-12" />
- </button>
- </div>
+ <div class="hidden lg:block lg:w-[300px]" />
+ <div class="lg:w-[300px] lg:absolute space-y-5 left-20 mx-auto max-w-sm">
+ <div
+ class="flex text-3xl font-bold text-center mx-auto justify-center gap-2"
+ >
+ <div class="my-auto">{display || username}</div>
+ <button on:click={toggleDetails}>
+ <Icon icon="stats" style="w-12" />
+ </button>
+ </div>
- {#if showDetails}
- <div>
- <div class="font-bold">{$t('user.profilePage')}</div>
- <div class="flex gap-4">
- <div class="break-all grow text-secondary text-lg">
- {profile}
- </div>
- <div class="flex mb-auto gap-1">
- <button class="my-auto" on:click={() => copy(profile)}
- ><Icon icon="copy" style="max-w-max min-w-[32px]" /></button
- >
- <a
- href={`/qr/${encodeURIComponent(profile)}`}
- class="my-auto"
- >
- <Icon icon="qr" style="invert max-w-max min-w-[32px]" />
- </a>
- </div>
- </div>
- </div>
- <div>
- <div class="font-bold">{$t('user.lightningAddress')}</div>
- <div class="flex gap-4">
- <div class="break-all grow text-secondary text-lg">
- {lnaddr}
- </div>
- <div class="flex mb-auto gap-1">
- <button class="my-auto" on:click={() => copy(lnaddr)}
- ><Icon icon="copy" style="max-w-max min-w-[32px]" /></button
- >
- <a
- href={`/qr/${encodeURIComponent(lnaddr)}`}
- class="my-auto"
- >
- <Icon icon="qr" style="invert max-w-max min-w-[32px]" />
- </a>
- </div>
- </div>
- </div>
- <div>
- <div class="font-bold">{$t('user.nostrPubkey')}</div>
- <div class="flex gap-4">
- <div class="break-all grow text-secondary text-lg">
- {npub}
- </div>
- <div class="flex my-auto gap-1">
- <button class="my-auto" on:click={() => copy(npub)}
- ><Icon icon="copy" style="max-w-max min-w-[32px]" /></button
- >
- <a
- href={`/qr/${encodeURIComponent(npub)}`}
- class="my-auto"
- >
- <Icon icon="qr" style="invert max-w-max min-w-[32px]" />
- </a>
- </div>
- </div>
- </div>
- {/if}
+ {#if showDetails}
+ <div>
+ <div class="font-bold">{$t("user.profilePage")}</div>
+ <div class="flex gap-4">
+ <div class="break-all grow text-secondary text-lg">
+ {profile}
+ </div>
+ <div class="flex mb-auto gap-1">
+ <button class="my-auto" on:click={() => copy(profile)}
+ ><Icon icon="copy" style="max-w-max min-w-[32px]" /></button
+ >
+ <a href={`/qr/${encodeURIComponent(profile)}`} class="my-auto">
+ <Icon icon="qr" style="invert max-w-max min-w-[32px]" />
+ </a>
+ </div>
+ </div>
+ </div>
+ <div>
+ <div class="font-bold">{$t("user.lightningAddress")}</div>
+ <div class="flex gap-4">
+ <div class="break-all grow text-secondary text-lg">
+ {lnaddr}
+ </div>
+ <div class="flex mb-auto gap-1">
+ <button class="my-auto" on:click={() => copy(lnaddr)}
+ ><Icon icon="copy" style="max-w-max min-w-[32px]" /></button
+ >
+ <a href={`/qr/${encodeURIComponent(lnaddr)}`} class="my-auto">
+ <Icon icon="qr" style="invert max-w-max min-w-[32px]" />
+ </a>
+ </div>
+ </div>
+ </div>
+ <div>
+ <div class="font-bold">{$t("user.nostrPubkey")}</div>
+ <div class="flex gap-4">
+ <div class="break-all grow text-secondary text-lg">
+ {npub}
+ </div>
+ <div class="flex my-auto gap-1">
+ <button class="my-auto" on:click={() => copy(npub)}
+ ><Icon icon="copy" style="max-w-max min-w-[32px]" /></button
+ >
+ <a href={`/qr/${encodeURIComponent(npub)}`} class="my-auto">
+ <Icon icon="qr" style="invert max-w-max min-w-[32px]" />
+ </a>
+ </div>
+ </div>
+ </div>
+ {/if}
- {#if subject.address && subject.id !== user?.id}
- <div
- class="text-secondary mx-auto text-center lg:text-left lg:mx-0"
- class:line-clamp-3={showBio}
- on:click={toggleBio}
- on:keydown={toggleBio}
- >
- {subject.address}
- </div>
- {/if}
+ {#if subject.address && subject.id !== user?.id}
+ <div
+ class="text-secondary mx-auto text-center lg:text-left lg:mx-0"
+ class:line-clamp-3={showBio}
+ on:click={toggleBio}
+ on:keydown={toggleBio}
+ >
+ {subject.address}
+ </div>
+ {/if}
- <div class="flex justify-center gap-2">
- <a href={`/${subject.pubkey}/follows`}
- ><b>{subject.follows.length}</b>
- <span class="text-secondary">{$t('user.following')}</span></a
- >
- <a href={`/${subject.pubkey}/followers`}
- ><b>{subject.followers.length}</b>
- <span class="text-secondary">{$t('user.followers')}</span></a
- >
- </div>
+ <div class="flex justify-center gap-2">
+ <a href={`/${subject.pubkey}/follows`}
+ ><b>{subject.follows.length}</b>
+ <span class="text-secondary">{$t("user.following")}</span></a
+ >
+ <a href={`/${subject.pubkey}/followers`}
+ ><b>{subject.followers.length}</b>
+ <span class="text-secondary">{$t("user.followers")}</span></a
+ >
+ </div>
- <div class="flex flex-wrap gap-2 w-full">
- {#if user && user.username !== subject.username && subject.pubkey}
- {#if following}
- <div class="w-full flex">
- <button
- class="mx-auto rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60 bg-black text-white"
- on:click={unfollow}
- >
- <div class="mx-auto flex">
- <Icon icon={'profile'} style="my-auto h-6 mr-2 invert" />
- <div class="my-auto">{$t('user.following')}</div>
- </div>
- </button>
- </div>
- {:else}
- <div class="w-full flex">
- <button
- class="mx-auto rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60"
- on:click={follow}
- >
- <div class="mx-auto flex">
- <Icon icon={'profile'} style="my-auto h-6 mr-2" />
- <div class="my-auto">{$t('user.follow')}</div>
- </div>
- </button>
- </div>
- {/if}
- {/if}
+ <div class="flex flex-wrap gap-2 w-full">
+ {#if user && user.username !== subject.username && subject.pubkey}
+ {#if following}
+ <div class="w-full flex">
+ <button
+ class="mx-auto rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60 bg-black text-white"
+ on:click={unfollow}
+ >
+ <div class="mx-auto flex">
+ <Icon icon={"profile"} style="my-auto h-6 mr-2 invert" />
+ <div class="my-auto">{$t("user.following")}</div>
+ </div>
+ </button>
+ </div>
+ {:else}
+ <div class="w-full flex">
+ <button
+ class="mx-auto rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60"
+ on:click={follow}
+ >
+ <div class="mx-auto flex">
+ <Icon icon={"profile"} style="my-auto h-6 mr-2" />
+ <div class="my-auto">{$t("user.follow")}</div>
+ </div>
+ </button>
+ </div>
+ {/if}
+ {/if}
- {#if user && user.username !== subject.username && subject.pubkey}
- <div class="w-full flex">
- <a href={`/${subject.username}/messages`} class="mx-auto">
- <button class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60">
- <div class="mx-auto flex">
- <Icon icon="message" style="mr-2 my-auto" />
- <div class="mt-1 my-auto">{$t('user.message')}</div>
- </div>
- </button>
- </a>
- </div>
- {/if}
+ {#if user && user.username !== subject.username && subject.pubkey}
+ <div class="w-full flex">
+ <a href={`/${subject.username}/messages`} class="mx-auto">
+ <button
+ class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60"
+ >
+ <div class="mx-auto flex">
+ <Icon icon="message" style="mr-2 my-auto" />
+ <div class="mt-1 my-auto">{$t("user.message")}</div>
+ </div>
+ </button>
+ </a>
+ </div>
+ {/if}
- {#if !subject.anon && subject.username !== user?.username}
- <div class="w-full flex">
- <a
- href={user ? `/send/${subject.username}` : `/${subject.username}/receive`}
- class="mx-auto"
- >
- <button class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60">
- <div class="mx-auto flex">
- <Icon icon="send" style="mr-2" />
- <div class="mt-1">{$t('user.pay')}</div>
- </div>
- </button>
- </a>
- </div>
- {/if}
- </div>
- </div>
+ {#if !subject.anon && subject.username !== user?.username}
+ <div class="w-full flex">
+ <a
+ href={user
+ ? `/send/${subject.username}`
+ : `/${subject.username}/receive`}
+ class="mx-auto"
+ >
+ <button
+ class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60"
+ >
+ <div class="mx-auto flex">
+ <Icon icon="send" style="mr-2" />
+ <div class="mt-1">{$t("user.pay")}</div>
+ </div>
+ </button>
+ </a>
+ </div>
+ {/if}
+ </div>
+ </div>
- <div class="w-full">
- <div class="mx-auto space-y-5 lg:max-w-xl lg:pl-10 xl:max-w-2xl lg:pl-10">
- <slot />
- </div>
- </div>
+ <div class="w-full">
+ <div class="mx-auto space-y-5 lg:max-w-xl lg:pl-10 xl:max-w-2xl lg:pl-10">
+ <slot />
+ </div>
+ </div>
</div>
{#if currency && !isNaN($animatedRate)}
- <div class="flex fixed w-full px-4 bg-white py-2 pb-3 bottom-0 bg-opacity-90 tabular-nums">
- <div class="text-secondary flex mr-auto">
- <div class="flex mr-1">
- <div class="my-auto mr-1">1</div>
- <img src="/images/bitcoin.svg" class="w-5 my-auto" alt="Bitcoin" />
- </div>
- <div>= {f($animatedRate, currency)}</div>
- </div>
- <div class="text-secondary flex ml-auto">
- <div class="flex">
- <div class="mr-1">⚡️{s((1 * sats) / $animatedRate)} =</div>
- <div>{f(1, currency)}</div>
- </div>
- </div>
- </div>
+ <div
+ class="flex fixed w-full px-4 bg-white py-2 pb-3 bottom-0 bg-opacity-90 tabular-nums"
+ >
+ <div class="text-secondary flex mr-auto">
+ <div class="flex mr-1">
+ <div class="my-auto mr-1">1</div>
+ <img src="/images/bitcoin.svg" class="w-5 my-auto" alt="Bitcoin" />
+ </div>
+ <div>= {f($animatedRate, currency)}</div>
+ </div>
+ <div class="text-secondary flex ml-auto">
+ <div class="flex">
+ <div class="mr-1">⚡️{s((1 * sats) / $animatedRate)} =</div>
+ <div>{f(1, currency)}</div>
+ </div>
+ </div>
+ </div>
{/if}
diff --git a/src/routes/(app)/[username]/(profile)/+page.server.js b/src/routes/(app)/[username]/(profile)/+page.server.js
index e37db1fe..3110ec40 100644
--- a/src/routes/(app)/[username]/(profile)/+page.server.js
+++ b/src/routes/(app)/[username]/(profile)/+page.server.js
@@ -1,35 +1,35 @@
-import { auth, get } from '$lib/utils';
+import { auth, get } from "$lib/utils";
export async function load({ cookies, params, parent, url }) {
- let { user, subject } = await parent();
- let { pubkey } = subject;
- let { since = 0 } = params;
+ let { user, subject } = await parent();
+ let { pubkey } = subject;
+ let { since = 0 } = params;
- let messages, invoices, sent, received, notes;
- messages = invoices = sent = received = notes = [];
+ let messages, invoices, sent, received, notes;
+ messages = invoices = sent = received = notes = [];
- if (pubkey) {
- if (user) {
- try {
- messages = await get(`/${user.pubkey}/${since}/messages`);
- messages = messages.sort((a, b) => b.created_at - a.created_at);
- } catch (e) {
- console.log(`failed to fetch nostr messages`, e);
- }
+ if (pubkey) {
+ if (user) {
+ try {
+ messages = await get(`/${user.pubkey}/${since}/messages`);
+ messages = messages.sort((a, b) => b.created_at - a.created_at);
+ } catch (e) {
+ console.log(`failed to fetch nostr messages`, e);
+ }
- ({ invoices, sent, received } = await get('/requests', auth(cookies)));
- }
+ ({ invoices, sent, received } = await get("/requests", auth(cookies)));
+ }
- try {
- notes = await get(`/${pubkey}/notes`);
- } catch (e) {
- console.log(`failed to fetch nostr notes for ${pubkey}`, e);
- }
+ try {
+ notes = await get(`/${pubkey}/notes`);
+ } catch (e) {
+ console.log(`failed to fetch nostr notes for ${pubkey}`, e);
+ }
- notes.map((e) => {
- e.seen = e.created_at;
- });
- }
+ notes.map((e) => {
+ e.seen = e.created_at;
+ });
+ }
- return { invoices, messages, notes, sent, received };
+ return { invoices, messages, notes, sent, received };
}
diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte
index 32a1f2cb..b0e36090 100644
--- a/src/routes/(app)/[username]/(profile)/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/+page.svelte
@@ -1,227 +1,245 @@
<script>
- import { onMount } from 'svelte';
- import { Icon, Avatar, Balance, Feed } from '$comp';
- import { t } from '$lib/translations';
- import { ease, f, sat, sats } from '$lib/utils';
- import { sign, send, encrypt, decrypt } from '$lib/nostr';
- import { event as e, password, rate, animatedRate } from '$lib/store';
- import { browser } from '$app/environment';
+ import { onMount } from "svelte";
+ import { Icon, Avatar, Balance, Feed } from "$comp";
+ import { t } from "$lib/translations";
+ import { ease, f, sat, sats } from "$lib/utils";
+ import { sign, send, encrypt, decrypt } from "$lib/nostr";
+ import { event as e, password, rate, animatedRate } from "$lib/store";
+ import { browser } from "$app/environment";
- export let data;
- let { messages, notes, invoices, sent, received, subject, user, rates } = data;
- $: refresh(data);
- let refresh = (d) => ({ messages, notes, invoices, sent, received, subject, user, rates } = d);
+ export let data;
+ let { messages, notes, invoices, sent, received, subject, user, rates } =
+ data;
+ $: refresh(data);
+ let refresh = (d) =>
+ ({ messages, notes, invoices, sent, received, subject, user, rates } = d);
- let keys = new Set();
- let latest = [];
- let ready;
+ let keys = new Set();
+ let latest = [];
+ let ready;
- e.subscribe(async (event) => {
- if (!(event && ready)) return;
- if (event.recipient?.id === user.id && !~latest.findIndex((m) => m.id === event.id)) {
- event.content = await decrypt({ event, user });
+ e.subscribe(async (event) => {
+ if (!(event && ready)) return;
+ if (
+ event.recipient?.id === user.id &&
+ !~latest.findIndex((m) => m.id === event.id)
+ ) {
+ event.content = await decrypt({ event, user });
- let i = latest.findIndex((m) => m.pubkey === event.pubkey);
- let popped;
- if (~i) popped = latest.splice(i, 1);
- else popped = latest.pop();
+ let i = latest.findIndex((m) => m.pubkey === event.pubkey);
+ let popped;
+ if (~i) popped = latest.splice(i, 1);
+ else popped = latest.pop();
- latest.unshift(event);
- latest = latest;
- }
- });
+ latest.unshift(event);
+ latest = latest;
+ }
+ });
- $: initialize($password);
- let initialize = async (p) => {
- let i = 0;
- ready = false;
- if (!p || !user) return;
- while (i < messages.length) {
- let event = messages[i];
- i++;
+ $: initialize($password);
+ let initialize = async (p) => {
+ let i = 0;
+ ready = false;
+ if (!p || !user) return;
+ while (i < messages.length) {
+ let event = messages[i];
+ i++;
- if (!(event.author && event.recipient)) continue;
+ if (!(event.author && event.recipient)) continue;
- let k = event.author.id === user.id ? event.recipient?.pubkey : event.author.pubkey;
- if (!keys.has(k)) {
- keys.add(k);
- event.content = await decrypt({ event, user });
- if (event.content) latest.push(event);
- }
- }
+ let k =
+ event.author.id === user.id
+ ? event.recipient?.pubkey
+ : event.author.pubkey;
+ if (!keys.has(k)) {
+ keys.add(k);
+ event.content = await decrypt({ event, user });
+ if (event.content) latest.push(event);
+ }
+ }
- latest = latest;
- ready = true;
- };
+ latest = latest;
+ ready = true;
+ };
- let i;
- let o;
+ let i;
+ let o;
- rate.subscribe((n) => {
- if (!user?.currency) return;
- clearInterval(i);
+ rate.subscribe((n) => {
+ if (!user?.currency) return;
+ clearInterval(i);
- let r = ((rates[user.currency] / rates['USD']) * n).toFixed(2);
+ let r = ((rates[user.currency] / rates["USD"]) * n).toFixed(2);
- if (!r || isNaN(r)) return;
- if (!o) o = r;
+ if (!r || isNaN(r)) return;
+ if (!o) o = r;
- let t = 0;
- let d = o - r;
+ let t = 0;
+ let d = o - r;
- i = setInterval(() => {
- if (!r) return;
- $animatedRate = (o - d * ease(t / 100)).toFixed(2);
- if (t > 80) (o = r) && clearInterval(i);
- t++;
- }, 10);
- });
+ i = setInterval(() => {
+ if (!r) return;
+ $animatedRate = (o - d * ease(t / 100)).toFixed(2);
+ if (t > 80) (o = r) && clearInterval(i);
+ t++;
+ }, 10);
+ });
</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} />
- </div>
+ {#if user?.id === subject.id}
+ <div class="space-y-5">
+ <div class="flex justify-center lg:justify-start">
+ <Balance {user} />
+ </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 gap-2 justify-center w-full">
- <a href={`/${user.username}/receive`} class="w-full md:w-60">
- <button
- class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-full"
- >
- <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>
- <a href={`/send`} class="w-full md:w-60">
- <button
- class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-full"
- >
- <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 gap-2 justify-center w-full">
+ <a href={`/${user.username}/receive`} class="w-full md:w-60">
+ <button
+ class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-full"
+ >
+ <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>
+ <a href={`/send`} class="w-full md:w-60">
+ <button
+ class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-full"
+ >
+ <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>
- {#if user.eligible}
- <a href={`/buy`}>
- <button class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60">
- <div class="mx-auto flex">
- <!-- <Icon icon="plus" style="my-auto h-6 mr-2" /> -->
+ {#if user.eligible}
+ <a href={`/buy`}>
+ <button
+ class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60"
+ >
+ <div class="mx-auto flex">
+ <!-- <Icon icon="plus" style="my-auto h-6 mr-2" /> -->
- <img
- src="/images/bitcoin.svg"
- class="w-8 border-4 border-transparent mr-2"
- alt="Bitcoin"
- />
- <div class="my-auto mt-1">Buy Bitcoin</div>
- </div>
- </button>
- </a>
- {/if}
- </div>
- </div>
- {/if}
+ <img
+ src="/images/bitcoin.svg"
+ class="w-8 border-4 border-transparent mr-2"
+ alt="Bitcoin"
+ />
+ <div class="my-auto mt-1">Buy Bitcoin</div>
+ </div>
+ </button>
+ </a>
+ {/if}
+ </div>
+ </div>
+ {/if}
- {#if invoices.length}
- <div>
- <h1 class="text-secondary text-xl">Invoices</h1>
- {#each invoices as { amount, currency, rate, request: { requester, recipient }, hash }}
- {@const requested = user.username === requester?.username}
- <a href={`/${recipient.username}/invoice/${hash}` + (requested ? '/tip' : '')}>
- <div class="border-b p-2 last:border-b-0 hover:bg-gray-100">
- <div class="flex gap-2">
- <div>
- {#if requested}
- <div class="flex">
- <Avatar user={recipient} size={20} />
- <div class="my-auto text-left">
- <p class="text-secondary">{$t('payments.invoiceFrom')}</p>
- <p class="ml-1 text-lg break-words">{recipient.username}</p>
- </div>
- </div>
- {:else}
- <div class="flex">
- <Avatar user={requester} size={20} />
- <div class="my-auto text-left">
- <p class="text-secondary">{$t('payments.awaiting')}</p>
- <p class="ml-1 text-lg break-words">{requester.username}</p>
- </div>
- </div>
- {/if}
- </div>
- <div class="whitespace-nowrap my-auto ml-auto flex gap-2">
- <div class="font-bold">
- {f(amount * (rate / sats), currency)}
- </div>
+ {#if invoices.length}
+ <div>
+ <h1 class="text-secondary text-xl">Invoices</h1>
+ {#each invoices as { amount, currency, rate, request: { requester, recipient }, hash }}
+ {@const requested = user.username === requester?.username}
+ <a
+ href={`/${recipient.username}/invoice/${hash}` +
+ (requested ? "/tip" : "")}
+ >
+ <div class="border-b p-2 last:border-b-0 hover:bg-gray-100">
+ <div class="flex gap-2">
+ <div>
+ {#if requested}
+ <div class="flex">
+ <Avatar user={recipient} size={20} />
+ <div class="my-auto text-left">
+ <p class="text-secondary">{$t("payments.invoiceFrom")}</p>
+ <p class="ml-1 text-lg break-words">
+ {recipient.username}
+ </p>
+ </div>
+ </div>
+ {:else}
+ <div class="flex">
+ <Avatar user={requester} size={20} />
+ <div class="my-auto text-left">
+ <p class="text-secondary">{$t("payments.awaiting")}</p>
+ <p class="ml-1 text-lg break-words">
+ {requester.username}
+ </p>
+ </div>
+ </div>
+ {/if}
+ </div>
+ <div class="whitespace-nowrap my-auto ml-auto flex gap-2">
+ <div class="font-bold">
+ {f(amount * (rate / sats), currency)}
+ </div>
- <div class="text-secondary">{sat(amount)}</div>
- </div>
- </div>
- </div>
- </a>
- {/each}
- </div>
- {/if}
- {#if received.length}
- <div>
- <h1 class="text-secondary text-xl">Requests</h1>
+ <div class="text-secondary">{sat(amount)}</div>
+ </div>
+ </div>
+ </div>
+ </a>
+ {/each}
+ </div>
+ {/if}
+ {#if received.length}
+ <div>
+ <h1 class="text-secondary text-xl">Requests</h1>
- {#each received as { id, invoice, memo, requester: r }}
- <a href={`/${user.username}/receive/${id}`}>
- <div class="border-b p-2 last:border-b-0 hover:bg-gray-100">
- <div class="flex">
- <div>
- <div class="flex">
- <Avatar user={r} size={20} />
- <div class="my-auto text-left">
- <p class="ml-1 text-lg break-words">{r.username}</p>
- <p class="ml-1 text-secondary">{memo}</p>
- </div>
- </div>
- </div>
- <div class="whitespace-nowrap my-auto ml-auto flex gap-2">
- <button class="rounded-full border py-2 px-4 font-bold hover:opacity-80 w-full"
- >Invoice</button
- >
- </div>
- </div>
- </div>
- </a>
- {/each}
- </div>
- {/if}
+ {#each received as { id, invoice, memo, requester: r }}
+ <a href={`/${user.username}/receive/${id}`}>
+ <div class="border-b p-2 last:border-b-0 hover:bg-gray-100">
+ <div class="flex">
+ <div>
+ <div class="flex">
+ <Avatar user={r} size={20} />
+ <div class="my-auto text-left">
+ <p class="ml-1 text-lg break-words">{r.username}</p>
+ <p class="ml-1 text-secondary">{memo}</p>
+ </div>
+ </div>
+ </div>
+ <div class="whitespace-nowrap my-auto ml-auto flex gap-2">
+ <button
+ class="rounded-full border py-2 px-4 font-bold hover:opacity-80 w-full"
+ >Invoice</button
+ >
+ </div>
+ </div>
+ </div>
+ </a>
+ {/each}
+ </div>
+ {/if}
- {#if latest.length && user?.id === subject.id}
- <div class="relative">
- {#each latest as { content, pubkey, author, recipient }}
- {@const user = author.id === user.id ? recipient : author}
- <a href={`/${user.username}/messages`}>
- <div class="flex hover:bg-gray-100 p-4 rounded-2xl">
- <div class="my-auto">
- <Avatar {user} size={'20'} disabled={true} />
- </div>
- <div class="my-auto truncate">
- <div class="my-auto ml-1 text-lg font-bold">{user.username}</div>
- <div class="my-auto ml-1 text-secondary text-lg truncate">
- {content}
- </div>
- </div>
- </div>
- </a>
- {/each}
- </div>
- {/if}
+ {#if latest.length && user?.id === subject.id}
+ <div class="relative">
+ {#each latest as { content, pubkey, author, recipient }}
+ {@const user = author.id === user.id ? recipient : author}
+ <a href={`/${user.username}/messages`}>
+ <div class="flex hover:bg-gray-100 p-4 rounded-2xl">
+ <div class="my-auto">
+ <Avatar {user} size={"20"} disabled={true} />
+ </div>
+ <div class="my-auto truncate">
+ <div class="my-auto ml-1 text-lg font-bold">{user.username}</div>
+ <div class="my-auto ml-1 text-secondary text-lg truncate">
+ {content}
+ </div>
+ </div>
+ </div>
+ </a>
+ {/each}
+ </div>
+ {/if}
</div>
diff --git a/src/routes/(app)/[username]/(profile)/event/[id]/+page.server.js b/src/routes/(app)/[username]/(profile)/event/[id]/+page.server.js
index 6182de17..ec4da865 100644
--- a/src/routes/(app)/[username]/(profile)/event/[id]/+page.server.js
+++ b/src/routes/(app)/[username]/(profile)/event/[id]/+page.server.js
@@ -1,12 +1,12 @@
-import { get } from '$lib/utils';
-import { error } from '@sveltejs/kit';
+import { get } from "$lib/utils";
+import { error } from "@sveltejs/kit";
export async function load({ params: { id } }) {
- try {
- let event = await get(`/event/${id}`);
- if (!event) throw error(500, 'Event not found');
- return { event };
- } catch (e) {
- throw error(500, e.message);
- }
+ try {
+ let event = await get(`/event/${id}`);
+ if (!event) throw error(500, "Event not found");
+ return { event };
+ } catch (e) {
+ throw error(500, e.message);
+ }
}
diff --git a/src/routes/(app)/[username]/(profile)/event/[id]/+page.svelte b/src/routes/(app)/[username]/(profile)/event/[id]/+page.svelte
index 85fc815a..1347947f 100644
--- a/src/routes/(app)/[username]/(profile)/event/[id]/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/event/[id]/+page.svelte
@@ -1,7 +1,7 @@
<script>
- import { Event } from '$comp';
- export let data;
- let { event } = data;
+ import { Event } from "$comp";
+ export let data;
+ let { event } = data;
</script>
<Event {event} />
diff --git a/src/routes/(app)/[username]/(profile)/followers/+page.svelte b/src/routes/(app)/[username]/(profile)/followers/+page.svelte
index 56b3ba16..f51af86b 100644
--- a/src/routes/(app)/[username]/(profile)/followers/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/followers/+page.svelte
@@ -1,51 +1,52 @@
<script>
- import { t } from '$lib/translations';
- import { Avatar } from '$comp';
- import { browser } from '$app/environment';
- import VirtualScroll from 'svelte-virtual-scroll-list';
+ import { t } from "$lib/translations";
+ import { Avatar } from "$comp";
+ import { browser } from "$app/environment";
+ import VirtualScroll from "svelte-virtual-scroll-list";
- export let data;
- let {
- subject: { followers }
- } = data;
+ export let data;
+ let {
+ subject: { followers },
+ } = data;
- let w;
+ let w;
</script>
<svelte:window bind:innerWidth={w} />
<div class="px-3 md:px-0 w-full md:w-[400px] mx-auto space-y-8">
- <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold">
- {$t('user.followers')}
- </h1>
+ <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold">
+ {$t("user.followers")}
+ </h1>
- {#if browser}
- {#if followers.length}
- <VirtualScroll data={followers} key="pubkey" let:data pageMode={true}>
- <a href={`/${data.pubkey}`}>
- <div
- class="flex border-b last:border-b-0 py-4 text-sm lg:text-lg text-secondary"
- :key={data.pubkey}
- >
- <div class="mb-auto mr-2">
- <div class="md:hidden">
- <Avatar size={12} user={data} disabled={true} />
- </div>
- <div class="hidden md:block">
- <Avatar size={20} user={data} disabled={true} />
- </div>
- </div>
+ {#if browser}
+ {#if followers.length}
+ <VirtualScroll data={followers} key="pubkey" let:data pageMode={true}>
+ <a href={`/${data.pubkey}`}>
+ <div
+ class="flex border-b last:border-b-0 py-4 text-sm lg:text-lg text-secondary"
+ :key={data.pubkey}
+ >
+ <div class="mb-auto mr-2">
+ <div class="md:hidden">
+ <Avatar size={12} user={data} disabled={true} />
+ </div>
+ <div class="hidden md:block">
+ <Avatar size={20} user={data} disabled={true} />
+ </div>
+ </div>
- <div class="w-full flex pb-1 text-black my-auto">
- <div>
- {data.display_name || ''} <span class="text-secondary">@{data.username}</span>
- </div>
- </div>
- </div>
- </a>
- </VirtualScroll>
- {:else}
- <div>No followers</div>
- {/if}
- {/if}
+ <div class="w-full flex pb-1 text-black my-auto">
+ <div>
+ {data.display_name || ""}
+ <span class="text-secondary">@{data.username}</span>
+ </div>
+ </div>
+ </div>
+ </a>
+ </VirtualScroll>
+ {:else}
+ <div>No followers</div>
+ {/if}
+ {/if}
</div>
diff --git a/src/routes/(app)/[username]/(profile)/follows/+page.svelte b/src/routes/(app)/[username]/(profile)/follows/+page.svelte
index b238c3a7..4f1bf32b 100644
--- a/src/routes/(app)/[username]/(profile)/follows/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/follows/+page.svelte
@@ -1,47 +1,51 @@
<script>
- import { t } from '$lib/translations';
- import { Avatar } from '$comp';
- import { browser } from '$app/environment';
- import VirtualScroll from 'svelte-virtual-scroll-list';
+ import { t } from "$lib/translations";
+ import { Avatar } from "$comp";
+ import { browser } from "$app/environment";
+ import VirtualScroll from "svelte-virtual-scroll-list";
- export let data;
- let {
- subject: { follows }
- } = data;
- let w;
+ export let data;
+ let {
+ subject: { follows },
+ } = data;
+ let w;
</script>
<svelte:window bind:innerWidth={w} />
<div class="px-3 md:px-0 w-full md:w-[400px] mx-auto space-y-8">
- <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold">
- {$t('user.following')}
- </h1>
+ <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold">
+ {$t("user.following")}
+ </h1>
- {#if browser}
- {#if follows.length}
- <VirtualScroll data={follows} key="pubkey" let:data pageMode={true}>
- <a href={`/${data.pubkey}`}>
- <div class="flex py-4 text-sm lg:text-lg text-secondary" :key={data.pubkey}>
- <div class="mb-auto mr-2">
- <div class="md:hidden">
- <Avatar size={12} user={data} disabled={true} />
- </div>
- <div class="hidden md:block">
- <Avatar size={20} user={data} disabled={true} />
- </div>
- </div>
+ {#if browser}
+ {#if follows.length}
+ <VirtualScroll data={follows} key="pubkey" let:data pageMode={true}>
+ <a href={`/${data.pubkey}`}>
+ <div
+ class="flex py-4 text-sm lg:text-lg text-secondary"
+ :key={data.pubkey}
+ >
+ <div class="mb-auto mr-2">
+ <div class="md:hidden">
+ <Avatar size={12} user={data} disabled={true} />
+ </div>
+ <div class="hidden md:block">
+ <Avatar size={20} user={data} disabled={true} />
+ </div>
+ </div>
- <div class="w-full flex pb-1 text-black my-auto">
- <div>
- {data.display_name || ''} <span class="text-secondary">@{data.username}</span>
- </div>
- </div>
- </div>
- </a>
- </VirtualScroll>
- {:else}
- <div>No follows</div>
- {/if}
- {/if}
+ <div class="w-full flex pb-1 text-black my-auto">
+ <div>
+ {data.display_name || ""}
+ <span class="text-secondary">@{data.username}</span>
+ </div>
+ </div>
+ </div>
+ </a>
+ </VirtualScroll>
+ {:else}
+ <div>No follows</div>
+ {/if}
+ {/if}
</div>
diff --git a/src/routes/(app)/[username]/(profile)/request/+page.server.js b/src/routes/(app)/[username]/(profile)/request/+page.server.js
index e7ad6a8f..754c5805 100644
--- a/src/routes/(app)/[username]/(profile)/request/+page.server.js
+++ b/src/routes/(app)/[username]/(profile)/request/+page.server.js
@@ -1,15 +1,15 @@
-import { redirect } from '@sveltejs/kit';
-import { fd, auth, post } from '$lib/utils';
+import { redirect } from "@sveltejs/kit";
+import { fd, auth, post } from "$lib/utils";
export const actions = {
- create: async ({ cookies, params, request, url }) => {
- let form = await fd(request);
- let { id, requester } = await post('/requests', form, auth(cookies));
- throw redirect(307, `/${params.username}/request/${id}`);
- },
+ create: async ({ cookies, params, request, url }) => {
+ let form = await fd(request);
+ let { id, requester } = await post("/requests", form, auth(cookies));
+ throw redirect(307, `/${params.username}/request/${id}`);
+ },
- delete: async ({ cookies, request }) => {
- let form = await fd(request);
- let r = await post('/requests/delete', form, auth(cookies));
- }
+ delete: async ({ cookies, request }) => {
+ let form = await fd(request);
+ let r = await post("/requests/delete", form, auth(cookies));
+ },
};
diff --git a/src/routes/(app)/[username]/(profile)/request/+page.svelte b/src/routes/(app)/[username]/(profile)/request/+page.svelte
index 75117ff4..5727c3d4 100644
--- a/src/routes/(app)/[username]/(profile)/request/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/request/+page.svelte
@@ -1,35 +1,35 @@
<script>
- import { t } from '$lib/translations';
- import { enhance } from '$app/forms';
- import { Icon } from '$comp';
+ import { t } from "$lib/translations";
+ import { enhance } from "$app/forms";
+ import { Icon } from "$comp";
- export let data;
+ export let data;
- let { subject, user } = data;
+ let { subject, user } = data;
</script>
<div class="container max-w-lg px-4 mx-auto mt-10 space-y-5">
- <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold">
- {$t('payments.requestAnInvoice')}
- </h1>
+ <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold">
+ {$t("payments.requestAnInvoice")}
+ </h1>
- <form action="?/create" method="POST" use:enhance class="space-y-5">
- <input type="hidden" name="requester_id" value={user.id} />
- <input type="hidden" name="recipient" value={subject.username} />
+ <form action="?/create" method="POST" use:enhance class="space-y-5">
+ <input type="hidden" name="requester_id" value={user.id} />
+ <input type="hidden" name="recipient" value={subject.username} />
- <textarea
- name="memo"
- placeholder={$t('payments.describeOrder')}
- class="w-full p-4 border rounded-xl h-48"
- />
+ <textarea
+ name="memo"
+ placeholder={$t("payments.describeOrder")}
+ class="w-full p-4 border rounded-xl h-48"
+ />
- <div class="w-full flex">
- <button
- type="submit"
- class="bg-black text-white border rounded-full px-6 py-2 font-bold mx-auto"
- >
- {$t('payments.submit')}
- </button>
- </div>
- </form>
+ <div class="w-full flex">
+ <button
+ type="submit"
+ class="bg-black text-white border rounded-full px-6 py-2 font-bold mx-auto"
+ >
+ {$t("payments.submit")}
+ </button>
+ </div>
+ </form>
</div>
diff --git a/src/routes/(app)/[username]/(profile)/request/sent/+page.svelte b/src/routes/(app)/[username]/(profile)/request/sent/+page.svelte
index 4995bfb2..6ce0c9f6 100644
--- a/src/routes/(app)/[username]/(profile)/request/sent/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/request/sent/+page.svelte
@@ -1,18 +1,21 @@
<script>
- import { scale } from 'svelte/transition';
- import { Icon } from '$comp';
+ import { scale } from "svelte/transition";
+ import { Icon } from "$comp";
- export let data;
- let { subject } = data;
+ export let data;
+ let { subject } = data;
</script>
<div class="container px-4 text-center mx-auto">
- <div class="flex w-full py-20 max-w-[200px] mx-auto" in:scale={{ start: 0.5 }}>
- <Icon icon="check" style="mx-auto" />
- </div>
- <h1 class="text-3xl md:text-4xl font-bold mb-6">Request sent</h1>
+ <div
+ class="flex w-full py-20 max-w-[200px] mx-auto"
+ in:scale={{ start: 0.5 }}
+ >
+ <Icon icon="check" style="mx-auto" />
+ </div>
+ <h1 class="text-3xl md:text-4xl font-bold mb-6">Request sent</h1>
</div>
<a href={`/${subject.username}`}>
- <div class="opacity-0 w-screen h-screen fixed top-24 left-0 z-50" />
+ <div class="opacity-0 w-screen h-screen fixed top-24 left-0 z-50" />
</a>
diff --git a/src/routes/(app)/[username]/+layout.server.js b/src/routes/(app)/[username]/+layout.server.js
index 8df75f0f..0e962173 100644
--- a/src/routes/(app)/[username]/+layout.server.js
+++ b/src/routes/(app)/[username]/+layout.server.js
@@ -1,13 +1,13 @@
-import { get } from '$lib/utils';
-import { error } from '@sveltejs/kit';
+import { get } from "$lib/utils";
+import { error } from "@sveltejs/kit";
export let load = async ({ depends, params: { username } }) => {
- depends('app:user');
+ depends("app:user");
- try {
- let subject = await get(`/users/${username}`);
- return { subject };
- } catch (e) {
- throw error(500, 'Unable to retrieve user account data');
- }
+ try {
+ let subject = await get(`/users/${username}`);
+ return { subject };
+ } catch (e) {
+ throw error(500, "Unable to retrieve user account data");
+ }
};
diff --git a/src/routes/(app)/[username]/+layout.svelte b/src/routes/(app)/[username]/+layout.svelte
index 54fcd600..3a351e48 100644
--- a/src/routes/(app)/[username]/+layout.svelte
+++ b/src/routes/(app)/[username]/+layout.svelte
@@ -1,57 +1,57 @@
<script>
- import { AppHeader } from '$comp';
- import { onNavigate } from '$app/navigation';
- export let data;
+ import { AppHeader } from "$comp";
+ import { onNavigate } from "$app/navigation";
+ export let data;
- onNavigate((navigation) => {
- if (!document.startViewTransition) return;
+ onNavigate((navigation) => {
+ if (!document.startViewTransition) return;
- return new Promise((resolve) => {
- document.startViewTransition(async () => {
- resolve();
- await navigation.complete;
- });
- });
- });
+ return new Promise((resolve) => {
+ document.startViewTransition(async () => {
+ resolve();
+ await navigation.complete;
+ });
+ });
+ });
</script>
<AppHeader {data} />
<div class="content">
- <slot />
+ <slot />
</div>
<style>
- @keyframes fade-in {
- from {
- opacity: 0;
- }
- }
+ @keyframes fade-in {
+ from {
+ opacity: 0;
+ }
+ }
- @keyframes fade-out {
- to {
- opacity: 0;
- }
- }
+ @keyframes fade-out {
+ to {
+ opacity: 0;
+ }
+ }
- @keyframes slide-from-right {
- from {
- transform: translateX(60px);
- }
- }
+ @keyframes slide-from-right {
+ from {
+ transform: translateX(60px);
+ }
+ }
- @keyframes slide-to-left {
- to {
- transform: translateX(-60px);
- }
- }
+ @keyframes slide-to-left {
+ to {
+ transform: translateX(-60px);
+ }
+ }
- :root::view-transition-old(root) {
- animation: 280ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
- 180ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
- }
+ :root::view-transition-old(root) {
+ animation: 280ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
+ 180ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
+ }
- :root::view-transition-new(root) {
- animation: 280ms cubic-bezier(0, 0, 0.2, 1) 80ms both fade-in,
- 180ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
- }
+ :root::view-transition-new(root) {
+ animation: 280ms cubic-bezier(0, 0, 0.2, 1) 80ms both fade-in,
+ 180ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
+ }
</style>
diff --git a/src/routes/(app)/[username]/address/+page.server.js b/src/routes/(app)/[username]/address/+page.server.js
index 247a33a2..1a782f9d 100644
--- a/src/routes/(app)/[username]/address/+page.server.js
+++ b/src/routes/(app)/[username]/address/+page.server.js
@@ -1,10 +1,10 @@
-import { auth, get, post } from '$lib/utils';
-import { redirect } from '@sveltejs/kit';
+import { auth, get, post } from "$lib/utils";
+import { redirect } from "@sveltejs/kit";
export async function load({ cookies, params, parent }) {
- let user = (await parent()).subject;
- let rates = await get('/rates');
- let invoice = { type: 'bitcoin' };
- let { hash } = await post(`/invoice`, { invoice, user }, auth(cookies));
- throw redirect(307, `/${user.username}/invoice/${hash}`);
+ let user = (await parent()).subject;
+ let rates = await get("/rates");
+ let invoice = { type: "bitcoin" };
+ let { hash } = await post(`/invoice`, { invoice, user }, auth(cookies));
+ throw redirect(307, `/${user.username}/invoice/${hash}`);
}
diff --git a/src/routes/(app)/[username]/card/[serial]/+page.server.js b/src/routes/(app)/[username]/card/[serial]/+page.server.js
index 40e375aa..20b657a1 100644
--- a/src/routes/(app)/[username]/card/[serial]/+page.server.js
+++ b/src/routes/(app)/[username]/card/[serial]/+page.server.js
@@ -1,16 +1,16 @@
-import { auth, get } from '$lib/utils';
-import { redirect } from "@sveltejs/kit"
+import { auth, get } from "$lib/utils";
+import { redirect } from "@sveltejs/kit";
export let load = async ({ cookies, params }) => {
- let { serial } = params;
- let fund;
- try {
- fund = await get(`/fund/${serial}`, auth(cookies));
- } catch (e) {
- console.log(e);
- }
+ let { serial } = params;
+ let fund;
+ try {
+ fund = await get(`/fund/${serial}`, auth(cookies));
+ } catch (e) {
+ console.log(e);
+ }
- if (fund) throw redirect(307, `/fund/${serial}`);
+ if (fund) throw redirect(307, `/fund/${serial}`);
- return params;
+ return params;
};
diff --git a/src/routes/(app)/[username]/card/[serial]/+page.svelte b/src/routes/(app)/[username]/card/[serial]/+page.svelte
index afdaf65d..a2e17dfc 100644
--- a/src/routes/(app)/[username]/card/[serial]/+page.svelte
+++ b/src/routes/(app)/[username]/card/[serial]/+page.svelte
@@ -1,35 +1,35 @@
<script>
- import { Card, Icon } from '$comp';
- export let data;
- let { serial } = data;
- let balance = 0;
+ import { Card, Icon } from "$comp";
+ export let data;
+ let { serial } = data;
+ let balance = 0;
</script>
<div class="space-y-5">
- <div class="relative flex justify-center gap-2">
- <h1 class="text-2xl md:text-3xl font-semibold my-auto">Card {serial}</h1>
- </div>
- <div class="flex justify-center">
- <a href={`/send/pot/${serial}`}>
- <button
- class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60 bg-black text-white justify-center gap-2"
- >
- <svg
- xmlns="http://www.w3.org/2000/svg"
- class="invert text-black peer-placeholder-shown:text-gray-300 h-6 w-6 my-auto"
- fill="none"
- viewBox="0 0 24 24"
- stroke="currentColor"
- >
- <path
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="2"
- d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"
- />
- </svg>
- <div class="my-auto">Add Funds</div>
- </button>
- </a>
- </div>
+ <div class="relative flex justify-center gap-2">
+ <h1 class="text-2xl md:text-3xl font-semibold my-auto">Card {serial}</h1>
+ </div>
+ <div class="flex justify-center">
+ <a href={`/send/pot/${serial}`}>
+ <button
+ class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60 bg-black text-white justify-center gap-2"
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ class="invert text-black peer-placeholder-shown:text-gray-300 h-6 w-6 my-auto"
+ fill="none"
+ viewBox="0 0 24 24"
+ stroke="currentColor"
+ >
+ <path
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ stroke-width="2"
+ d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"
+ />
+ </svg>
+ <div class="my-auto">Add Funds</div>
+ </button>
+ </a>
+ </div>
</div>
diff --git a/src/routes/(app)/[username]/funds/+page.svelte b/src/routes/(app)/[username]/funds/+page.svelte
index 6479fb8b..667176e1 100644
--- a/src/routes/(app)/[username]/funds/+page.svelte
+++ b/src/routes/(app)/[username]/funds/+page.svelte
@@ -1,6 +1,6 @@
<script>
- import { Avatar, Icon } from '$comp';
- import { t } from '$lib/translations';
+ import { Avatar, Icon } from "$comp";
+ import { t } from "$lib/translations";
export let data;
@@ -8,49 +8,44 @@
</script>
<div class="mt-24 mb-20">
- <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10">
- Funds
- </h1>
+ <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10">
+ Funds
+ </h1>
- <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5">
- <div class="text-base">
- <div class="grid grid-cols-3 border-b h-24 hover:bg-gray-100 px-4">
- <div class="whitespace-nowrap my-auto">
+ <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5">
+ <div class="text-base">
+ <div class="grid grid-cols-3 border-b h-24 hover:bg-gray-100 px-4">
+ <div class="whitespace-nowrap my-auto">
+ <div class="text-secondary" />
+ </div>
- <div class="text-secondary">
-
- </div>
- </div>
+ <div class="flex my-auto">
+ <div class="text-secondary flex">
+ <div class="my-auto mr-1">
+ <img src="/images/moneypot.png" class="w-12" alt="Pot" />
+ </div>
- <div class="flex my-auto">
- <div class="text-secondary flex">
- <div class="my-auto mr-1">
- <img src="/images/moneypot.png" class="w-12" alt="Pot" />
- </div>
+ <div class="my-auto">Pot</div>
+ </div>
+ </div>
- <div class="my-auto">Pot</div>
- </div>
- </div>
+ <div class="text-secondary text-right text-sm my-auto">
+ <div />
+ </div>
+ </div>
+ </div>
- <div class="text-secondary text-right text-sm my-auto">
- <div>
-
- </div>
- </div>
- </div>
- </div>
-
- <button
- class="ml-auto rounded-full border py-2 px-4 w-36 hover:opacity-80 flex mx-auto"
- >
- <Icon icon="plus" style="opacity-50 mr-2 my-auto" />
- <div class="my-auto">{$t('payments.create')}</div>
- </button>
- </div>
+ <button
+ class="ml-auto rounded-full border py-2 px-4 w-36 hover:opacity-80 flex mx-auto"
+ >
+ <Icon icon="plus" style="opacity-50 mr-2 my-auto" />
+ <div class="my-auto">{$t("payments.create")}</div>
+ </button>
+ </div>
</div>
<style>
- .active * {
- @apply bg-black text-white border-black;
- }
+ .active * {
+ @apply bg-black text-white border-black;
+ }
</style>
diff --git a/src/routes/(app)/[username]/generate/+page.svelte b/src/routes/(app)/[username]/generate/+page.svelte
index b9274434..4ebb5eae 100644
--- a/src/routes/(app)/[username]/generate/+page.svelte
+++ b/src/routes/(app)/[username]/generate/+page.svelte
@@ -1,62 +1,62 @@
<script>
- import { fail, post, wait } from '$lib/utils';
- import { onMount } from 'svelte';
- import { goto, invalidate } from '$app/navigation';
- import { Pin } from '$comp';
- import { loginRedirect, pin, password, passwordPrompt } from '$lib/store';
- import { generate } from '$lib/nostr';
- import { browser } from '$app/environment';
+ import { fail, post, wait } from "$lib/utils";
+ import { onMount } from "svelte";
+ import { goto, invalidate } from "$app/navigation";
+ import { Pin } from "$comp";
+ import { loginRedirect, pin, password, passwordPrompt } from "$lib/store";
+ import { generate } from "$lib/nostr";
+ import { browser } from "$app/environment";
- export let data;
- let { user } = data;
+ export let data;
+ let { user } = data;
- let loaded;
- let gen = async () => {
- if (!browser) return;
+ let loaded;
+ let gen = async () => {
+ if (!browser) return;
- if (user.pubkey) {
- goto($loginRedirect || `/${user.username}`, { invalidateAll: true });
- return;
- }
+ if (user.pubkey) {
+ goto($loginRedirect || `/${user.username}`, { invalidateAll: true });
+ return;
+ }
- if (!$password) $passwordPrompt = true;
+ if (!$password) $passwordPrompt = true;
- try {
- await post('/password', { password: $password });
- } catch (e) {
- $passwordPrompt = true;
- }
+ try {
+ await post("/password", { password: $password });
+ } catch (e) {
+ $passwordPrompt = true;
+ }
- await wait(() => $password);
+ await wait(() => $password);
- try {
- await generate(user);
- } catch (e) {
- console.log('error generating', e);
- }
+ try {
+ await generate(user);
+ } catch (e) {
+ console.log("error generating", e);
+ }
- try {
- user.pin = $pin;
- await post(`/${user.username}/generate`, user);
- goto($loginRedirect || `/${user.username}`, { invalidateAll: true });
- } catch (e) {
- $pin = '';
- if (e.message?.startsWith('Pin')) {
- fail('Wrong pin, try again');
- } else {
- fail('Failed to generate keys');
- goto('/');
- }
- }
- };
+ try {
+ user.pin = $pin;
+ await post(`/${user.username}/generate`, user);
+ goto($loginRedirect || `/${user.username}`, { invalidateAll: true });
+ } catch (e) {
+ $pin = "";
+ if (e.message?.startsWith("Pin")) {
+ fail("Wrong pin, try again");
+ } else {
+ fail("Failed to generate keys");
+ goto("/");
+ }
+ }
+ };
- onMount(() => browser && setTimeout(() => (loaded = true) && gen(), 50));
+ onMount(() => browser && setTimeout(() => (loaded = true) && gen(), 50));
- $: $pin?.length === 6 && gen(user);
+ $: $pin?.length === 6 && gen(user);
</script>
{#if loaded && user?.haspin && $pin?.length !== 6}
- <Pin />
+ <Pin />
{/if}
{loaded}
{JSON.stringify(user)}
diff --git a/src/routes/(app)/[username]/generate/+server.js b/src/routes/(app)/[username]/generate/+server.js
index e1df08e3..1dca1ece 100644
--- a/src/routes/(app)/[username]/generate/+server.js
+++ b/src/routes/(app)/[username]/generate/+server.js
@@ -1,11 +1,11 @@
-import { auth, post } from '$lib/utils';
-import { error, json } from '@sveltejs/kit';
+import { auth, post } from "$lib/utils";
+import { error, json } from "@sveltejs/kit";
export async function POST({ cookies, request }) {
- try {
- let body = await request.json();
- return json(await post(`/user`, body, auth(cookies)));
- } catch (e) {
- throw error(500, e.message);
- }
+ try {
+ let body = await request.json();
+ return json(await post(`/user`, body, auth(cookies)));
+ } catch (e) {
+ throw error(500, e.message);
+ }
}
diff --git a/src/routes/(app)/[username]/invoice/+server.js b/src/routes/(app)/[username]/invoice/+server.js
index 461d380c..5e3d0300 100644
--- a/src/routes/(app)/[username]/invoice/+server.js
+++ b/src/routes/(app)/[username]/invoice/+server.js
@@ -1,7 +1,7 @@
-import { auth, post } from '$lib/utils';
-import { json } from '@sveltejs/kit';
+import { auth, post } from "$lib/utils";
+import { json } from "@sveltejs/kit";
export let POST = async ({ cookies: c, request: r }) => {
- let j = await r.json();
- return json(await post('/invoice', j, auth(c)));
+ let j = await r.json();
+ return json(await post("/invoice", j, auth(c)));
};
diff --git a/src/routes/(app)/[username]/invoice/[id]/+layout.server.js b/src/routes/(app)/[username]/invoice/[id]/+layout.server.js
index 4d7fd3b8..b9d1c43d 100644
--- a/src/routes/(app)/[username]/invoice/[id]/+layout.server.js
+++ b/src/routes/(app)/[username]/invoice/[id]/+layout.server.js
@@ -1,31 +1,39 @@
-import Qr from 'qrcode-base64';
-import { get } from '$lib/utils';
-import { redirect } from '@sveltejs/kit';
+import Qr from "qrcode-base64";
+import { get } from "$lib/utils";
+import { redirect } from "@sveltejs/kit";
export async function load({ depends, params, url, parent }) {
- depends('app:invoice');
+ depends("app:invoice");
- let { user } = await parent();
- let { id } = params;
- let invoice = await get(`/invoice/${id}`);
- let options = !!url.searchParams.get('options');
+ let { user } = await parent();
+ let { id } = params;
+ let invoice = await get(`/invoice/${id}`);
+ let options = !!url.searchParams.get("options");
- if (user && invoice.uid !== user.id && !url.pathname.includes('tip') && !options) {
- throw redirect(307, `/send/invoice/${id}`);
- }
+ if (
+ user &&
+ invoice.uid !== user.id &&
+ !url.pathname.includes("tip") &&
+ !options
+ ) {
+ throw redirect(307, `/send/invoice/${id}`);
+ }
- let { amount, pending, received } = invoice;
- amount = parseInt(amount);
+ let { amount, pending, received } = invoice;
+ amount = parseInt(amount);
- let paid = (!amount && received) || (amount > 0 && (pending >= amount || received >= amount));
- if (paid && !url.pathname.endsWith('paid')) {
- throw redirect(
- 307,
- `/${params.username}/invoice/${id}/paid` + (options ? '?options=true' : '')
- );
- }
+ let paid =
+ (!amount && received) ||
+ (amount > 0 && (pending >= amount || received >= amount));
+ if (paid && !url.pathname.endsWith("paid")) {
+ throw redirect(
+ 307,
+ `/${params.username}/invoice/${id}/paid` +
+ (options ? "?options=true" : "")
+ );
+ }
- let sm = Qr.drawImg(invoice.text || '', { size: 300 });
+ let sm = Qr.drawImg(invoice.text || "", { size: 300 });
- return { id, invoice, sm, lg: sm };
+ return { id, invoice, sm, lg: sm };
}
diff --git a/src/routes/(app)/[username]/invoice/[id]/+page.server.js b/src/routes/(app)/[username]/invoice/[id]/+page.server.js
index 1068f273..a57efa64 100644
--- a/src/routes/(app)/[username]/invoice/[id]/+page.server.js
+++ b/src/routes/(app)/[username]/invoice/[id]/+page.server.js
@@ -1,5 +1,5 @@
-import invoice from '$lib/invoice';
+import invoice from "$lib/invoice";
export const actions = {
- default: invoice
+ default: invoice,
};
diff --git a/src/routes/(app)/[username]/invoice/[id]/+page.svelte b/src/routes/(app)/[username]/invoice/[id]/+page.svelte
index a0894dd1..b6bf70b7 100644
--- a/src/routes/(app)/[username]/invoice/[id]/+page.svelte
+++ b/src/routes/(app)/[username]/invoice/[id]/+page.svelte
@@ -1,217 +1,235 @@
<script>
- import { enhance } from '$app/forms';
- import { send } from '$lib/socket';
- import { post, back, copy, f, get, types, sat, reverseFormat, s, sats } from '$lib/utils';
- import { tick, onMount, onDestroy } from 'svelte';
- import { browser } from '$app/environment';
- import { last } from '$lib/store';
- import { Avatar, Icon, Heart, Image } from '$comp';
- import { t } from '$lib/translations';
- import { goto, invalidate } from '$app/navigation';
+ import { enhance } from "$app/forms";
+ import { send } from "$lib/socket";
+ import {
+ post,
+ back,
+ copy,
+ f,
+ get,
+ types,
+ sat,
+ reverseFormat,
+ s,
+ sats,
+ } from "$lib/utils";
+ import { tick, onMount, onDestroy } from "svelte";
+ import { browser } from "$app/environment";
+ import { last } from "$lib/store";
+ import { Avatar, Icon, Heart, Image } from "$comp";
+ import { t } from "$lib/translations";
+ import { goto, invalidate } from "$app/navigation";
- export let data;
+ export let data;
- let ndef;
+ let ndef;
- let reading = async ({ message, serialNumber }) => {
- let name = serialNumber.replace(/:/g, '-');
- let result = await post(`/fund/${name}/withdraw`, { amount, hash, name });
- };
+ let reading = async ({ message, serialNumber }) => {
+ let name = serialNumber.replace(/:/g, "-");
+ let result = await post(`/fund/${name}/withdraw`, { amount, hash, name });
+ };
- let readingerror = (e) => console.log('nfc error', e);
+ let readingerror = (e) => console.log("nfc error", e);
- $: refresh(data);
- let { invoice, id, user, sm, lg } = data;
- let {
- amount,
- hash,
- type,
- rate,
- received,
- prompt,
- text,
- tip,
- request_id,
- user: { username, currency }
- } = invoice;
+ $: refresh(data);
+ let { invoice, id, user, sm, lg } = data;
+ let {
+ amount,
+ hash,
+ type,
+ rate,
+ received,
+ prompt,
+ text,
+ tip,
+ request_id,
+ user: { username, currency },
+ } = invoice;
- $: showQr = invoice.uid === user?.id;
+ $: showQr = invoice.uid === user?.id;
- $: src = sm;
+ $: src = sm;
- let qr;
- let tipPercent = 0;
+ let qr;
+ let tipPercent = 0;
- let refresh = async (data) => {
- ({ invoice, id, user, sm, lg } = data);
- ({
- amount,
- hash,
- type,
- rate,
- received,
- prompt,
- text,
- tip,
- user: { username, currency }
- } = invoice);
+ let refresh = async (data) => {
+ ({ invoice, id, user, sm, lg } = data);
+ ({
+ amount,
+ hash,
+ type,
+ rate,
+ received,
+ prompt,
+ text,
+ tip,
+ user: { username, currency },
+ } = invoice);
- tipPercent = (tip / amount) * 100;
+ tipPercent = (tip / amount) * 100;
- // if (browser && window.NDEFReader) {
- // try {
- // ndef = new NDEFReader();
- // await ndef.scan();
- //
- // ndef.removeEventListener('readingerror', readingerror);
- // ndef.removeEventListener('reading', reading);
- //
- // ndef.addEventListener('readingerror', readingerror);
- // ndef.addEventListener('reading', reading);
- // } catch (e) {
- // console.log(e);
- // }
- // }
- };
+ // if (browser && window.NDEFReader) {
+ // try {
+ // ndef = new NDEFReader();
+ // await ndef.scan();
+ //
+ // ndef.removeEventListener('readingerror', readingerror);
+ // ndef.removeEventListener('reading', reading);
+ //
+ // ndef.addEventListener('readingerror', readingerror);
+ // ndef.addEventListener('reading', reading);
+ // } catch (e) {
+ // console.log(e);
+ // }
+ // }
+ };
- $: amountFiat = parseFloat(((amount * rate) / sats).toFixed(2));
- $: tipAmount = ((tip * rate) / sats).toFixed(2);
+ $: amountFiat = parseFloat(((amount * rate) / sats).toFixed(2));
+ $: tipAmount = ((tip * rate) / sats).toFixed(2);
- let subbed;
+ let subbed;
- onMount(async () => {
- if (browser) {
- last.subscribe((v) => {
- if (!v || subbed) return;
- subbed = true;
- send('subscribe', invoice);
- });
- }
- });
+ onMount(async () => {
+ if (browser) {
+ last.subscribe((v) => {
+ if (!v || subbed) return;
+ subbed = true;
+ send("subscribe", invoice);
+ });
+ }
+ });
- onDestroy(() => {
- if (ndef) {
- ndef.removeEventListener('readingerror', readingerror);
- ndef.removeEventListener('reading', reading);
- }
- });
+ onDestroy(() => {
+ if (ndef) {
+ ndef.removeEventListener("readingerror", readingerror);
+ ndef.removeEventListener("reading", reading);
+ }
+ });
- $: link = type === 'bitcoin' ? text : `lightning:${text}`;
- $: txt = type === 'bitcoin' ? hash : text;
+ $: link = type === "bitcoin" ? text : `lightning:${text}`;
+ $: txt = type === "bitcoin" ? hash : text;
- let toggleType = async () => {
- invoice.type = invoice.type === types.lightning ? types.bitcoin : types.lightning;
- ({ id } = await post(`/${username}/invoice`, {
- invoice,
- user: { username, currency }
- }));
+ let toggleType = async () => {
+ invoice.type =
+ invoice.type === types.lightning ? types.bitcoin : types.lightning;
+ ({ id } = await post(`/${username}/invoice`, {
+ invoice,
+ user: { username, currency },
+ }));
- goto(`./${id}`, { invalidateAll: true });
- };
+ goto(`./${id}`, { invalidateAll: true });
+ };
</script>
<div class="container mx-auto max-w-lg px-4 space-y-8">
- <div class="whitespace-nowrap my-auto ml-auto flex gap-2">
- <button
- class="rounded-full border py-2 px-4 font-bold hover:opacity-80 w-full"
- class:bg-black={type === types.lightning}
- class:text-white={type === types.lightning}
- on:click={toggleType}
- >
- ⚡️ Lightning
- </button>
+ <div class="whitespace-nowrap my-auto ml-auto flex gap-2">
+ <button
+ class="rounded-full border py-2 px-4 font-bold hover:opacity-80 w-full"
+ class:bg-black={type === types.lightning}
+ class:text-white={type === types.lightning}
+ on:click={toggleType}
+ >
+ ⚡️ Lightning
+ </button>
- <button
- class="rounded-full border py-2 px-4 font-bold hover:opacity-80 w-full flex justify-center "
- class:bg-black={type === types.bitcoin}
- class:text-white={type === types.bitcoin}
- on:click={toggleType}
- >
- <img
- src="/images/bitcoin.svg"
- class="my-auto w-8 border-4 border-transparent"
- alt="Bitcoin"
- />
- <div class="my-auto">Bitcoin</div>
- </button>
- </div>
+ <button
+ class="rounded-full border py-2 px-4 font-bold hover:opacity-80 w-full flex justify-center"
+ class:bg-black={type === types.bitcoin}
+ class:text-white={type === types.bitcoin}
+ on:click={toggleType}
+ >
+ <img
+ src="/images/bitcoin.svg"
+ class="my-auto w-8 border-4 border-transparent"
+ alt="Bitcoin"
+ />
+ <div class="my-auto">Bitcoin</div>
+ </button>
+ </div>
- {#if showQr}
- <div>
- <a href={invoice.type === 'bitcoin' ? invoice.text : 'lightning:' + invoice.text}>
- <div class="relative flex">
- <div class="flex mx-auto w-[360px] h-[360px]">
- <img
- {src}
- class="w-[300px] h-[300px] mx-auto z-10 mt-[20px]"
- bind:this={qr}
- alt={txt}
- />
- </div>
- <div
- class="absolute m-auto left-0 right-0 w-[340px] h-[340px] rounded-full bg-gradient-to-r from-[#F2F6FC] to-[#E1E3FF] z-0"
- />
- </div>
- </a>
- </div>
- {/if}
+ {#if showQr}
+ <div>
+ <a
+ href={invoice.type === "bitcoin"
+ ? invoice.text
+ : "lightning:" + invoice.text}
+ >
+ <div class="relative flex">
+ <div class="flex mx-auto w-[360px] h-[360px]">
+ <img
+ {src}
+ class="w-[300px] h-[300px] mx-auto z-10 mt-[20px]"
+ bind:this={qr}
+ alt={txt}
+ />
+ </div>
+ <div
+ class="absolute m-auto left-0 right-0 w-[340px] h-[340px] rounded-full bg-gradient-to-r from-[#F2F6FC] to-[#E1E3FF] z-0"
+ />
+ </div>
+ </a>
+ </div>
+ {/if}
- {#if amount > 0}
- <div class="text-center font-bold text-2xl">
- <div>
- {f(amountFiat, currency)}
+ {#if amount > 0}
+ <div class="text-center font-bold text-2xl">
+ <div>
+ {f(amountFiat, currency)}
- {#if tip}
- <span class="text-sm">
- +{f(tip * (rate / sats), currency)}
- </span>
- {/if}
- </div>
- <div>
- <span class="text-secondary font-normal text-lg">{`${sat(amount)}`}</span>
+ {#if tip}
+ <span class="text-sm">
+ +{f(tip * (rate / sats), currency)}
+ </span>
+ {/if}
+ </div>
+ <div>
+ <span class="text-secondary font-normal text-lg"
+ >{`${sat(amount)}`}</span
+ >
- {#if tip}
- <span class="text-sm text-secondary font-normal">
- +{sat(tip)}
- </span>
- {/if}
- </div>
- </div>
- {/if}
+ {#if tip}
+ <span class="text-sm text-secondary font-normal">
+ +{sat(tip)}
+ </span>
+ {/if}
+ </div>
+ </div>
+ {/if}
- <div class="text-center break-all">
- <a href={link}>
- {txt}
- </a>
- </div>
+ <div class="text-center break-all">
+ <a href={link}>
+ {txt}
+ </a>
+ </div>
- <div class="w-full flex justify-center gap-2 flex-wrap">
- <a href={link} class="w-full md:w-auto">
- <button
- class="w-full md:w-auto flex justify-center rounded-full border py-3 px-5 hover:opacity-80"
- >
- <Icon icon="mobile" style="mr-1 w-6" />
- <div class="text-secondary">{$t('payments.openLink')}</div>
- </button>
- </a>
+ <div class="w-full flex justify-center gap-2 flex-wrap">
+ <a href={link} class="w-full md:w-auto">
+ <button
+ class="w-full md:w-auto flex justify-center rounded-full border py-3 px-5 hover:opacity-80"
+ >
+ <Icon icon="mobile" style="mr-1 w-6" />
+ <div class="text-secondary">{$t("payments.openLink")}</div>
+ </button>
+ </a>
- <button
- class="flex rounded-full justify-center border py-3 px-5 hover:opacity-80 w-full md:w-auto"
- on:click={() => copy(txt)}
- >
- <Icon icon="copy" style="mr-1" />
- <div class="text-secondary">{$t('payments.copyText')}</div></button
- >
+ <button
+ class="flex rounded-full justify-center border py-3 px-5 hover:opacity-80 w-full md:w-auto"
+ on:click={() => copy(txt)}
+ >
+ <Icon icon="copy" style="mr-1" />
+ <div class="text-secondary">{$t("payments.copyText")}</div></button
+ >
- <button
- class="w-full md:w-auto flex justify-center rounded-full border py-3 px-5 hover:opacity-80"
- on:click={() => (showQr = !showQr)}
- >
- <Icon icon="qr" style="mr-1 invert" />
- <div class="text-secondary">
- {showQr ? $t('payments.hide') : $t('payments.show')}
- {$t('payments.qr')}
- </div></button
- >
- </div>
+ <button
+ class="w-full md:w-auto flex justify-center rounded-full border py-3 px-5 hover:opacity-80"
+ on:click={() => (showQr = !showQr)}
+ >
+ <Icon icon="qr" style="mr-1 invert" />
+ <div class="text-secondary">
+ {showQr ? $t("payments.hide") : $t("payments.show")}
+ {$t("payments.qr")}
+ </div></button
+ >
+ </div>
</div>
diff --git a/src/routes/(app)/[username]/invoice/[id]/paid/+page.svelte b/src/routes/(app)/[username]/invoice/[id]/paid/+page.svelte
index 84e493d9..655bc3e4 100644
--- a/src/routes/(app)/[username]/invoice/[id]/paid/+page.svelte
+++ b/src/routes/(app)/[username]/invoice/[id]/paid/+page.svelte
@@ -1,64 +1,70 @@
<script>
- import { scale } from 'svelte/transition';
- import { Icon } from '$comp';
- import { toast } from '@zerodevx/svelte-toast';
- import { browser } from '$app/environment';
- import { t } from '$lib/translations';
- import { fiat, f, s, sat, sats } from '$lib/utils';
+ import { scale } from "svelte/transition";
+ import { Icon } from "$comp";
+ import { toast } from "@zerodevx/svelte-toast";
+ import { browser } from "$app/environment";
+ import { t } from "$lib/translations";
+ import { fiat, f, s, sat, sats } from "$lib/utils";
- export let data;
- let amount, currency, rate, received, pending, tip, user, id;
- $: data && ({ amount, currency, rate, received, pending, tip, user, id } = data.invoice);
+ export let data;
+ let amount, currency, rate, received, pending, tip, user, id;
+ $: data &&
+ ({ amount, currency, rate, received, pending, tip, user, id } =
+ data.invoice);
- toast.pop(0);
+ toast.pop(0);
</script>
<div class="container px-4 text-center mx-auto">
- <div class="flex w-full py-5 max-w-[200px] mx-auto" in:scale={{ start: 0.5 }}>
- <Icon icon="check" style="mx-auto" />
- </div>
- {#if received}
- <h1 class="text-3xl md:text-4xl font-bold mb-6">{$t('invoice.paymentSuccessful')}</h1>
- <h2 class="text-2xl md:text-3xl font-semibold">
- {f(fiat(received - tip, rate), currency)}
- {#if tip}
- <span class="text-lg">
- + {f(fiat(tip, rate), currency)}
- </span>
- {/if}
- </h2>
- <h3 class="text-secondary md:text-lg mb-6 mt-1">
- ⚡️{s(received - tip)}
+ <div class="flex w-full py-5 max-w-[200px] mx-auto" in:scale={{ start: 0.5 }}>
+ <Icon icon="check" style="mx-auto" />
+ </div>
+ {#if received}
+ <h1 class="text-3xl md:text-4xl font-bold mb-6">
+ {$t("invoice.paymentSuccessful")}
+ </h1>
+ <h2 class="text-2xl md:text-3xl font-semibold">
+ {f(fiat(received - tip, rate), currency)}
+ {#if tip}
+ <span class="text-lg">
+ + {f(fiat(tip, rate), currency)}
+ </span>
+ {/if}
+ </h2>
+ <h3 class="text-secondary md:text-lg mb-6 mt-1">
+ ⚡️{s(received - tip)}
- {#if tip}
- <span class="text-lg">
- + ⚡️{s(tip)}
- </span>
- {/if}
- </h3>
- {/if}
+ {#if tip}
+ <span class="text-lg">
+ + ⚡️{s(tip)}
+ </span>
+ {/if}
+ </h3>
+ {/if}
- {#if pending}
- <h1 class="text-3xl md:text-4xl font-bold mb-6">Payment detected</h1>
- <h2 class="text-2xl md:text-3xl font-semibold">
- {f(fiat(pending - tip, rate), currency)}
- {#if tip}
- + {f(fiat(tip, rate), currency)}
- {/if}
- </h2>
- <h3 class="text-secondary md:text-lg mb-6 mt-1">
- ⚡️{s(pending - tip)}
- {#if tip}
- <span class="text-lg">
- + ⚡️{s(tip)}
- </span>
- {/if}
- </h3>
- {/if}
+ {#if pending}
+ <h1 class="text-3xl md:text-4xl font-bold mb-6">Payment detected</h1>
+ <h2 class="text-2xl md:text-3xl font-semibold">
+ {f(fiat(pending - tip, rate), currency)}
+ {#if tip}
+ + {f(fiat(tip, rate), currency)}
+ {/if}
+ </h2>
+ <h3 class="text-secondary md:text-lg mb-6 mt-1">
+ ⚡️{s(pending - tip)}
+ {#if tip}
+ <span class="text-lg">
+ + ⚡️{s(tip)}
+ </span>
+ {/if}
+ </h3>
+ {/if}
</div>
<a href={`/${user.username}/payments`}>
- <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" />
+ <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" />
</a>
-<div class="fixed bottom-10 w-full text-center">{$t('payments.tapAnywhere')}</div>
+<div class="fixed bottom-10 w-full text-center">
+ {$t("payments.tapAnywhere")}
+</div>
diff --git a/src/routes/(app)/[username]/invoice/[id]/tip/+page.server.js b/src/routes/(app)/[username]/invoice/[id]/tip/+page.server.js
index 814900fb..c9377e36 100644
--- a/src/routes/(app)/[username]/invoice/[id]/tip/+page.server.js
+++ b/src/routes/(app)/[username]/invoice/[id]/tip/+page.server.js
@@ -1,11 +1,12 @@
-import invoice from '$lib/invoice';
-import { redirect } from '@sveltejs/kit';
+import invoice from "$lib/invoice";
+import { redirect } from "@sveltejs/kit";
export async function load({ parent, params }) {
- let { invoice } = await parent();
- if (!invoice.amount) throw redirect(307, `/${params.username}/invoice/${invoice.id}`);
+ let { invoice } = await parent();
+ if (!invoice.amount)
+ throw redirect(307, `/${params.username}/invoice/${invoice.id}`);
}
export const actions = {
- default: invoice
+ default: invoice,
};
diff --git a/src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte b/src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte
index fe6c67be..87e48f01 100644
--- a/src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte
+++ b/src/routes/(app)/[username]/invoice/[id]/tip/+page.svelte
@@ -1,201 +1,203 @@
<script>
- import { onMount } from 'svelte';
- import { browser } from '$app/environment';
- import { invoice as inv, request } from '$lib/store';
- import { copy, focus, f, sat, get, reverseFormat, s, sats } from '$lib/utils';
- import { Icon, Slider } from '$comp';
- import { goto } from '$app/navigation';
- import { t } from '$lib/translations';
- import { enhance } from '$app/forms';
- import { tick } from 'svelte';
+ import { onMount } from "svelte";
+ import { browser } from "$app/environment";
+ import { invoice as inv, request } from "$lib/store";
+ import { copy, focus, f, sat, get, reverseFormat, s, sats } from "$lib/utils";
+ import { Icon, Slider } from "$comp";
+ import { goto } from "$app/navigation";
+ import { t } from "$lib/translations";
+ import { enhance } from "$app/forms";
+ import { tick } from "svelte";
- const tipAmounts = ['No', '10%', '15%', '20%'];
- let showCustomAmount;
+ const tipAmounts = ["No", "10%", "15%", "20%"];
+ let showCustomAmount;
- let customInput;
- let customTipAmount;
+ let customInput;
+ let customTipAmount;
- let apply = async () => {
- if (customTipAmount > 0) {
- tipPercent = (customTipAmount / amountFiat) * 100;
- showCustomAmount = false;
- }
- };
+ let apply = async () => {
+ if (customTipAmount > 0) {
+ tipPercent = (customTipAmount / amountFiat) * 100;
+ showCustomAmount = false;
+ }
+ };
- let active = (amount) =>
- !customTipAmount && Math.round(tipPercent) === parseInt(amount.slice(0, 2));
+ let active = (amount) =>
+ !customTipAmount && Math.round(tipPercent) === parseInt(amount.slice(0, 2));
- const handleCustomTipAmount = (e) => {
- tipPercent = 0;
- customTipAmount = e.target.value;
- };
+ const handleCustomTipAmount = (e) => {
+ tipPercent = 0;
+ customTipAmount = e.target.value;
+ };
- let timeout;
- const handleSlide = (e) => {
- customTipAmount = '';
- if (customInput) {
- customInput.value = '';
- }
- tipPercent = e.target.value;
- };
+ let timeout;
+ const handleSlide = (e) => {
+ customTipAmount = "";
+ if (customInput) {
+ customInput.value = "";
+ }
+ tipPercent = e.target.value;
+ };
- let submitting;
- const handleTipButtonClick = async (amount) => {
- submitting = true;
- if (amount === 'No') {
- tipPercent = 0;
- } else {
- tipPercent = parseInt(amount.slice(0, 2));
- }
+ let submitting;
+ const handleTipButtonClick = async (amount) => {
+ submitting = true;
+ if (amount === "No") {
+ tipPercent = 0;
+ } else {
+ tipPercent = parseInt(amount.slice(0, 2));
+ }
- await tick();
- submit.click();
- };
+ await tick();
+ submit.click();
+ };
- let submit;
+ let submit;
- export let data;
- $: refresh(data);
- let { invoice, id, user, rates } = data;
- let {
- amount,
- hash,
- type,
- rate,
- received,
- prompt,
- text,
- tip,
- user: { username, currency }
- } = invoice;
+ export let data;
+ $: refresh(data);
+ let { invoice, id, user, rates } = data;
+ let {
+ amount,
+ hash,
+ type,
+ rate,
+ received,
+ prompt,
+ text,
+ tip,
+ user: { username, currency },
+ } = invoice;
- let qr;
- let tipPercent = 0;
+ let qr;
+ let tipPercent = 0;
- let fullscreen;
+ let fullscreen;
- let refresh = (data) => {
- $inv = null;
+ let refresh = (data) => {
+ $inv = null;
- ({ invoice, id } = data);
- ({
- amount,
- hash,
- type,
- received,
- prompt,
- text,
- tip,
- user: { username }
- } = invoice);
+ ({ invoice, id } = data);
+ ({
+ amount,
+ hash,
+ type,
+ received,
+ prompt,
+ text,
+ tip,
+ user: { username },
+ } = invoice);
- tipPercent = (tip / amount) * 100;
- };
+ tipPercent = (tip / amount) * 100;
+ };
- $: currency = user?.currency || invoice.currency;
- $: rate = invoice.rate * (rates[currency] / rates[invoice.currency]);
+ $: currency = user?.currency || invoice.currency;
+ $: rate = invoice.rate * (rates[currency] / rates[invoice.currency]);
- $: tip = Math.round((amount / 100) * tipPercent);
+ $: tip = Math.round((amount / 100) * tipPercent);
- $: amountFiat = parseFloat(((amount * rate) / sats).toFixed(2));
+ $: amountFiat = parseFloat(((amount * rate) / sats).toFixed(2));
- $: tipAmount = ((tip * rate) / sats).toFixed(2);
+ $: tipAmount = ((tip * rate) / sats).toFixed(2);
- $: invoiceAmountFiatFormatted = f(amountFiat, currency);
+ $: invoiceAmountFiatFormatted = f(amountFiat, currency);
- onMount(() => ($request = undefined));
+ onMount(() => ($request = undefined));
</script>
<div class="container px-4 max-w-lg text-center mx-auto">
- <form method="POST" use:enhance class:invisible={submitting}>
- <input type="hidden" name="amount" value={amount} />
- <input type="hidden" name="tip" value={tip} />
- <input type="hidden" name="username" value={username} />
- <input type="hidden" name="rate" value={invoice.rate} />
- <input type="hidden" name="prompt" value="false" />
- <input type="hidden" name="type" value={type} />
- <input type="hidden" name="hash" value={hash} />
+ <form method="POST" use:enhance class:invisible={submitting}>
+ <input type="hidden" name="amount" value={amount} />
+ <input type="hidden" name="tip" value={tip} />
+ <input type="hidden" name="username" value={username} />
+ <input type="hidden" name="rate" value={invoice.rate} />
+ <input type="hidden" name="prompt" value="false" />
+ <input type="hidden" name="type" value={type} />
+ <input type="hidden" name="hash" value={hash} />
- <input type="hidden" name="request_id" value={invoice.request?.id} />
+ <input type="hidden" name="request_id" value={invoice.request?.id} />
- <h1 class="text-4xl font-semibold my-8">{$t('invoice.addTipq')}</h1>
- {#if !showCustomAmount}
- <div class="my-2 text-xl">{Math.round(tipPercent)}%</div>
+ <h1 class="text-4xl font-semibold my-8">{$t("invoice.addTipq")}</h1>
+ {#if !showCustomAmount}
+ <div class="my-2 text-xl">{Math.round(tipPercent)}%</div>
- <Slider handle={handleSlide} bind:value={tipPercent} />
+ <Slider handle={handleSlide} bind:value={tipPercent} />
- <div class="flex mb-8 text-lg">
- <div>
- <span class="mr-1">
- {f(amountFiat, currency)}
- <span class="font-semibold">+{f(tipAmount, currency)}</span>
- </span>
- </div>
+ <div class="flex mb-8 text-lg">
+ <div>
+ <span class="mr-1">
+ {f(amountFiat, currency)}
+ <span class="font-semibold">+{f(tipAmount, currency)}</span>
+ </span>
+ </div>
- <div class="ml-auto text-lg">
- {sat(amount)}
- <span class="font-semibold">+{sat(tip)}</span>
- </div>
- </div>
+ <div class="ml-auto text-lg">
+ {sat(amount)}
+ <span class="font-semibold">+{sat(tip)}</span>
+ </div>
+ </div>
- {#if tip}
- <div>
- <button type="button" on:click={() => submit.click()}>Next</button>
- <button type="button" on:click={() => (tipPercent = 0)}>Reset</button>
- </div>
- {:else}
- {#each tipAmounts as amount}
- <button
- type="button"
- class:active={active(amount, tipPercent)}
- on:click={() => handleTipButtonClick(amount)}>{amount}</button
- >
- {/each}
- {/if}
+ {#if tip}
+ <div>
+ <button type="button" on:click={() => submit.click()}>Next</button>
+ <button type="button" on:click={() => (tipPercent = 0)}>Reset</button>
+ </div>
+ {:else}
+ {#each tipAmounts as amount}
+ <button
+ type="button"
+ class:active={active(amount, tipPercent)}
+ on:click={() => handleTipButtonClick(amount)}>{amount}</button
+ >
+ {/each}
+ {/if}
- <div>
- <button type="button" on:click={() => (showCustomAmount = true)}>Custom</button>
- </div>
- {/if}
+ <div>
+ <button type="button" on:click={() => (showCustomAmount = true)}
+ >Custom</button
+ >
+ </div>
+ {/if}
- <button type="submit" bind:this={submit} />
- </form>
+ <button type="submit" bind:this={submit} />
+ </form>
- {#if showCustomAmount}
- <form on:submit|preventDefault={apply}>
- <div class="relative">
- <span
- class="absolute top-[18px] left-5 font-semibold {!customTipAmount
- ? 'opacity-50'
- : 'opacity-100'}">$</span
- >
- <input
- bind:this={customInput}
- type="number"
- step="0.01"
- on:input={(e) => handleCustomTipAmount(e)}
- class="pl-10"
- placeholder="Custom amount"
- use:focus
- />
- <button
- type="submit"
- class="w-full mt-2 bg-black text-white font-semibold py-3 px-7 rounded-2xl {!customTipAmount
- ? 'opacity-50'
- : 'opacity-100 hover:opacity-80'}"
- disabled={!customTipAmount}>Apply</button
- >
- </div>
- </form>
- {/if}
+ {#if showCustomAmount}
+ <form on:submit|preventDefault={apply}>
+ <div class="relative">
+ <span
+ class="absolute top-[18px] left-5 font-semibold {!customTipAmount
+ ? 'opacity-50'
+ : 'opacity-100'}">$</span
+ >
+ <input
+ bind:this={customInput}
+ type="number"
+ step="0.01"
+ on:input={(e) => handleCustomTipAmount(e)}
+ class="pl-10"
+ placeholder="Custom amount"
+ use:focus
+ />
+ <button
+ type="submit"
+ class="w-full mt-2 bg-black text-white font-semibold py-3 px-7 rounded-2xl {!customTipAmount
+ ? 'opacity-50'
+ : 'opacity-100 hover:opacity-80'}"
+ disabled={!customTipAmount}>Apply</button
+ >
+ </div>
+ </form>
+ {/if}
</div>
<style>
- .active {
- @apply !bg-black !text-white;
- }
+ .active {
+ @apply !bg-black !text-white;
+ }
- .container button[type='button'] {
- @apply w-full mt-2 bg-primary text-black hover:bg-black hover:text-white font-semibold py-5 px-7 rounded-2xl;
- }
+ .container button[type="button"] {
+ @apply w-full mt-2 bg-primary text-black hover:bg-black hover:text-white font-semibold py-5 px-7 rounded-2xl;
+ }
</style>
diff --git a/src/routes/(app)/[username]/invoice/sent/+page.svelte b/src/routes/(app)/[username]/invoice/sent/+page.svelte
index ae71eb2e..8583de09 100644
--- a/src/routes/(app)/[username]/invoice/sent/+page.svelte
+++ b/src/routes/(app)/[username]/invoice/sent/+page.svelte
@@ -1,10 +1,10 @@
<script>
- import { Avatar, Icon } from '$comp';
- import { request } from '$lib/store';
+ import { Avatar, Icon } from "$comp";
+ import { request } from "$lib/store";
</script>
Invoice sent to
<div class="flex">
- <Avatar user={$request.requester} size={12} />
- <h1 class="my-auto"><b>{$request.requester.username}</b> is ready to pay</h1>
+ <Avatar user={$request.requester} size={12} />
+ <h1 class="my-auto"><b>{$request.requester.username}</b> is ready to pay</h1>
</div>
diff --git a/src/routes/(app)/[username]/map/+page.server.js b/src/routes/(app)/[username]/map/+page.server.js
index 7936e88b..616d28f6 100644
--- a/src/routes/(app)/[username]/map/+page.server.js
+++ b/src/routes/(app)/[username]/map/+page.server.js
@@ -1,12 +1,12 @@
-import { get } from '$lib/utils';
+import { get } from "$lib/utils";
// export const prerender = true;
export let load = async () => {
- let data = { locations: [] };
+ let data = { locations: [] };
- try {
- data = await get('/locations');
- } catch (e) {}
+ try {
+ data = await get("/locations");
+ } catch (e) {}
- return data;
+ return data;
};
diff --git a/src/routes/(app)/[username]/map/+page.svelte b/src/routes/(app)/[username]/map/+page.svelte
index 99c822d3..a2ae657c 100644
--- a/src/routes/(app)/[username]/map/+page.svelte
+++ b/src/routes/(app)/[username]/map/+page.svelte
@@ -1,8 +1,8 @@
<script>
- import { LocationsMap } from '$comp';
- export let data;
+ import { LocationsMap } from "$comp";
+ export let data;
- let { locations } = data;
+ let { locations } = data;
</script>
<LocationsMap {locations} />
diff --git a/src/routes/(app)/[username]/messages/+page.server.js b/src/routes/(app)/[username]/messages/+page.server.js
index ec5c5eee..f0148be7 100644
--- a/src/routes/(app)/[username]/messages/+page.server.js
+++ b/src/routes/(app)/[username]/messages/+page.server.js
@@ -1,22 +1,25 @@
-import { get } from '$lib/utils';
+import { get } from "$lib/utils";
export async function load({ params, parent, url }) {
- let { user, subject } = await parent();
- let { since = 0 } = params;
+ let { user, subject } = await parent();
+ let { since = 0 } = params;
- let messages = [];
+ let messages = [];
- try {
- messages = await get(`/${user.pubkey}/${since}/messages`);
+ try {
+ messages = await get(`/${user.pubkey}/${since}/messages`);
- messages = messages
- .filter((m) => m.recipient?.id === subject.id || m.author?.id === subject.id)
- .sort((a, b) => a.created_at - b.created_at);
+ messages = messages
+ .filter(
+ (m) => m.recipient?.id === subject.id || m.author?.id === subject.id
+ )
+ .sort((a, b) => a.created_at - b.created_at);
- if (user.id === subject.id) messages = messages.filter((m) => m.recipient.id === m.author.id);
- } catch (e) {
- console.log(`failed to fetch nostr messages`, e);
- }
+ if (user.id === subject.id)
+ messages = messages.filter((m) => m.recipient.id === m.author.id);
+ } catch (e) {
+ console.log(`failed to fetch nostr messages`, e);
+ }
- return { messages, user };
+ return { messages, user };
}
diff --git a/src/routes/(app)/[username]/messages/+page.svelte b/src/routes/(app)/[username]/messages/+page.svelte
index cfa7f741..16b82679 100644
--- a/src/routes/(app)/[username]/messages/+page.svelte
+++ b/src/routes/(app)/[username]/messages/+page.svelte
@@ -1,171 +1,182 @@
<script>
- import { cubicInOut } from 'svelte/easing';
- import { browser } from '$app/environment';
- import { t } from '$lib/translations';
- import { format } from 'date-fns';
- import { scale, fade, fly } from 'svelte/transition';
- import { enhance } from '$app/forms';
- import { Avatar, Icon } from '$comp';
- import { back, fail, focus } from '$lib/utils';
- import { sign, send, encrypt, decrypt } from '$lib/nostr';
- import { event as e, password } from '$lib/store';
- import { tick, onMount } from 'svelte';
- import { getEventHash } from 'nostr-tools';
+ import { cubicInOut } from "svelte/easing";
+ import { browser } from "$app/environment";
+ import { t } from "$lib/translations";
+ import { format } from "date-fns";
+ import { scale, fade, fly } from "svelte/transition";
+ import { enhance } from "$app/forms";
+ import { Avatar, Icon } from "$comp";
+ import { back, fail, focus } from "$lib/utils";
+ import { sign, send, encrypt, decrypt } from "$lib/nostr";
+ import { event as e, password } from "$lib/store";
+ import { tick, onMount } from "svelte";
+ import { getEventHash } from "nostr-tools";
- export let data;
+ export let data;
- function fadeScale(node, { delay = 0, duration = 200, easing = (x) => x, baseScale = 0 }) {
- const o = +getComputedStyle(node).opacity;
- const m = getComputedStyle(node).transform.match(/scale\(([0-9.]+)\)/);
- const s = m ? m[1] : 1;
- const is = 1 - baseScale;
+ function fadeScale(
+ node,
+ { delay = 0, duration = 200, easing = (x) => x, baseScale = 0 }
+ ) {
+ const o = +getComputedStyle(node).opacity;
+ const m = getComputedStyle(node).transform.match(/scale\(([0-9.]+)\)/);
+ const s = m ? m[1] : 1;
+ const is = 1 - baseScale;
- return {
- delay,
- duration,
- css: (t) => {
- const eased = easing(t);
- return `opacity: ${eased * o}; transform: scale(${eased * s * is + baseScale})`;
- }
- };
- }
+ return {
+ delay,
+ duration,
+ css: (t) => {
+ const eased = easing(t);
+ return `opacity: ${eased * o}; transform: scale(${
+ eased * s * is + baseScale
+ })`;
+ },
+ };
+ }
- let { messages, subject, user } = data;
- let input, pane;
+ let { messages, subject, user } = data;
+ let input, pane;
- $: initialize($password);
- let initialize = async (p) => {
- if (!p) return;
- await Promise.all(
- messages.map(async (event) => (event.message = await decrypt({ event, user })))
- );
+ $: initialize($password);
+ let initialize = async (p) => {
+ if (!p) return;
+ await Promise.all(
+ messages.map(
+ async (event) => (event.message = await decrypt({ event, user }))
+ )
+ );
- messages = messages;
- tick().then(() => pane && (pane.scrollTop = pane.scrollHeight));
- };
+ messages = messages;
+ tick().then(() => pane && (pane.scrollTop = pane.scrollHeight));
+ };
- e.subscribe(async (event) => {
- let found = ~messages.findIndex((m) => m.id === event?.id);
- if (event?.recipient.id === user.id && !found) {
- event.message = await decrypt({ event, user });
- messages.push(event);
- messages = messages;
- tick().then(() => pane && (pane.scrollTop = pane.scrollHeight));
- }
- });
+ e.subscribe(async (event) => {
+ let found = ~messages.findIndex((m) => m.id === event?.id);
+ if (event?.recipient.id === user.id && !found) {
+ event.message = await decrypt({ event, user });
+ messages.push(event);
+ messages = messages;
+ tick().then(() => pane && (pane.scrollTop = pane.scrollHeight));
+ }
+ });
- let sent, submitting, message;
- let submit = async () => {
- submitting = true;
+ let sent, submitting, message;
+ let submit = async () => {
+ submitting = true;
- let event = {
- pubkey: user.pubkey,
- created_at: Math.floor(Date.now() / 1000),
- kind: 4,
- tags: [['p', subject.pubkey]]
- };
+ let event = {
+ pubkey: user.pubkey,
+ created_at: Math.floor(Date.now() / 1000),
+ kind: 4,
+ tags: [["p", subject.pubkey]],
+ };
- event.message = message;
+ event.message = message;
- message = '';
+ message = "";
- event.author = user;
- event.recipient = subject;
+ event.author = user;
+ event.recipient = subject;
- messages.push(event);
- messages = messages;
- tick().then(() => pane && (pane.scrollTop = pane.scrollHeight));
+ messages.push(event);
+ messages = messages;
+ tick().then(() => pane && (pane.scrollTop = pane.scrollHeight));
- try {
- event.content = await encrypt({ message: event.message, recipient: subject.pubkey, user });
- event.id = getEventHash(event);
- await sign({ event, user });
- await send(event);
+ try {
+ event.content = await encrypt({
+ message: event.message,
+ recipient: subject.pubkey,
+ user,
+ });
+ event.id = getEventHash(event);
+ await sign({ event, user });
+ await send(event);
- sent = true;
- } catch (e) {
- console.log(e);
- fail('Failed to send message');
- }
+ sent = true;
+ } catch (e) {
+ console.log(e);
+ fail("Failed to send message");
+ }
- message = '';
- submitting = false;
- };
+ message = "";
+ submitting = false;
+ };
- let keydown = (e) => {
- if (e.key === 'Enter') {
- e.preventDefault();
- submit();
- }
- };
+ let keydown = (e) => {
+ if (e.key === "Enter") {
+ e.preventDefault();
+ submit();
+ }
+ };
</script>
<div class="container max-w-xl mx-auto px-4 space-y-5">
- <div
- class="h-[50vh] max-h-[50vh] overflow-y-scroll scrollbar-thin scrollbar-thumb-[#F2F6FC] scrollbar-track-white pr-8"
- bind:this={pane}
- >
- {#each messages as { id, author, message, created_at, pubkey }}
- {@const ours = pubkey === user.pubkey}
- {@const theirs = !ours}
+ <div
+ class="h-[50vh] max-h-[50vh] overflow-y-scroll scrollbar-thin scrollbar-thumb-[#F2F6FC] scrollbar-track-white pr-8"
+ bind:this={pane}
+ >
+ {#each messages as { id, author, message, created_at, pubkey }}
+ {@const ours = pubkey === user.pubkey}
+ {@const theirs = !ours}
- {#if message}
- <div
- class="flex gap-2"
- class:flex-row-reverse={theirs}
- class:justify-end={theirs}
- in:fadeScale={{
- easing: cubicInOut,
- baseScale: 0.5
- }}
- >
- <div
- class="rounded-2xl px-4 py-2 max-w-[300px] mb-1 text-lg"
- class:ours
- class:theirs
- :key={id}
- >
- {message}
- </div>
- <div class="mt-auto">
- <Avatar user={author} size={'12'} />
- </div>
- </div>
- <div class="text-sm text-gray-400 mb-6" class:text-right={ours}>
- {format(new Date(created_at * 1000), 'MMM d')},
- {format(new Date(created_at * 1000), 'h:mm aa')}
- </div>
- {/if}
- {/each}
- </div>
+ {#if message}
+ <div
+ class="flex gap-2"
+ class:flex-row-reverse={theirs}
+ class:justify-end={theirs}
+ in:fadeScale={{
+ easing: cubicInOut,
+ baseScale: 0.5,
+ }}
+ >
+ <div
+ class="rounded-2xl px-4 py-2 max-w-[300px] mb-1 text-lg"
+ class:ours
+ class:theirs
+ :key={id}
+ >
+ {message}
+ </div>
+ <div class="mt-auto">
+ <Avatar user={author} size={"12"} />
+ </div>
+ </div>
+ <div class="text-sm text-gray-400 mb-6" class:text-right={ours}>
+ {format(new Date(created_at * 1000), "MMM d")},
+ {format(new Date(created_at * 1000), "h:mm aa")}
+ </div>
+ {/if}
+ {/each}
+ </div>
- <form
- method="POST"
- class="w-full border rounded-xl outline-none gap-4 flex p-0 pr-2"
- on:submit|preventDefault={submit}
- >
- <input type="hidden" name="requester_id" value={user.id} />
- <input type="hidden" name="recipient" value={subject.username} />
+ <form
+ method="POST"
+ class="w-full border rounded-xl outline-none gap-4 flex p-0 pr-2"
+ on:submit|preventDefault={submit}
+ >
+ <input type="hidden" name="requester_id" value={user.id} />
+ <input type="hidden" name="recipient" value={subject.username} />
- <div
- use:focus
- contenteditable
- class="grow break-all py-4 outline-none mt-0 pl-4"
- bind:innerHTML={message}
- on:keydown={keydown}
- />
- <button type="submit" class="my-auto shrink-0">
- <Icon icon="send" style="w-8" />
- </button>
- </form>
+ <div
+ use:focus
+ contenteditable
+ class="grow break-all py-4 outline-none mt-0 pl-4"
+ bind:innerHTML={message}
+ on:keydown={keydown}
+ />
+ <button type="submit" class="my-auto shrink-0">
+ <Icon icon="send" style="w-8" />
+ </button>
+ </form>
</div>
<style>
- .ours {
- @apply bg-gradient-to-r from-[#F2F6FC] to-[#E1E3FF] text-black rounded-br-none ml-auto;
- }
+ .ours {
+ @apply bg-gradient-to-r from-[#F2F6FC] to-[#E1E3FF] text-black rounded-br-none ml-auto;
+ }
- .theirs {
- @apply bg-gray-100 text-black rounded-bl-none;
- }
+ .theirs {
+ @apply bg-gray-100 text-black rounded-bl-none;
+ }
</style>
diff --git a/src/routes/(app)/[username]/payments/[...page]/+page.server.js b/src/routes/(app)/[username]/payments/[...page]/+page.server.js
index a3b0ad6f..7a3bc179 100644
--- a/src/routes/(app)/[username]/payments/[...page]/+page.server.js
+++ b/src/routes/(app)/[username]/payments/[...page]/+page.server.js
@@ -1,26 +1,26 @@
-import { get, auth } from '$lib/utils';
+import { get, auth } from "$lib/utils";
let limit = 25;
export async function load({ cookies, depends, params, request }) {
- depends('app:payments');
+ depends("app:payments");
- let parts = params.page.split('/');
+ let parts = params.page.split("/");
- let start, end, page;
- if (parts.length === 1) [page] = parts;
- if (parts.length === 2) [start, page] = parts;
- if (parts.length === 3) [start, end, page] = parts;
- if (!parseInt(page)) page = 1;
+ let start, end, page;
+ if (parts.length === 1) [page] = parts;
+ if (parts.length === 2) [start, page] = parts;
+ if (parts.length === 3) [start, end, page] = parts;
+ if (!parseInt(page)) page = 1;
- let offset = (page - 1) * limit;
- if (!start) start = Date.now() / 1000 - 24 * 60 * 60;
+ let offset = (page - 1) * limit;
+ if (!start) start = Date.now() / 1000 - 24 * 60 * 60;
- let url = `/payments?start=${start * 1000}&limit=${limit}&offset=${offset}`;
- if (end) url += `&end=${end * 1000}`;
+ let url = `/payments?start=${start * 1000}&limit=${limit}&offset=${offset}`;
+ if (end) url += `&end=${end * 1000}`;
- let { count, payments, totals } = await get(url, auth(cookies));
- let pages = new Array(Math.ceil(count / limit));
+ let { count, payments, totals } = await get(url, auth(cookies));
+ let pages = new Array(Math.ceil(count / limit));
- return { payments, page, pages, start, end, totals };
+ return { payments, page, pages, start, end, totals };
}
diff --git a/src/routes/(app)/[username]/payments/[...page]/+page.svelte b/src/routes/(app)/[username]/payments/[...page]/+page.svelte
index 2ea225ad..e61e156c 100644
--- a/src/routes/(app)/[username]/payments/[...page]/+page.svelte
+++ b/src/routes/(app)/[username]/payments/[...page]/+page.svelte
@@ -1,249 +1,281 @@
<script>
- import { Avatar, Icon } from '$comp';
- import { onMount } from 'svelte';
- import { format } from 'date-fns';
- import { newPayment } from '$lib/store';
- import { t } from '$lib/translations';
- import { get, f, s, sat, sats, types } from '$lib/utils';
- import { page } from '$app/stores';
- import { differenceInDays, getUnixTime, sub } from 'date-fns';
- import { goto, invalidate } from '$app/navigation';
+ import { Avatar, Icon } from "$comp";
+ import { onMount } from "svelte";
+ import { format } from "date-fns";
+ import { newPayment } from "$lib/store";
+ import { t } from "$lib/translations";
+ import { get, f, s, sat, sats, types } from "$lib/utils";
+ import { page } from "$app/stores";
+ import { differenceInDays, getUnixTime, sub } from "date-fns";
+ import { goto, invalidate } from "$app/navigation";
- export let data;
+ export let data;
- let { start, end, user, rates, payments } = data;
+ let { start, end, user, rates, payments } = data;
- let change = ({ target: { value } }) => goto(value);
- let link = (p) => {
- if (p.pot) return `/pot/${p.pot}`;
- if (p.with) return '/' + p.with.username;
- return `/p/${p.id}`;
- };
+ let change = ({ target: { value } }) => goto(value);
+ let link = (p) => {
+ if (p.pot) return `/pot/${p.pot}`;
+ if (p.with) return "/" + p.with.username;
+ return `/p/${p.id}`;
+ };
- $: presets = [
- { title: $t('payments.day'), start: sub(new Date(), { days: 1 }), end: null },
- { title: $t('payments.week'), start: sub(new Date(), { days: 7 }), end: null },
- { title: $t('payments.month'), start: sub(new Date(), { months: 1 }), end: null },
- { title: $t('payments.all'), start: sub(new Date(), { years: 5 }), end: null }
- ];
+ $: presets = [
+ {
+ title: $t("payments.day"),
+ start: sub(new Date(), { days: 1 }),
+ end: null,
+ },
+ {
+ title: $t("payments.week"),
+ start: sub(new Date(), { days: 7 }),
+ end: null,
+ },
+ {
+ title: $t("payments.month"),
+ start: sub(new Date(), { months: 1 }),
+ end: null,
+ },
+ {
+ title: $t("payments.all"),
+ start: sub(new Date(), { years: 5 }),
+ end: null,
+ },
+ ];
- $: selection = start
- ? presets.findIndex((p) => Math.abs(differenceInDays(new Date(start * 1000), p.start)) < 1)
- : 0;
+ $: selection = start
+ ? presets.findIndex(
+ (p) => Math.abs(differenceInDays(new Date(start * 1000), p.start)) < 1
+ )
+ : 0;
- let p,
- totals,
- pages = [];
- $: data && ({ page: p, pages, start, end, totals, payments } = data);
+ let p,
+ totals,
+ pages = [];
+ $: data && ({ page: p, pages, start, end, totals, payments } = data);
- $: $page && ($newPayment = false);
- $: $newPayment && invalidate(`/users/${user.username}`);
+ $: $page && ($newPayment = false);
+ $: $newPayment && invalidate(`/users/${user.username}`);
- $: path = $page.params.page
- ? $page.url.pathname.substring(0, $page.url.pathname.lastIndexOf('/'))
- : $page.url.pathname;
+ $: path = $page.params.page
+ ? $page.url.pathname.substring(0, $page.url.pathname.lastIndexOf("/"))
+ : $page.url.pathname;
- let csv = async () => {
- let url = `/${user.username}/payments`;
- if (start) url += `/${start}`;
- if (end) url += `/${end}`;
+ let csv = async () => {
+ let url = `/${user.username}/payments`;
+ if (start) url += `/${start}`;
+ if (end) url += `/${end}`;
- let { payments } = await get(url);
+ let { payments } = await get(url);
- payments = payments.map((p) => ({
- ...p,
- created: new Date(p.created),
- total: p.amount + (p.tip || 0),
- amount_fiat: f((p.amount * p.rate) / sats, p.currency),
- fee_fiat: p.fee ? f((p.fee * p.rate) / sats, p.currency) : null,
- tip_fiat: p.tip ? f((p.tip * p.rate) / sats, p.currency) : null,
- total_fiat: f(((p.amount + (p.tip || 0)) * p.rate) / sats, p.currency)
- }));
+ payments = payments.map((p) => ({
+ ...p,
+ created: new Date(p.created),
+ total: p.amount + (p.tip || 0),
+ amount_fiat: f((p.amount * p.rate) / sats, p.currency),
+ fee_fiat: p.fee ? f((p.fee * p.rate) / sats, p.currency) : null,
+ tip_fiat: p.tip ? f((p.tip * p.rate) / sats, p.currency) : null,
+ total_fiat: f(((p.amount + (p.tip || 0)) * p.rate) / sats, p.currency),
+ }));
- let keys = [
- 'type',
- 'id',
- 'created',
- 'rate',
- 'currency',
- 'amount',
- 'tip',
- 'total',
- 'fee',
- 'amount_fiat',
- 'tip_fiat',
- 'total_fiat',
- 'fee_fiat'
- ];
+ let keys = [
+ "type",
+ "id",
+ "created",
+ "rate",
+ "currency",
+ "amount",
+ "tip",
+ "total",
+ "fee",
+ "amount_fiat",
+ "tip_fiat",
+ "total_fiat",
+ "fee_fiat",
+ ];
- let csv =
- keys.map((k) => `"${k}"`).join(',') +
- '\n' +
- payments.map((r) => keys.map((k) => `"${r[k] || ''}"`).join(',')).join('\n');
+ let csv =
+ keys.map((k) => `"${k}"`).join(",") +
+ "\n" +
+ payments
+ .map((r) => keys.map((k) => `"${r[k] || ""}"`).join(","))
+ .join("\n");
- let filename = 'payments.csv';
- let blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
- if (navigator.msSaveBlob) {
- navigator.msSaveBlob(blob, filename);
- } else {
- let link = document.createElement('a');
- if (link.download !== undefined) {
- let url = URL.createObjectURL(blob);
- link.setAttribute('href', url);
- link.setAttribute('download', filename);
- link.style.visibility = 'hidden';
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- }
- }
- };
+ let filename = "payments.csv";
+ let blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
+ if (navigator.msSaveBlob) {
+ navigator.msSaveBlob(blob, filename);
+ } else {
+ let link = document.createElement("a");
+ if (link.download !== undefined) {
+ let url = URL.createObjectURL(blob);
+ link.setAttribute("href", url);
+ link.setAttribute("download", filename);
+ link.style.visibility = "hidden";
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ }
+ }
+ };
</script>
<div class="mt-24 mb-20">
- <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10">
- {$t('payments.header')}
- </h1>
+ <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10">
+ {$t("payments.header")}
+ </h1>
- <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5">
- <div class="flex text-md text-secondary relative">
- <div class="mx-auto flex justify-center w-full gap-1">
- {#each presets as { start, end, title }, i}
- <a
- class:active={selection === i}
- href={`/${user.username}/payments/${getUnixTime(start) + '/'}1`}
- >
- <button
- class="text-sm md:text-lg rounded-full border py-2 px-4 hover:opacity-80 min-w-[72px]"
- >
- <div class="my-auto">{title}</div>
- </button>
- </a>
- {/each}
- </div>
- </div>
+ <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5">
+ <div class="flex text-md text-secondary relative">
+ <div class="mx-auto flex justify-center w-full gap-1">
+ {#each presets as { start, end, title }, i}
+ <a
+ class:active={selection === i}
+ href={`/${user.username}/payments/${getUnixTime(start) + "/"}1`}
+ >
+ <button
+ class="text-sm md:text-lg rounded-full border py-2 px-4 hover:opacity-80 min-w-[72px]"
+ >
+ <div class="my-auto">{title}</div>
+ </button>
+ </a>
+ {/each}
+ </div>
+ </div>
- <div class="flex flex-wrap justify-center mb-8">
- {#if pages.length > 1}
- {#each pages as _, i}
- <a class="mr-1 last:mr-0" href={`${path}/${i + 1}`} class:active={parseInt(p) === i + 1}>
- <div class="border py-2 rounded-full border-2 w-12 h-12 hover:opacity-80 text-center">
- {i + 1}
- </div>
- </a>
- {/each}
- {/if}
- </div>
+ <div class="flex flex-wrap justify-center mb-8">
+ {#if pages.length > 1}
+ {#each pages as _, i}
+ <a
+ class="mr-1 last:mr-0"
+ href={`${path}/${i + 1}`}
+ class:active={parseInt(p) === i + 1}
+ >
+ <div
+ class="border py-2 rounded-full border-2 w-12 h-12 hover:opacity-80 text-center"
+ >
+ {i + 1}
+ </div>
+ </a>
+ {/each}
+ {/if}
+ </div>
- <div class="text-base">
- {#each payments as p}
- <a href={`/payment/${p.id}`}>
- <div class="grid grid-cols-3 border-b h-24 hover:bg-gray-100 px-4">
- <div class="whitespace-nowrap my-auto">
- <div class="font-bold" class:text-red-800={p.amount < 0}>
- {f(Math.abs(p.amount) * (p.rate / sats), p.currency)}
- {#if p.tip}
- <span class="text-sm text-secondary">
- +{Math.round((p.tip / Math.abs(p.amount)) * 100)}%
- </span>
- {/if}
- </div>
+ <div class="text-base">
+ {#each payments as p}
+ <a href={`/payment/${p.id}`}>
+ <div class="grid grid-cols-3 border-b h-24 hover:bg-gray-100 px-4">
+ <div class="whitespace-nowrap my-auto">
+ <div class="font-bold" class:text-red-800={p.amount < 0}>
+ {f(Math.abs(p.amount) * (p.rate / sats), p.currency)}
+ {#if p.tip}
+ <span class="text-sm text-secondary">
+ +{Math.round((p.tip / Math.abs(p.amount)) * 100)}%
+ </span>
+ {/if}
+ </div>
- <div class="text-secondary">
- {sat(Math.abs(p.amount) + (p.tip || 0))}
- </div>
- </div>
+ <div class="text-secondary">
+ {sat(Math.abs(p.amount) + (p.tip || 0))}
+ </div>
+ </div>
- <div class="flex my-auto">
- {#if p.type === types.pot}
- <a href={`/pot/${p.memo}`}>
- <div class="text-secondary flex">
- <div class="my-auto mr-1">
- <img src="/images/moneypot.png" class="w-12" alt="Pot" />
- </div>
+ <div class="flex my-auto">
+ {#if p.type === types.pot}
+ <a href={`/pot/${p.memo}`}>
+ <div class="text-secondary flex">
+ <div class="my-auto mr-1">
+ <img src="/images/moneypot.png" class="w-12" alt="Pot" />
+ </div>
- <div class="my-auto">Pot</div>
- </div>
- </a>
- {:else if p.with}
- <div class="flex">
- <div class="my-auto">
- <Avatar user={p.with} size={16} disabled={true} />
- </div>
- <div class="my-auto ml-1 text-secondary">{p.with.username}</div>
- </div>
- {:else if p.type === types.classic}
- <img
- src="/images/classic.png"
- class="w-24 border-4 border-transparent mr-1"
- alt="Bitcoin"
- />
- <div class="my-auto text-secondary">{p.memo}</div>
- {:else}
- <div class="text-secondary flex">
- {#if p.type === types.lightning}
- <div class="text-3xl">⚡️</div>
- {:else if p.type === types.bitcoin}
- <div class="my-auto mr-1">
- <img
- src="/images/bitcoin.svg"
- class="w-12 border-4 border-transparent"
- alt="Bitcoin"
- />
- </div>
- {/if}
+ <div class="my-auto">Pot</div>
+ </div>
+ </a>
+ {:else if p.with}
+ <div class="flex">
+ <div class="my-auto">
+ <Avatar user={p.with} size={16} disabled={true} />
+ </div>
+ <div class="my-auto ml-1 text-secondary">
+ {p.with.username}
+ </div>
+ </div>
+ {:else if p.type === types.classic}
+ <img
+ src="/images/classic.png"
+ class="w-24 border-4 border-transparent mr-1"
+ alt="Bitcoin"
+ />
+ <div class="my-auto text-secondary">{p.memo}</div>
+ {:else}
+ <div class="text-secondary flex">
+ {#if p.type === types.lightning}
+ <div class="text-3xl">⚡️</div>
+ {:else if p.type === types.bitcoin}
+ <div class="my-auto mr-1">
+ <img
+ src="/images/bitcoin.svg"
+ class="w-12 border-4 border-transparent"
+ alt="Bitcoin"
+ />
+ </div>
+ {/if}
- <div class="my-auto">
- {p.amount > 0 ? (p.confirmed ? 'Received' : 'Pending') : 'Sent'}
- </div>
- </div>
- {/if}
- </div>
+ <div class="my-auto">
+ {p.amount > 0
+ ? p.confirmed
+ ? "Received"
+ : "Pending"
+ : "Sent"}
+ </div>
+ </div>
+ {/if}
+ </div>
- <div class="text-secondary text-right text-sm my-auto">
- <div>
- {format(new Date(p.created), 'h:mm aaa')}
- </div>
- <div>
- {format(new Date(p.created), 'MMM d')}
- </div>
- </div>
- </div>
- </a>
- {:else}
- <p class="text-secondary text-lg text-center">{$t('payments.empty')}</p>
- {/each}
- </div>
+ <div class="text-secondary text-right text-sm my-auto">
+ <div>
+ {format(new Date(p.created), "h:mm aaa")}
+ </div>
+ <div>
+ {format(new Date(p.created), "MMM d")}
+ </div>
+ </div>
+ </div>
+ </a>
+ {:else}
+ <p class="text-secondary text-lg text-center">{$t("payments.empty")}</p>
+ {/each}
+ </div>
- <div class="grid grid-cols-3 w-full text-center">
- <span class="text-lg text-secondary">{$t('payments.total')}</span>
- <span class="text-lg text-secondary">{$t('payments.presentValue')}</span>
- <span class="text-lg text-secondary">{$t('payments.gainLoss')}</span>
+ <div class="grid grid-cols-3 w-full text-center">
+ <span class="text-lg text-secondary">{$t("payments.total")}</span>
+ <span class="text-lg text-secondary">{$t("payments.presentValue")}</span>
+ <span class="text-lg text-secondary">{$t("payments.gainLoss")}</span>
- {#each Object.keys(totals) as c}
- {@const total = totals[c]['fiat']}
- {@const pv = (totals[c]['sats'] * rates[c]) / sats}
- {@const gain = (pv * 100) / total - 100}
- <span class="text-lg"><b>{f(total, c)}</b></span>
- <span class="text-lg"><b>{f(pv, c)}</b></span>
- <span class="text-lg" class:text-red-800={gain < 0}
- ><b>{Math.abs(gain).toFixed(2)}%</b></span
- >
- {/each}
- </div>
+ {#each Object.keys(totals) as c}
+ {@const total = totals[c]["fiat"]}
+ {@const pv = (totals[c]["sats"] * rates[c]) / sats}
+ {@const gain = (pv * 100) / total - 100}
+ <span class="text-lg"><b>{f(total, c)}</b></span>
+ <span class="text-lg"><b>{f(pv, c)}</b></span>
+ <span class="text-lg" class:text-red-800={gain < 0}
+ ><b>{Math.abs(gain).toFixed(2)}%</b></span
+ >
+ {/each}
+ </div>
- <button
- class="ml-auto rounded-full border py-2 px-4 w-36 hover:opacity-80 flex mx-auto"
- on:click={csv}
- >
- <Icon icon="save" style="opacity-50 mr-2 my-auto" />
- <div class="my-auto">{$t('payments.export')}</div>
- </button>
- </div>
+ <button
+ class="ml-auto rounded-full border py-2 px-4 w-36 hover:opacity-80 flex mx-auto"
+ on:click={csv}
+ >
+ <Icon icon="save" style="opacity-50 mr-2 my-auto" />
+ <div class="my-auto">{$t("payments.export")}</div>
+ </button>
+ </div>
</div>
<style>
- .active * {
- @apply bg-black text-white border-black;
- }
+ .active * {
+ @apply bg-black text-white border-black;
+ }
</style>
diff --git a/src/routes/(app)/[username]/payments/[...page]/+server.js b/src/routes/(app)/[username]/payments/[...page]/+server.js
index c7c5222d..a81faaf3 100644
--- a/src/routes/(app)/[username]/payments/[...page]/+server.js
+++ b/src/routes/(app)/[username]/payments/[...page]/+server.js
@@ -1,18 +1,18 @@
-import { get, auth } from '$lib/utils';
-import { json } from '@sveltejs/kit';
+import { get, auth } from "$lib/utils";
+import { json } from "@sveltejs/kit";
export async function GET({ cookies, params }) {
- let parts = params.page.split('/');
+ let parts = params.page.split("/");
- let start, end;
- if (parts.length === 1) [start] = parts;
- if (parts.length === 2) [start, end] = parts;
+ let start, end;
+ if (parts.length === 1) [start] = parts;
+ if (parts.length === 2) [start, end] = parts;
- if (!start) start = Date.now() / 1000 - 24 * 60 * 60;
+ if (!start) start = Date.now() / 1000 - 24 * 60 * 60;
- let url = `/payments?start=${start * 1000}`;
- if (end) url += `&end=${end * 1000}`;
+ let url = `/payments?start=${start * 1000}`;
+ if (end) url += `&end=${end * 1000}`;
- let { payments } = await get(url, auth(cookies));
- return json({ payments });
+ let { payments } = await get(url, auth(cookies));
+ return json({ payments });
}
diff --git a/src/routes/(app)/[username]/receive/[...param]/+page.server.js b/src/routes/(app)/[username]/receive/[...param]/+page.server.js
index cdd89c8b..a6e59175 100644
--- a/src/routes/(app)/[username]/receive/[...param]/+page.server.js
+++ b/src/routes/(app)/[username]/receive/[...param]/+page.server.js
@@ -1,15 +1,15 @@
-import { auth, get } from '$lib/utils';
-import invoice from '$lib/invoice';
+import { auth, get } from "$lib/utils";
+import invoice from "$lib/invoice";
export let load = async ({ cookies, params }) => {
- let id = params.param;
+ let id = params.param;
- let request;
- if (id) ({ request } = await get(`/request/${id}`, auth(cookies)));
+ let request;
+ if (id) ({ request } = await get(`/request/${id}`, auth(cookies)));
- return { ...params, request };
+ return { ...params, request };
};
export const actions = {
- default: invoice
+ default: invoice,
};
diff --git a/src/routes/(app)/[username]/receive/[...param]/+page.svelte b/src/routes/(app)/[username]/receive/[...param]/+page.svelte
index d7001079..085a6f6d 100644
--- a/src/routes/(app)/[username]/receive/[...param]/+page.svelte
+++ b/src/routes/(app)/[username]/receive/[...param]/+page.svelte
@@ -1,50 +1,54 @@
<script>
- import { Icon, Numpad, Toggle } from '$comp';
- import { t } from '$lib/translations';
- import { enhance } from '$app/forms';
- import { page } from '$app/stores';
- import { requestRedirect } from '$lib/store';
- import { browser } from '$app/environment';
+ import { Icon, Numpad, Toggle } from "$comp";
+ import { t } from "$lib/translations";
+ import { enhance } from "$app/forms";
+ import { page } from "$app/stores";
+ import { requestRedirect } from "$lib/store";
+ import { browser } from "$app/environment";
- export let data;
- let type = 'lightning';
+ export let data;
+ let type = "lightning";
- if ($page.params.param === 'bitcoin') type = 'bitcoin';
+ if ($page.params.param === "bitcoin") type = "bitcoin";
- let { request } = data;
+ let { request } = data;
- let amount;
+ let amount;
- let lightning = true;
- let { currency, username, prompt } = data.subject;
- let { rates, user } = data;
- let submit;
+ let lightning = true;
+ let { currency, username, prompt } = data.subject;
+ let { rates, user } = data;
+ let submit;
- $: rate = rates[currency];
- $: type = lightning ? 'lightning' : 'bitcoin';
+ $: rate = rates[currency];
+ $: type = lightning ? "lightning" : "bitcoin";
</script>
-<form method="POST" class="flex justify-center items-center mt-24 mb-3 px-3" use:enhance>
- <input type="hidden" name="amount" value={amount} />
- <input type="hidden" name="currency" value={currency} />
- <input type="hidden" name="username" value={username} />
- <input type="hidden" name="type" value={type} />
+<form
+ method="POST"
+ class="flex justify-center items-center mt-24 mb-3 px-3"
+ use:enhance
+>
+ <input type="hidden" name="amount" value={amount} />
+ <input type="hidden" name="currency" value={currency} />
+ <input type="hidden" name="username" value={username} />
+ <input type="hidden" name="type" value={type} />
- {#if request}
- <input type="hidden" name="request_id" value={request.id} />
- {/if}
+ {#if request}
+ <input type="hidden" name="request_id" value={request.id} />
+ {/if}
- <input type="hidden" name="prompt" value={prompt} />
+ <input type="hidden" name="prompt" value={prompt} />
- <div class="space-y-3 w-[300px] mx-auto">
- <Numpad bind:amount bind:currency bind:rate {submit} />
- <button
- bind:this={submit}
- type="submit"
- class="bg-black text-white rounded-xl w-full h-[48px] flex justify-center items-center font-semibold
+ <div class="space-y-3 w-[300px] mx-auto">
+ <Numpad bind:amount bind:currency bind:rate {submit} />
+ <button
+ bind:this={submit}
+ type="submit"
+ class="bg-black text-white rounded-xl w-full h-[48px] flex justify-center items-center font-semibold
opacity-100 hover:opacity-80"
- >
- {$t('payments.next')}
- </button>
- </div>
+ >
+ {$t("payments.next")}
+ </button>
+ </div>
</form>
diff --git a/src/routes/(app)/[username]/request/[id]/+page.server.js b/src/routes/(app)/[username]/request/[id]/+page.server.js
index 89792ab8..09cce465 100644
--- a/src/routes/(app)/[username]/request/[id]/+page.server.js
+++ b/src/routes/(app)/[username]/request/[id]/+page.server.js
@@ -1,25 +1,26 @@
-import { auth, get } from '$lib/utils';
-import { redirect } from '@sveltejs/kit';
+import { auth, get } from "$lib/utils";
+import { redirect } from "@sveltejs/kit";
export let load = async ({ cookies, depends, params, parent }) => {
- depends('app:invoice');
+ depends("app:invoice");
- let { user } = await parent();
- if (!user) throw redirect(307, '/login');
+ let { user } = await parent();
+ if (!user) throw redirect(307, "/login");
- let { request } = await get(`/request/${params.id}`, auth(cookies));
- let { invoice } = request;
+ let { request } = await get(`/request/${params.id}`, auth(cookies));
+ let { invoice } = request;
- if (invoice) {
- let { amount, received, id } = invoice;
- if (
- request.recipient.username === user.username &&
- ((received && !amount) || received >= amount)
- )
- throw redirect(307, `/${user.username}/invoice/${id}/paid`);
+ if (invoice) {
+ let { amount, received, id } = invoice;
+ if (
+ request.recipient.username === user.username &&
+ ((received && !amount) || received >= amount)
+ )
+ throw redirect(307, `/${user.username}/invoice/${id}/paid`);
- if (request.requester.username === user.username) throw redirect(307, `/send/${id}`);
- }
+ if (request.requester.username === user.username)
+ throw redirect(307, `/send/${id}`);
+ }
- return { request };
+ return { request };
};
diff --git a/src/routes/(app)/[username]/request/[id]/+page.svelte b/src/routes/(app)/[username]/request/[id]/+page.svelte
index bdd14f4a..f9672c6f 100644
--- a/src/routes/(app)/[username]/request/[id]/+page.svelte
+++ b/src/routes/(app)/[username]/request/[id]/+page.svelte
@@ -1,52 +1,57 @@
<script>
- import { scale } from 'svelte/transition';
- import { t } from '$lib/translations';
- import { Avatar, Icon } from '$comp';
- import { f, sat, sats } from '$lib/utils';
- import { request as req, requestRedirect } from '$lib/store';
- import { onMount } from 'svelte';
+ import { scale } from "svelte/transition";
+ import { t } from "$lib/translations";
+ import { Avatar, Icon } from "$comp";
+ import { f, sat, sats } from "$lib/utils";
+ import { request as req, requestRedirect } from "$lib/store";
+ import { onMount } from "svelte";
- export let data;
- let { request, user } = data;
+ export let data;
+ let { request, user } = data;
- let amount, currency, rate, fiat;
- if (request.invoice) {
- ({ amount, rate, currency } = request.invoice);
- fiat = (amount * rate) / sats;
- }
+ let amount, currency, rate, fiat;
+ if (request.invoice) {
+ ({ amount, rate, currency } = request.invoice);
+ fiat = (amount * rate) / sats;
+ }
- onMount(() => req.set());
+ onMount(() => req.set());
</script>
<div class="container px-4 max-w-xl mx-auto space-y-8">
- <div class="text-center mb-8 space-y-5">
- {#if request.recipient_id === user.id}
- <p class="text-4xl break-words">
- <span class="text-xl md:text-2xl">{$t('payments.sentInvoiceTo')}</span>
- </p>
- <div class="flex p-1 gap-2 justify-center">
- <Avatar user={request.requester} size={20} />
- <p class="text-4xl break-words my-auto">{request.requester.username}</p>
- </div>
- <div>
- <h2 class="text-2xl md:text-3xl font-semibold">
- {f(fiat, currency)}
- </h2>
- <h3 class="text-secondary md:text-lg mb-6 mt-1">{sat(amount)}</h3>
- </div>
- {:else}
- <div class="flex w-full max-w-[200px] mx-auto py-20" in:scale={{ start: 0.5 }}>
- <Icon icon="check" style="mx-auto" />
- </div>
- <p class="text-3xl">
- {$t('payments.sentRequestTo')}
- </p>
- {/if}
- </div>
+ <div class="text-center mb-8 space-y-5">
+ {#if request.recipient_id === user.id}
+ <p class="text-4xl break-words">
+ <span class="text-xl md:text-2xl">{$t("payments.sentInvoiceTo")}</span>
+ </p>
+ <div class="flex p-1 gap-2 justify-center">
+ <Avatar user={request.requester} size={20} />
+ <p class="text-4xl break-words my-auto">{request.requester.username}</p>
+ </div>
+ <div>
+ <h2 class="text-2xl md:text-3xl font-semibold">
+ {f(fiat, currency)}
+ </h2>
+ <h3 class="text-secondary md:text-lg mb-6 mt-1">{sat(amount)}</h3>
+ </div>
+ {:else}
+ <div
+ class="flex w-full max-w-[200px] mx-auto py-20"
+ in:scale={{ start: 0.5 }}
+ >
+ <Icon icon="check" style="mx-auto" />
+ </div>
+ <p class="text-3xl">
+ {$t("payments.sentRequestTo")}
+ </p>
+ {/if}
+ </div>
</div>
<a href={$requestRedirect || `/${user.username}`}>
- <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" />
+ <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" />
</a>
-<div class="fixed bottom-10 left-0 text-center w-full">{$t('payments.tapAnywhere')}</div>
+<div class="fixed bottom-10 left-0 text-center w-full">
+ {$t("payments.tapAnywhere")}
+</div>
diff --git a/src/routes/(app)/[username]/reset/+server.js b/src/routes/(app)/[username]/reset/+server.js
index 52fff367..f7dba02c 100644
--- a/src/routes/(app)/[username]/reset/+server.js
+++ b/src/routes/(app)/[username]/reset/+server.js
@@ -1,10 +1,10 @@
-import { json } from '@sveltejs/kit';
-import { auth, post } from '$lib/utils';
+import { json } from "@sveltejs/kit";
+import { auth, post } from "$lib/utils";
export async function POST({ cookies, request, params }) {
- let { username } = params;
- let r = await request.json();
- let { password } = r;
- let result = await post('/reset', { password, username }, auth(cookies));
- return json(result);
+ let { username } = params;
+ let r = await request.json();
+ let { password } = r;
+ let result = await post("/reset", { password, username }, auth(cookies));
+ return json(result);
}
diff --git a/src/routes/(app)/[username]/settings/[...tab]/+page.server.js b/src/routes/(app)/[username]/settings/[...tab]/+page.server.js
index b0c849d0..156b6062 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/+page.server.js
+++ b/src/routes/(app)/[username]/settings/[...tab]/+page.server.js
@@ -1,23 +1,24 @@
-import { fail, redirect } from '@sveltejs/kit';
-import { fd, auth, get, post } from '$lib/utils';
+import { fail, redirect } from "@sveltejs/kit";
+import { fd, auth, get, post } from "$lib/utils";
export function load({ params, url }) {
- if (url.pathname.endsWith('settings')) throw redirect(307, url.pathname + '/account');
- return params;
+ if (url.pathname.endsWith("settings"))
+ throw redirect(307, url.pathname + "/account");
+ return params;
}
export const actions = {
- default: async ({ cookies, request }) => {
- let form = await fd(request);
- if (form.prompt) form.prompt = form.prompt === 'on';
- let user = { ...(await get('/me', auth(cookies))), ...form };
+ default: async ({ cookies, request }) => {
+ let form = await fd(request);
+ if (form.prompt) form.prompt = form.prompt === "on";
+ let user = { ...(await get("/me", auth(cookies))), ...form };
- try {
- ({ user } = await post(`/user`, user, auth(cookies)));
- } catch (e) {
- return fail(400, { message: e.message });
- }
+ try {
+ ({ user } = await post(`/user`, user, auth(cookies)));
+ } catch (e) {
+ return fail(400, { message: e.message });
+ }
- return { user, success: true };
- }
+ return { user, success: true };
+ },
};
diff --git a/src/routes/(app)/[username]/settings/[...tab]/+page.svelte b/src/routes/(app)/[username]/settings/[...tab]/+page.svelte
index 933fa1f6..92772f43 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/+page.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/+page.svelte
@@ -1,206 +1,218 @@
<script>
- import { browser } from '$app/environment';
- import { onMount, tick } from 'svelte';
- import { fly } from 'svelte/transition';
- import { applyAction, deserialize } from '$app/forms';
+ import { browser } from "$app/environment";
+ import { onMount, tick } from "svelte";
+ import { fly } from "svelte/transition";
+ import { applyAction, deserialize } from "$app/forms";
- import { Icon, Spinner, Pin } from '$comp';
- import { t } from '$lib/translations';
- import { fail, post, warning, success } from '$lib/utils';
- import { avatar, banner, password, pin } from '$lib/store';
- import { upload } from '$lib/upload';
- import { page } from '$app/stores';
- import { sign, send, reEncryptEntropy } from '$lib/nostr';
- import { invalidateAll } from '$app/navigation';
+ import { Icon, Spinner, Pin } from "$comp";
+ import { t } from "$lib/translations";
+ import { fail, post, warning, success } from "$lib/utils";
+ import { avatar, banner, password, pin } from "$lib/store";
+ import { upload } from "$lib/upload";
+ import { page } from "$app/stores";
+ import { sign, send, reEncryptEntropy } 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 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';
+ import { PUBLIC_COINOS_URL } from "$env/static/public";
- export let data;
- export let form;
+ export let data;
+ export let form;
- let submit;
- let formElement;
+ let submit;
+ let formElement;
- let { user, token, rates, tab } = data;
- let prev;
+ let { user, token, rates, tab } = data;
+ let prev;
- $: update(data);
- let update = () => {
- ({ user, token, rates, tab } = data);
- prev = { ...user };
- };
+ $: update(data);
+ let update = () => {
+ ({ user, token, rates, tab } = data);
+ prev = { ...user };
+ };
- $: form?.user && ({ user } = form);
+ $: form?.user && ({ user } = form);
- $: form?.success && success('Settings saved!');
+ $: form?.success && success("Settings saved!");
- $: form?.message && fail(form.message);
+ $: form?.message && fail(form.message);
- $: if (form?.message?.startsWith('Pin')) {
- fail('Wrong pin, try again');
- $pin = '';
- }
+ $: if (form?.message?.startsWith("Pin")) {
+ fail("Wrong pin, try again");
+ $pin = "";
+ }
- 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 }
- ];
+ 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 },
+ ];
- $: ({ comp } = tabs.find((t) => t.name === tab));
+ $: ({ comp } = tabs.find((t) => t.name === tab));
- let { address, id, username } = user;
- let loading;
+ let { address, id, username } = user;
+ let loading;
- async function handleSubmit() {
- try {
- loading = true;
- let data = new FormData(formElement);
+ async function handleSubmit() {
+ try {
+ loading = true;
+ let data = new FormData(formElement);
- if (data.get('password')) {
- try {
- data.set('cipher', await reEncryptEntropy(user, data.get('password')));
- } catch (e) {
- console.log('Failed to encrypt keys with new password');
- throw e;
- }
- }
+ if (data.get("password")) {
+ try {
+ data.set(
+ "cipher",
+ await reEncryptEntropy(user, data.get("password"))
+ );
+ } catch (e) {
+ console.log("Failed to encrypt keys with new password");
+ throw e;
+ }
+ }
- if ($avatar) {
- try {
- let { hash } = JSON.parse(
- await upload($avatar.file, $avatar.type, $avatar.progress, token)
- );
+ if ($avatar) {
+ try {
+ let { hash } = JSON.parse(
+ await upload($avatar.file, $avatar.type, $avatar.progress, token)
+ );
- data.set('profile', hash);
+ data.set("profile", hash);
- await fetch(`/api/public/${hash}.webp`, { cache: 'reload', mode: 'no-cors' });
- } catch (e) {
- console.log('problem uploading avatar', e);
- }
- }
+ await fetch(`/api/public/${hash}.webp`, {
+ cache: "reload",
+ mode: "no-cors",
+ });
+ } catch (e) {
+ console.log("problem uploading avatar", e);
+ }
+ }
- if ($banner) {
- try {
- let { hash } = JSON.parse(
- await upload($banner.file, $banner.type, $banner.progress, token)
- );
+ if ($banner) {
+ try {
+ let { hash } = JSON.parse(
+ await upload($banner.file, $banner.type, $banner.progress, token)
+ );
- data.set('banner', hash);
+ data.set("banner", hash);
- await fetch(`/api/public/${hash}.webp`, { cache: 'reload', mode: 'no-cors' });
- } catch (e) {
- console.log('problem uploading banner', e);
- }
- }
+ await fetch(`/api/public/${hash}.webp`, {
+ cache: "reload",
+ mode: "no-cors",
+ });
+ } catch (e) {
+ console.log("problem uploading banner", e);
+ }
+ }
- if (['username', 'address', 'profile'].some((a) => user[a] !== prev[a])) {
- let event = {
- pubkey: user.pubkey,
- created_at: Math.floor(Date.now() / 1000),
- kind: 0,
- content: JSON.stringify({
- name: user.username,
- about: user.address,
- picture: `${PUBLIC_COINOS_URL}/public/${user.profile}.webp`
- }),
- tags: []
- };
+ if (["username", "address", "profile"].some((a) => user[a] !== prev[a])) {
+ let event = {
+ pubkey: user.pubkey,
+ created_at: Math.floor(Date.now() / 1000),
+ kind: 0,
+ content: JSON.stringify({
+ name: user.username,
+ about: user.address,
+ picture: `${PUBLIC_COINOS_URL}/public/${user.profile}.webp`,
+ }),
+ tags: [],
+ };
- try {
- await sign({ event, user });
- await send(event);
- } catch (e) {
- warning('nostr profile could not be updated');
- }
- }
+ try {
+ await sign({ event, user });
+ await send(event);
+ } catch (e) {
+ warning("nostr profile could not be updated");
+ }
+ }
- if (user.email !== prev.email) {
- await post('/api/verify', { test: true });
- }
+ if (user.email !== prev.email) {
+ await post("/api/verify", { test: true });
+ }
- const response = await fetch(formElement.action, {
- method: 'POST',
- body: data
- });
+ const response = await fetch(formElement.action, {
+ method: "POST",
+ body: data,
+ });
- const result = deserialize(await response.text());
+ const result = deserialize(await response.text());
- if (result.type === 'success') {
- await invalidateAll();
- if (data.get('password')) $password = data.get('password');
- }
+ if (result.type === "success") {
+ await invalidateAll();
+ if (data.get("password")) $password = data.get("password");
+ }
- applyAction(result);
- } catch (e) {
- console.log(e);
- fail('Something went wrong');
- }
+ applyAction(result);
+ } catch (e) {
+ console.log(e);
+ fail("Something went wrong");
+ }
- loading = false;
- }
+ loading = false;
+ }
</script>
{#if user.haspin && $pin?.length !== 6}
- <Pin />
+ <Pin />
{/if}
<form
- method="POST"
- class="mb-[154px]"
- on:submit|preventDefault={handleSubmit}
- bind:this={formElement}
+ method="POST"
+ class="mb-[154px]"
+ on:submit|preventDefault={handleSubmit}
+ bind:this={formElement}
>
- <input type="hidden" name="pin" value={$pin} />
+ <input type="hidden" name="pin" value={$pin} />
- <div class="mt-24 mb-20 px-3 md:px-0 w-full md:w-[400px] mx-auto space-y-8">
- <div class="header">
- <h1 class="text-center text-3xl md:text-4xl font-semibold mb-10">
- {$t('user.settings.header')}
- </h1>
+ <div class="mt-24 mb-20 px-3 md:px-0 w-full md:w-[400px] mx-auto space-y-8">
+ <div class="header">
+ <h1 class="text-center text-3xl md:text-4xl font-semibold mb-10">
+ {$t("user.settings.header")}
+ </h1>
- <div class="font-bold flex justify-around items-center border-b pb-3 text-secondary">
- {#each tabs.filter((t) => t.name !== 'shopify') as { name, key }}
- <a href={`/${user.username}/settings/${name}`}>
- <button
- class="hover:opacity-80"
- class:text-black={tab === name || (tab === 'shopify' && name === 'pos')}
- >{$t(`user.settings.${key}`)}</button
- >
- </a>
- {/each}
- </div>
- </div>
+ <div
+ class="font-bold flex justify-around items-center border-b pb-3 text-secondary"
+ >
+ {#each tabs.filter((t) => t.name !== "shopify") as { name, key }}
+ <a href={`/${user.username}/settings/${name}`}>
+ <button
+ class="hover:opacity-80"
+ class:text-black={tab === name ||
+ (tab === "shopify" && name === "pos")}
+ >{$t(`user.settings.${key}`)}</button
+ >
+ </a>
+ {/each}
+ </div>
+ </div>
- <svelte:component this={comp} {user} {rates} {submit} />
- </div>
+ <svelte:component this={comp} {user} {rates} {submit} />
+ </div>
- <div
- class="z-10 fixed bottom-0 bg-white shadow border-t w-full flex justify-center items-center py-3"
- >
- <button
- bind:this={submit}
- type="submit"
- class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80"
- class:bg-black={loading}
- >
- {#if loading}
- <Spinner />
- {:else}
- <div class="my-auto">{$t('user.settings.saveSettings')}</div>
- {/if}
- </button>
- </div>
+ <div
+ class="z-10 fixed bottom-0 bg-white shadow border-t w-full flex justify-center items-center py-3"
+ >
+ <button
+ bind:this={submit}
+ type="submit"
+ class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80"
+ class:bg-black={loading}
+ >
+ {#if loading}
+ <Spinner />
+ {:else}
+ <div class="my-auto">{$t("user.settings.saveSettings")}</div>
+ {/if}
+ </button>
+ </div>
</form>
<style>
- .header {
- view-transition-name: tabs;
- }
+ .header {
+ view-transition-name: tabs;
+ }
</style>
diff --git a/src/routes/(app)/[username]/settings/[...tab]/_account.svelte b/src/routes/(app)/[username]/settings/[...tab]/_account.svelte
index 2a06b31d..64ab669f 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/_account.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/_account.svelte
@@ -1,183 +1,193 @@
<script>
- import { tick } from 'svelte';
- import { colorTheme, avatar, banner } from '$lib/store';
- import { Icon } from '$comp';
- import { t } from '$lib/translations';
- import { page } from '$app/stores';
+ import { tick } from "svelte";
+ import { colorTheme, avatar, banner } from "$lib/store";
+ import { Icon } from "$comp";
+ import { t } from "$lib/translations";
+ import { page } from "$app/stores";
- export let user, rates, submit;
+ export let user, rates, submit;
- let { id } = user;
+ let { id } = user;
- let selectedTheme = 1;
- let avatarFile, avatarInput, bannerFile, bannerInput;
+ 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 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 handleThemeClick = (colors) => {
+ selectedTheme = colors.theme;
+ $colorTheme = colors.color1 + " " + colors.color2;
+ };
- let selectAvatar = () => avatarInput.click();
- let selectBanner = () => bannerInput.click();
+ let selectAvatar = () => avatarInput.click();
+ let selectBanner = () => bannerInput.click();
- let percent;
- let progress = async (event) => {
- percent = Math.round((event.loaded / event.total) * 100);
- };
+ let percent;
+ let progress = async (event) => {
+ percent = Math.round((event.loaded / event.total) * 100);
+ };
- let tooLarge = {};
+ let tooLarge = {};
- let handleFile = async ({ target }, type) => {
- tooLarge[type] = false;
- let file = target.files[0];
- if (!file) return;
+ let handleFile = async ({ target }, type) => {
+ tooLarge[type] = false;
+ let file = target.files[0];
+ if (!file) return;
- if (file.size > 10000000) return (tooLarge[type] = true);
+ if (file.size > 10000000) return (tooLarge[type] = true);
- if (type === 'profile') {
- $avatar = { id, file, type, progress };
- } else if (type === 'banner') {
- $banner = { id, file, type, progress };
- }
+ if (type === "profile") {
+ $avatar = { id, file, type, progress };
+ } else if (type === "banner") {
+ $banner = { id, file, type, progress };
+ }
- var reader = new FileReader();
- reader.onload = async (e) => {
- if (type === 'profile') {
- $avatar.src = e.target.result;
- } else if (type === 'banner') {
- $banner.src = e.target.result;
- }
- };
+ var reader = new FileReader();
+ reader.onload = async (e) => {
+ if (type === "profile") {
+ $avatar.src = e.target.result;
+ } else if (type === "banner") {
+ $banner.src = e.target.result;
+ }
+ };
- reader.readAsDataURL(file);
- };
+ reader.readAsDataURL(file);
+ };
- if (!user.display) user.display = user.username;
+ if (!user.display) user.display = user.username;
- $: url = `${$page.url.host}/${user.username}`;
- $: full = `${$page.url.protocol}//${url}`;
- $: addr = `${user.username}@${$page.url.host}`;
+ $: url = `${$page.url.host}/${user.username}`;
+ $: full = `${$page.url.protocol}//${url}`;
+ $: addr = `${user.username}@${$page.url.host}`;
</script>
<div>
- <label for="username" class="font-bold mb-1 block">{$t('user.settings.username')}</label>
- <div class="flex mb-2">
- <input
- type="text"
- name="username"
- bind:value={user.username}
- class="w-auto min-w-0 pr-1 grow"
- />
- </div>
+ <label for="username" class="font-bold mb-1 block"
+ >{$t("user.settings.username")}</label
+ >
+ <div class="flex mb-2">
+ <input
+ type="text"
+ name="username"
+ bind:value={user.username}
+ class="w-auto min-w-0 pr-1 grow"
+ />
+ </div>
</div>
<div>
- <label for="display" class="font-bold mb-1 block">{$t('user.settings.displayName')}</label>
- <input type="text" name="display" bind:value={user.display} />
+ <label for="display" class="font-bold mb-1 block"
+ >{$t("user.settings.displayName")}</label
+ >
+ <input type="text" name="display" bind:value={user.display} />
</div>
<div>
- <span class="font-bold">{$t('user.settings.profileImage')}</span>
+ <span class="font-bold">{$t("user.settings.profileImage")}</span>
- <div class="flex">
- {#if $avatar || user.profile}
- <div
- class="relative rounded-full overflow-hidden text-center w-20 h-20 my-auto hover:opacity-80 cursor-pointer"
- on:click={selectAvatar}
- on:keydown={selectAvatar}
- >
- <img
- src={$avatar?.src || `/api/public/${user.profile}.webp`}
- class="absolute w-full h-full object-cover object-center visible overflow-hidden"
- alt={user.username}
- />
- </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"
- on:click={selectAvatar}
- on:keydown={selectAvatar}
- >
- <Icon icon="logo-symbol-white" style="mx-auto" />
- </div>
- {/if}
- <div class="ml-2 p-2">
- <!-- found missing translation -->
- <button
- type="button"
- class="border rounded-2xl font-bold w-24 text-center px-0 py-2 hover:opacity-80"
- on:click={selectAvatar}
- on:keydown={selectAvatar}>{$t('user.settings.select')}</button
- >
- <input
- type="file"
- class="hidden"
- bind:this={avatarInput}
- on:change={(e) => handleFile(e, 'profile')}
- />
- </div>
- </div>
+ <div class="flex">
+ {#if $avatar || user.profile}
+ <div
+ class="relative rounded-full overflow-hidden text-center w-20 h-20 my-auto hover:opacity-80 cursor-pointer"
+ on:click={selectAvatar}
+ on:keydown={selectAvatar}
+ >
+ <img
+ src={$avatar?.src || `/api/public/${user.profile}.webp`}
+ class="absolute w-full h-full object-cover object-center visible overflow-hidden"
+ alt={user.username}
+ />
+ </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"
+ on:click={selectAvatar}
+ on:keydown={selectAvatar}
+ >
+ <Icon icon="logo-symbol-white" style="mx-auto" />
+ </div>
+ {/if}
+ <div class="ml-2 p-2">
+ <!-- found missing translation -->
+ <button
+ type="button"
+ class="border rounded-2xl font-bold w-24 text-center px-0 py-2 hover:opacity-80"
+ on:click={selectAvatar}
+ on:keydown={selectAvatar}>{$t("user.settings.select")}</button
+ >
+ <input
+ type="file"
+ class="hidden"
+ bind:this={avatarInput}
+ on:change={(e) => handleFile(e, "profile")}
+ />
+ </div>
+ </div>
- {#if tooLarge['avatar']}
- <div class="text-red-600">Max file size 10MB</div>
- {/if}
+ {#if tooLarge["avatar"]}
+ <div class="text-red-600">Max file size 10MB</div>
+ {/if}
</div>
<div>
- <div class="flex justify-between items-center">
- <span class="font-bold">{$t('user.settings.bannerImage')}</span>
- </div>
+ <div class="flex justify-between items-center">
+ <span class="font-bold">{$t("user.settings.bannerImage")}</span>
+ </div>
- {#if $banner || user.banner}
- <img
- src={$banner ? $banner.src : `/api/public/${user.banner}.webp`}
- class="w-full object-cover object-center visible overflow-hidden h-48 mb-4 hover:opacity-80"
- on:click={selectBanner}
- on:keydown={selectBanner}
- alt="Banner"
- />
- {:else}
- <div
- class="bg-gradient-to-r {$colorTheme} w-full h-48 mb-4 cursor-pointer hover:opacity-80"
- on:click={selectBanner}
- on:keydown={selectBanner}
- alt="Banner"
- />
- {/if}
+ {#if $banner || user.banner}
+ <img
+ src={$banner ? $banner.src : `/api/public/${user.banner}.webp`}
+ class="w-full object-cover object-center visible overflow-hidden h-48 mb-4 hover:opacity-80"
+ on:click={selectBanner}
+ on:keydown={selectBanner}
+ alt="Banner"
+ />
+ {:else}
+ <div
+ class="bg-gradient-to-r {$colorTheme} w-full h-48 mb-4 cursor-pointer hover:opacity-80"
+ on:click={selectBanner}
+ on:keydown={selectBanner}
+ alt="Banner"
+ />
+ {/if}
- <button
- type="button"
- class="border rounded-2xl font-bold w-24 text-center px-0 py-2 hover:opacity-80"
- on:click={selectBanner}
- on:keydown={selectBanner}>{$t('user.settings.select')}</button
- >
- <input
- type="file"
- class="hidden"
- bind:this={bannerInput}
- on:change={(e) => handleFile(e, 'banner')}
- />
+ <button
+ type="button"
+ class="border rounded-2xl font-bold w-24 text-center px-0 py-2 hover:opacity-80"
+ on:click={selectBanner}
+ on:keydown={selectBanner}>{$t("user.settings.select")}</button
+ >
+ <input
+ type="file"
+ class="hidden"
+ bind:this={bannerInput}
+ on:change={(e) => handleFile(e, "banner")}
+ />
- {#if tooLarge['banner']}
- <div class="text-red-600">Max file size 10MB</div>
- {/if}
+ {#if tooLarge["banner"]}
+ <div class="text-red-600">Max file size 10MB</div>
+ {/if}
</div>
<div>
- <label for="address" class="font-bold mb-1 block">{$t('user.settings.about')}</label>
- <textarea
- type="text"
- name="address"
- bind:value={user.address}
- placeholder={$t('user.settings.aboutPlaceholder')}
- />
+ <label for="address" class="font-bold mb-1 block"
+ >{$t("user.settings.about")}</label
+ >
+ <textarea
+ type="text"
+ name="address"
+ bind:value={user.address}
+ placeholder={$t("user.settings.aboutPlaceholder")}
+ />
</div>
diff --git a/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte b/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte
index 9100401a..94a35493 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte
@@ -1,61 +1,71 @@
<script>
- import { Icon, LocaleSelector, Toggle } from '$comp';
- import { t } from '$lib/translations';
- import { success, fail } from '$lib/utils';
- import { page } from '$app/stores';
+ import { Icon, LocaleSelector, Toggle } from "$comp";
+ import { t } from "$lib/translations";
+ import { success, fail } from "$lib/utils";
+ import { page } from "$app/stores";
- export let user, rates, submit;
+ export let user, rates, submit;
- let fiats = Object.keys(rates);
+ let fiats = Object.keys(rates);
</script>
<div>
- <label for="language" class="font-bold block mb-1">{$t('user.settings.locale')}</label>
- <LocaleSelector style="select-styles block py-3 w-full" />
+ <label for="language" class="font-bold block mb-1"
+ >{$t("user.settings.locale")}</label
+ >
+ <LocaleSelector style="select-styles block py-3 w-full" />
</div>
<div>
- <label for="currency" class="font-bold block mb-1">{$t('user.settings.localCurrency')}</label>
- <select name="currency" class="select-styles block py-3 w-full" value={user.currency}>
- {#each fiats as fiat}
- <option value={fiat}>{fiat}</option>
- {/each}
- </select>
+ <label for="currency" class="font-bold block mb-1"
+ >{$t("user.settings.localCurrency")}</label
+ >
+ <select
+ name="currency"
+ class="select-styles block py-3 w-full"
+ value={user.currency}
+ >
+ {#each fiats as fiat}
+ <option value={fiat}>{fiat}</option>
+ {/each}
+ </select>
</div>
<div class="space-y-1">
- <label for="email" class="font-bold block">{$t('user.settings.email')}</label>
+ <label for="email" class="font-bold block">{$t("user.settings.email")}</label>
<p class="text-secondary">
- {$t('user.settings.emailDescription')}
+ {$t("user.settings.emailDescription")}
</p>
- <input type="text" name="email" bind:value={user.email} />
+ <input type="text" name="email" bind:value={user.email} />
</div>
<div>
- <div class="flex justify-between items-center">
- <span class="font-bold">{$t('user.settings.tipPrompt')}</span>
- <Toggle id="prompt" bind:value={user.prompt} />
- </div>
- <p class="text-secondary mt-1 w-9/12">
- {$t('user.settings.tipPromptDescription')}
- </p>
+ <div class="flex justify-between items-center">
+ <span class="font-bold">{$t("user.settings.tipPrompt")}</span>
+ <Toggle id="prompt" bind:value={user.prompt} />
+ </div>
+ <p class="text-secondary mt-1 w-9/12">
+ {$t("user.settings.tipPromptDescription")}
+ </p>
</div>
<div>
- <div class="flex justify-between items-center">
- <span class="font-bold">{$t('user.settings.publishNip5')}</span>
- <Toggle id="nip5" bind:value={user.nip5} />
- </div>
- <p class="text-secondary mt-1 w-9/12">
- {$t('user.settings.nip5a')} <span class="font-bold">{user.username}@{$page.url.host}</span> {$t('user.settings.nip5b')}
- </p>
+ <div class="flex justify-between items-center">
+ <span class="font-bold">{$t("user.settings.publishNip5")}</span>
+ <Toggle id="nip5" bind:value={user.nip5} />
+ </div>
+ <p class="text-secondary mt-1 w-9/12">
+ {$t("user.settings.nip5a")}
+ <span class="font-bold">{user.username}@{$page.url.host}</span>
+ {$t("user.settings.nip5b")}
+ </p>
</div>
<a href="./shopify">
- <button
- class="flex rounded-full border py-2 px-4 font-bold hover:opacity-80 w-full gap-2 my-5 justify-center"
- >
- <img src="/icons/shopify.svg" class="w-8" />
- <div class="my-auto">Shopify Integration</div>
- </button>
+ <button
+ class="flex rounded-full border py-2 px-4 font-bold hover:opacity-80 w-full gap-2 my-5 justify-center"
+ >
+ <img src="/icons/shopify.svg" class="w-8" />
+ <div class="my-auto">Shopify Integration</div>
+ </button>
</a>
diff --git a/src/routes/(app)/[username]/settings/[...tab]/_security.svelte b/src/routes/(app)/[username]/settings/[...tab]/_security.svelte
index 3cd0480e..f8c14e7b 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/_security.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/_security.svelte
@@ -1,290 +1,317 @@
<script>
- import { getMnemonic, getNsec } from '$lib/nostr';
- import { tick } from 'svelte';
- import { t } from '$lib/translations';
- import { Pin, Icon, Qr } from '$comp';
- import { copy, post, success, fail } from '$lib/utils';
- import { pin as current } from '$lib/store';
- import { invalidate } from '$app/navigation';
- import { page } from '$app/stores';
+ import { getMnemonic, getNsec } from "$lib/nostr";
+ import { tick } from "svelte";
+ import { t } from "$lib/translations";
+ import { Pin, Icon, Qr } from "$comp";
+ import { copy, post, success, fail } from "$lib/utils";
+ import { pin as current } from "$lib/store";
+ import { invalidate } from "$app/navigation";
+ import { page } from "$app/stores";
- export let user, rates, submit;
+ export let user, rates, submit;
- let confirming2fa,
- disabling2fa,
- locked,
- mnemonic,
- nsec,
- old,
- otp,
- password,
- pin,
- revealPassword,
- revealSeed,
- revealNsec,
- token = '',
- setting2fa,
- settingPin,
- verify;
+ let confirming2fa,
+ disabling2fa,
+ locked,
+ mnemonic,
+ nsec,
+ old,
+ otp,
+ password,
+ pin,
+ revealPassword,
+ revealSeed,
+ revealNsec,
+ token = "",
+ setting2fa,
+ settingPin,
+ verify;
- let toggleNsec = async () => {
- try {
- nsec = await getNsec(user);
- revealNsec = !revealNsec;
- } catch (e) {
- console.log(e);
- }
- };
+ let toggleNsec = async () => {
+ try {
+ nsec = await getNsec(user);
+ revealNsec = !revealNsec;
+ } catch (e) {
+ console.log(e);
+ }
+ };
- let togglePassword = () => (revealPassword = !revealPassword);
- let toggleSeed = async () => {
- try {
- mnemonic = await getMnemonic(user);
- revealSeed = !revealSeed;
- } catch (e) {
- console.log(e);
- }
- };
+ let togglePassword = () => (revealPassword = !revealPassword);
+ let toggleSeed = async () => {
+ try {
+ mnemonic = await getMnemonic(user);
+ revealSeed = !revealSeed;
+ } catch (e) {
+ console.log(e);
+ }
+ };
- $: verifying = pin?.length > 5;
- $: verify && checkPin(pin);
- let checkPin = async () => {
- old = $current;
+ $: verifying = pin?.length > 5;
+ $: verify && checkPin(pin);
+ let checkPin = async () => {
+ old = $current;
- try {
- if (pin.length > 5 && pin === verify) {
- user.haspin = true;
- $current = pin;
- pin = '';
- verify = '';
- submit.click();
- settingPin = false;
- verifying = false;
- } else {
- fail('Pin mismatch, try again');
- pin = '';
- verify = '';
- verifying = false;
- settingPin = true;
- }
- } catch (e) {
- console.log(e);
- fail('Problem setting PIN');
- }
- };
+ try {
+ if (pin.length > 5 && pin === verify) {
+ user.haspin = true;
+ $current = pin;
+ pin = "";
+ verify = "";
+ submit.click();
+ settingPin = false;
+ verifying = false;
+ } else {
+ fail("Pin mismatch, try again");
+ pin = "";
+ verify = "";
+ verifying = false;
+ settingPin = true;
+ }
+ } catch (e) {
+ console.log(e);
+ fail("Problem setting PIN");
+ }
+ };
- let reset = () => {
- token = '';
- return true;
- };
+ let reset = () => {
+ token = "";
+ return true;
+ };
- let disablingPin = false;
+ let disablingPin = false;
- let togglePin = async () => {
- if (user.haspin) {
- try {
- disablingPin = true;
- await tick();
- submit.click();
- } catch (e) {
- console.log(e);
- fail('Failed to disable pin');
- }
- } else {
- settingPin = true;
+ let togglePin = async () => {
+ if (user.haspin) {
+ try {
+ disablingPin = true;
+ await tick();
+ submit.click();
+ } catch (e) {
+ console.log(e);
+ fail("Failed to disable pin");
+ }
+ } else {
+ settingPin = true;
disablingPin = false;
- }
- };
+ }
+ };
- let startEnabling2fa = async () => {
- reset();
- try {
- if (!otp) otp = await post('/otpsecret', { pin: $current });
- } catch (e) {
- console.log(e);
- }
+ let startEnabling2fa = async () => {
+ reset();
+ try {
+ if (!otp) otp = await post("/otpsecret", { pin: $current });
+ } catch (e) {
+ console.log(e);
+ }
- setting2fa = true;
- };
+ setting2fa = true;
+ };
- let startDisabling2fa = () => (disabling2fa = true);
- let startConfirming2fa = () => (confirming2fa = true);
- let cancel = () => {
- pin = null;
- token = null;
- verifying = false;
- settingPin = false;
- setting2fa = false;
- confirming2fa = false;
- disabling2fa = false;
- };
+ let startDisabling2fa = () => (disabling2fa = true);
+ let startConfirming2fa = () => (confirming2fa = true);
+ let cancel = () => {
+ pin = null;
+ token = null;
+ verifying = false;
+ settingPin = false;
+ setting2fa = false;
+ confirming2fa = false;
+ disabling2fa = false;
+ };
- $: enable2fa(token);
- let enable2fa = async (twoFa) => {
- try {
- if (setting2fa && token.length === 6) {
- await post('/enable2fa', { token });
- success('2FA enabled');
- user.twofa = 1;
- cancel();
- }
- } catch (e) {
- fail('Failed to enable 2FA, try again');
- }
- };
+ $: enable2fa(token);
+ let enable2fa = async (twoFa) => {
+ try {
+ if (setting2fa && token.length === 6) {
+ await post("/enable2fa", { token });
+ success("2FA enabled");
+ user.twofa = 1;
+ cancel();
+ }
+ } catch (e) {
+ fail("Failed to enable 2FA, try again");
+ }
+ };
- $: disable2fa(token);
- let disable2fa = async () => {
- try {
- if (disabling2fa && token.length === 6) {
- await post('/disable2fa', { token });
- success('2FA disabled');
- delete user.twofa;
- disabling2fa = false;
- }
- } catch (e) {
- console.log(e);
- fail('Failed to disable 2FA, try again');
- }
- };
+ $: disable2fa(token);
+ let disable2fa = async () => {
+ try {
+ if (disabling2fa && token.length === 6) {
+ await post("/disable2fa", { token });
+ success("2FA disabled");
+ delete user.twofa;
+ disabling2fa = false;
+ }
+ } catch (e) {
+ console.log(e);
+ fail("Failed to disable 2FA, try again");
+ }
+ };
</script>
-<input type="hidden" name="newpin" value={disablingPin ? 'delete' : pin} />
+<input type="hidden" name="newpin" value={disablingPin ? "delete" : pin} />
<input type="hidden" name="confirm" bind:value={password} />
<div class="relative mb-5">
- <label for="password" class="block font-bold block mb-1">{$t('user.settings.newPassword')}</label>
- {#if revealPassword}
- <input name="password" type="text" bind:value={password} />
- {:else}
- <input name="password" type="password" bind:value={password} />
- {/if}
- <button
- type="button"
- on:click={togglePassword}
- on:keydown={togglePassword}
- class="absolute right-5 top-11"
- >
- <Icon icon={revealPassword ? 'eye' : 'eye-off'} />
- </button>
+ <label for="password" class="block font-bold block mb-1"
+ >{$t("user.settings.newPassword")}</label
+ >
+ {#if revealPassword}
+ <input name="password" type="text" bind:value={password} />
+ {:else}
+ <input name="password" type="password" bind:value={password} />
+ {/if}
+ <button
+ type="button"
+ on:click={togglePassword}
+ on:keydown={togglePassword}
+ class="absolute right-5 top-11"
+ >
+ <Icon icon={revealPassword ? "eye" : "eye-off"} />
+ </button>
</div>
<div>
- <span class="font-bold mb-1"
- >{verifying ? $t('user.settings.verifyPIN') : $t('user.settings.securityPIN')}</span
- >
- <p class="text-secondary mb-1">
- {$t('user.settings.securityPINDescription')}
- </p>
- {#if verifying}
- <Pin bind:value={verify} {cancel} notify={false} />
- {:else if settingPin}
- <Pin bind:value={pin} title={$t('user.settings.setPIN')} {cancel} notify={false} />
- {:else}
- <button type="button" class="primary" on:click={togglePin}
- ><Icon icon="lock" style="mr-1" />
- {user.haspin ? $t('user.settings.disablePIN') : $t('user.settings.enablePIN')}</button
- >
- {/if}
+ <span class="font-bold mb-1"
+ >{verifying
+ ? $t("user.settings.verifyPIN")
+ : $t("user.settings.securityPIN")}</span
+ >
+ <p class="text-secondary mb-1">
+ {$t("user.settings.securityPINDescription")}
+ </p>
+ {#if verifying}
+ <Pin bind:value={verify} {cancel} notify={false} />
+ {:else if settingPin}
+ <Pin
+ bind:value={pin}
+ title={$t("user.settings.setPIN")}
+ {cancel}
+ notify={false}
+ />
+ {:else}
+ <button type="button" class="primary" on:click={togglePin}
+ ><Icon icon="lock" style="mr-1" />
+ {user.haspin
+ ? $t("user.settings.disablePIN")
+ : $t("user.settings.enablePIN")}</button
+ >
+ {/if}
</div>
{#if user.haspin}
- <div>
- <label for="locktime" class="font-bold mb-1">{$t('user.settings.autoLock')}</label>
- <p class="text-secondary mb-1">
- {$t('user.settings.autoLockDescription')}
- </p>
- <select name="locktime" class="select-styles block py-3 w-full" value={parseInt(user.locktime)}>
- <option value={5 * 60}>5 {$t('user.settings.minutes')}</option>
- <option value={10 * 60}>10 {$t('user.settings.minutes')}</option>
- <option value={30 * 60}>30 {$t('user.settings.minutes')}</option>
- <option value={60 * 60}>1 {$t('user.settings.hour')}</option>
- <option value={8 * 60 * 60}>8 {$t('user.settings.hours')}</option>
- </select>
- </div>
+ <div>
+ <label for="locktime" class="font-bold mb-1"
+ >{$t("user.settings.autoLock")}</label
+ >
+ <p class="text-secondary mb-1">
+ {$t("user.settings.autoLockDescription")}
+ </p>
+ <select
+ name="locktime"
+ class="select-styles block py-3 w-full"
+ value={parseInt(user.locktime)}
+ >
+ <option value={5 * 60}>5 {$t("user.settings.minutes")}</option>
+ <option value={10 * 60}>10 {$t("user.settings.minutes")}</option>
+ <option value={30 * 60}>30 {$t("user.settings.minutes")}</option>
+ <option value={60 * 60}>1 {$t("user.settings.hour")}</option>
+ <option value={8 * 60 * 60}>8 {$t("user.settings.hours")}</option>
+ </select>
+ </div>
{/if}
<div>
- <span class="font-bold mb-1">{$t('user.settings.twofa')}</span>
- <p class="text-secondary mb-4">
- {$t('user.settings.twofaDescription')}
- </p>
+ <span class="font-bold mb-1">{$t("user.settings.twofa")}</span>
+ <p class="text-secondary mb-4">
+ {$t("user.settings.twofaDescription")}
+ </p>
- {#if setting2fa}
- <a href={otp.uri}>
- <Qr src={otp.qr} />
- </a>
+ {#if setting2fa}
+ <a href={otp.uri}>
+ <Qr src={otp.qr} />
+ </a>
- <div class="text-center my-4">
- {$t('user.settings.accountId')}<br />
- <b>{otp.secret}</b>
- </div>
+ <div class="text-center my-4">
+ {$t("user.settings.accountId")}<br />
+ <b>{otp.secret}</b>
+ </div>
- <button type="button" class="primary" on:click={startConfirming2fa}>
- <Icon icon="mobile" style="mr-1" />
- Confirm
- </button>
- {:else if user.twofa}
- <button type="button" class="primary" on:click={startDisabling2fa}>
- <Icon icon="mobile" style="mr-1" />
- {$t('user.settings.twofaDisable')}
- </button>
- {:else}
- <button type="button" class="primary" on:click={startEnabling2fa}>
- <Icon icon="mobile" style="mr-1" />
- {$t('user.settings.twofaSetup')}
- </button>
- {/if}
+ <button type="button" class="primary" on:click={startConfirming2fa}>
+ <Icon icon="mobile" style="mr-1" />
+ Confirm
+ </button>
+ {:else if user.twofa}
+ <button type="button" class="primary" on:click={startDisabling2fa}>
+ <Icon icon="mobile" style="mr-1" />
+ {$t("user.settings.twofaDisable")}
+ </button>
+ {:else}
+ <button type="button" class="primary" on:click={startEnabling2fa}>
+ <Icon icon="mobile" style="mr-1" />
+ {$t("user.settings.twofaSetup")}
+ </button>
+ {/if}
- {#if confirming2fa || disabling2fa}
- <Pin bind:value={token} title="Enter 2FA Code" {cancel} persist={false} notify={false} />
- {/if}
+ {#if confirming2fa || disabling2fa}
+ <Pin
+ bind:value={token}
+ title="Enter 2FA Code"
+ {cancel}
+ persist={false}
+ notify={false}
+ />
+ {/if}
</div>
<div>
- <label for="seedphrase" class="font-bold">{$t('user.settings.seedPhrase')}</label>
+ <label for="seedphrase" class="font-bold"
+ >{$t("user.settings.seedPhrase")}</label
+ >
- <p class="text-secondary mb-1">
- {$t('user.settings.seedDescription')}
- </p>
+ <p class="text-secondary mb-1">
+ {$t("user.settings.seedDescription")}
+ </p>
- <button type="button" class="primary" on:click={toggleSeed}>
- <Icon icon="warning" style="mr-1 w-6 my-auto" />
- {revealSeed ? $t('user.settings.hideSeed') : $t('user.settings.revealSeed')}
- </button>
+ <button type="button" class="primary" on:click={toggleSeed}>
+ <Icon icon="warning" style="mr-1 w-6 my-auto" />
+ {revealSeed ? $t("user.settings.hideSeed") : $t("user.settings.revealSeed")}
+ </button>
- {#if revealSeed}
- <div class="text-lg">
- {mnemonic}
- </div>
+ {#if revealSeed}
+ <div class="text-lg">
+ {mnemonic}
+ </div>
- <button type="button" class="primary" on:click={() => copy(mnemonic)}>
- <Icon icon="copy" style="mr-1 w-6 my-auto" />
- Copy
- </button>
- {/if}
+ <button type="button" class="primary" on:click={() => copy(mnemonic)}>
+ <Icon icon="copy" style="mr-1 w-6 my-auto" />
+ Copy
+ </button>
+ {/if}
</div>
<div>
- <label for="seedphrase" class="font-bold">{$t('user.settings.nostrKeys')}</label>
+ <label for="seedphrase" class="font-bold"
+ >{$t("user.settings.nostrKeys")}</label
+ >
- <p class="text-secondary mb-1">
- {$t('user.settings.nostrDescription')}
- </p>
+ <p class="text-secondary mb-1">
+ {$t("user.settings.nostrDescription")}
+ </p>
- <button type="button" class="primary" on:click={toggleNsec}>
- <Icon icon="warning" style="mr-1 w-6 my-auto" />
- {revealNsec ? $t('user.settings.hideNsec') : $t('user.settings.revealNsec')}
- </button>
+ <button type="button" class="primary" on:click={toggleNsec}>
+ <Icon icon="warning" style="mr-1 w-6 my-auto" />
+ {revealNsec ? $t("user.settings.hideNsec") : $t("user.settings.revealNsec")}
+ </button>
- {#if revealNsec}
- <div class="text-lg break-all">
- {nsec}
- </div>
+ {#if revealNsec}
+ <div class="text-lg break-all">
+ {nsec}
+ </div>
- <button type="button" class="primary" on:click={() => copy(nsec)}>
- <Icon icon="copy" style="mr-1 w-6 my-auto" />
- Copy
- </button>
- {/if}
+ <button type="button" class="primary" on:click={() => copy(nsec)}>
+ <Icon icon="copy" style="mr-1 w-6 my-auto" />
+ Copy
+ </button>
+ {/if}
</div>
diff --git a/src/routes/(app)/[username]/settings/[...tab]/_shopify.svelte b/src/routes/(app)/[username]/settings/[...tab]/_shopify.svelte
index aeecc4d7..3e2a8dae 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/_shopify.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/_shopify.svelte
@@ -1,69 +1,92 @@
<script>
- import { t } from '$lib/translations';
- import { Icon } from '$comp';
- import { page } from '$app/stores';
+ import { t } from "$lib/translations";
+ import { Icon } from "$comp";
+ import { page } from "$app/stores";
- export let user;
- $: paymentsUrl =
- user.shopifyStore &&
- 'https://admin.shopify.com/store/' + user.shopifyStore + '/settings/payments';
- $: checkoutUrl =
- user.shopifyStore &&
- 'https://admin.shopify.com/store/' + user.shopifyStore + '/settings/checkout';
- $: appsUrl =
- user.shopifyStore &&
- 'https://admin.shopify.com/store/' + user.shopifyStore + '/settings/apps/development';
+ export let user;
+ $: paymentsUrl =
+ user.shopifyStore &&
+ "https://admin.shopify.com/store/" +
+ user.shopifyStore +
+ "/settings/payments";
+ $: checkoutUrl =
+ user.shopifyStore &&
+ "https://admin.shopify.com/store/" +
+ user.shopifyStore +
+ "/settings/checkout";
+ $: appsUrl =
+ user.shopifyStore &&
+ "https://admin.shopify.com/store/" +
+ user.shopifyStore +
+ "/settings/apps/development";
+
+ let script = `<script src="${$page.url.protocol}//${$page.url.host}/${user.username}/shopify.js"><\/script>`;
</script>
<div>
- <label for="shopifyStore" class="font-bold mb-1 block">Store ID</label>
- <div class="flex mb-2">
- <div class="text-gray-600 p-4 my-auto bg-gray-100 border rounded-l-2xl border-r-0 pr-1">
- admin.shopify.com/store/
- </div>
- <input
- type="text"
- name="shopifyStore"
- bind:value={user.shopifyStore}
- class="border-l-0 rounded-l-none w-auto min-w-0 pl-1 grow"
- />
- </div>
- <p class="text-secondary my-2">
- Add Bitcoin under <a class:active={!!paymentsUrl} href={paymentsUrl || undefined}
- >Payments > Manual payment methods</a
- > with a description like "Pay with Bitcoin".
- </p>
+ <label for="shopifyStore" class="font-bold mb-1 block">Store ID</label>
+ <div class="flex mb-2">
+ <div
+ class="text-gray-600 p-4 my-auto bg-gray-100 border rounded-l-2xl border-r-0 pr-1"
+ >
+ admin.shopify.com/store/
+ </div>
+ <input
+ type="text"
+ name="shopifyStore"
+ bind:value={user.shopifyStore}
+ class="border-l-0 rounded-l-none w-auto min-w-0 pl-1 grow"
+ />
+ </div>
+ <p class="text-secondary my-2">
+ Add Bitcoin under <a
+ class:active={!!paymentsUrl}
+ href={paymentsUrl || undefined}>Payments > Manual payment methods</a
+ > with a description like "Pay with Bitcoin".
+ </p>
</div>
<div>
- <label for="display" class="font-bold mb-1 block">Additional scripts</label>
- <p class="text-secondary my-2">
- Copy this into <a class:active={!!paymentsUrl} href={checkoutUrl}
- >Checkout -> Additional scripts</a
- >
- </p>
- <textarea rows={5} type="text" name="shopifyScripts" readonly><script src="{`${$page.url.protocol}//${$page.url.host}/${user.username}/shopify.js`}"> </script></textarea>
+ <label for="display" class="font-bold mb-1 block">Additional scripts</label>
+ <p class="text-secondary my-2">
+ Copy this into <a class:active={!!paymentsUrl} href={checkoutUrl}
+ >Checkout -> Additional scripts</a
+ >
+ </p>
+ <textarea rows={5} type="text" name="shopifyScripts" readonly
+ >{script}</textarea
+ >
</div>
<div>
- <label for="shopifyToken" class="font-bold mb-1 block">Admin API token <span class="font-normal">(Optional)</span></label>
- <p class="text-secondary my-2">Coinos can automatically mark orders as Paid if you provide an API token.
- <input class="my-4" type="text" name="shopifyToken" bind:value={user.shopifyToken} />
-
- <p class="text-secondary my-2">
- Create a new app in <a class:active={!!appsUrl} href={appsUrl || undefined}
- >Apps > App Development</a
- >
- </p>
+ <label for="shopifyToken" class="font-bold mb-1 block"
+ >Admin API token <span class="font-normal">(Optional)</span></label
+ >
+ <p class="text-secondary my-2">
+ Coinos can automatically mark orders as Paid if you provide an API token.
+ <input
+ class="my-4"
+ type="text"
+ name="shopifyToken"
+ bind:value={user.shopifyToken}
+ />
+ </p>
+ <p class="text-secondary my-2">
+ Create a new app in <a class:active={!!appsUrl} href={appsUrl || undefined}
+ >Apps > App Development</a
+ >
+ </p>
- <p class="text-secondary my-2">
- Edit the Admin API scopes to enable <code class="text-black">write_orders</code> permission then install the
- app into your store and copy the Admin API access token here.
- </p>
+ <p class="text-secondary my-2">
+ Edit the Admin API scopes to enable <code class="text-black"
+ >write_orders</code
+ > permission then install the app into your store and copy the Admin API access
+ token here.
+ </p>
</div>
<style>
- .active {
- @apply text-blue-500;
- }
+ .active {
+ @apply text-blue-500;
+ }
</style>
diff --git a/src/routes/(app)/[username]/shopify.js/+server.js b/src/routes/(app)/[username]/shopify.js/+server.js
index 7b5c9d41..c93ac729 100644
--- a/src/routes/(app)/[username]/shopify.js/+server.js
+++ b/src/routes/(app)/[username]/shopify.js/+server.js
@@ -1,7 +1,7 @@
export async function GET({ cookies, params, url }) {
- let { username } = params;
+ let { username } = params;
- let js = `
+ let js = `
var qrcode=function(r,_){var t=r,e=QRErrorCorrectLevel[_],n=null,$=0,o=null,a=[],f={},i=function(r,_){n=function(r){for(var _=Array(r),t=0;t<r;t+=1){_[t]=Array(r);for(var e=0;e<r;e+=1)_[t][e]=null}return _}($=4*t+17),u(0,0),u($-7,0),u(0,$-7),l(),g(),v(r,_),t>=7&&h(r),null==o&&(o=x(t,e,a)),s(o,_)},u=function(r,_){for(var t=-1;t<=7;t+=1)if(!(r+t<=-1)&&!($<=r+t))for(var e=-1;e<=7;e+=1)_+e<=-1||$<=_+e||(0<=t&&t<=6&&(0==e||6==e)||0<=e&&e<=6&&(0==t||6==t)||2<=t&&t<=4&&2<=e&&e<=4?n[r+t][_+e]=!0:n[r+t][_+e]=!1)},c=function(){for(var r=0,_=0,t=0;t<8;t+=1){i(!0,t);var e=QRUtil.getLostPoint(f);(0==t||r>e)&&(r=e,_=t)}return _},g=function(){for(var r=8;r<$-8;r+=1)null==n[r][6]&&(n[r][6]=r%2==0);for(var _=8;_<$-8;_+=1)null==n[6][_]&&(n[6][_]=_%2==0)},l=function(){for(var r=QRUtil.getPatternPosition(t),_=0;_<r.length;_+=1)for(var e=0;e<r.length;e+=1){var $=r[_],o=r[e];if(null==n[$][o])for(var a=-2;a<=2;a+=1)for(var f=-2;f<=2;f+=1)-2==a||2==a||-2==f||2==f||0==a&&0==f?n[$+a][o+f]=!0:n[$+a][o+f]=!1}},h=function(r){for(var _=QRUtil.getBCHTypeNumber(t),e=0;e<18;e+=1){var o=!r&&(_>>e&1)==1;n[Math.floor(e/3)][e%3+$-8-3]=o}for(var e=0;e<18;e+=1){var o=!r&&(_>>e&1)==1;n[e%3+$-8-3][Math.floor(e/3)]=o}},v=function(r,_){for(var t=QRUtil.getBCHTypeInfo(e<<3|_),o=0;o<15;o+=1){var a=!r&&(t>>o&1)==1;o<6?n[o][8]=a:o<8?n[o+1][8]=a:n[$-15+o][8]=a}for(var o=0;o<15;o+=1){var a=!r&&(t>>o&1)==1;o<8?n[8][$-o-1]=a:o<9?n[8][15-o-1+1]=a:n[8][15-o-1]=a}n[$-8][8]=!r},s=function(r,_){for(var t=-1,e=$-1,o=7,a=0,f=QRUtil.getMaskFunction(_),i=$-1;i>0;i-=2)for(6==i&&(i-=1);;){for(var u=0;u<2;u+=1)if(null==n[e][i-u]){var c=!1;a<r.length&&(c=(r[a]>>>o&1)==1),f(e,i-u)&&(c=!c),n[e][i-u]=c,-1==(o-=1)&&(a+=1,o=7)}if((e+=t)<0||$<=e){e-=t,t=-t;break}}},d=function(r,_){for(var t=0,e=0,n=0,$=Array(_.length),o=Array(_.length),a=0;a<_.length;a+=1){var f=_[a].dataCount,i=_[a].totalCount-f;e=Math.max(e,f),n=Math.max(n,i),$[a]=Array(f);for(var u=0;u<$[a].length;u+=1)$[a][u]=255&r.getBuffer()[u+t];t+=f;var c=QRUtil.getErrorCorrectPolynomial(i),g=qrPolynomial($[a],c.getLength()-1).mod(c);o[a]=Array(c.getLength()-1);for(var u=0;u<o[a].length;u+=1){var l=u+g.getLength()-o[a].length;o[a][u]=l>=0?g.getAt(l):0}}for(var h=0,u=0;u<_.length;u+=1)h+=_[u].totalCount;for(var v=Array(h),s=0,u=0;u<e;u+=1)for(var a=0;a<_.length;a+=1)u<$[a].length&&(v[s]=$[a][u],s+=1);for(var u=0;u<n;u+=1)for(var a=0;a<_.length;a+=1)u<o[a].length&&(v[s]=o[a][u],s+=1);return v},x=function(r,_,t){for(var e=QRRSBlock.getRSBlocks(r,_),n=qrBitBuffer(),$=0;$<t.length;$+=1){var o=t[$];n.put(o.getMode(),4),n.put(o.getLength(),QRUtil.getLengthInBits(o.getMode(),r)),o.write(n)}for(var a=0,$=0;$<e.length;$+=1)a+=e[$].dataCount;if(n.getLengthInBits()>8*a)throw Error("code length overflow. ("+n.getLengthInBits()+">"+8*a+")");for(n.getLengthInBits()+4<=8*a&&n.put(0,4);n.getLengthInBits()%8!=0;)n.putBit(!1);for(;!(n.getLengthInBits()>=8*a)&&(n.put(236,8),!(n.getLengthInBits()>=8*a));){;n.put(17,8)}return d(n,e)};return f.addData=function(r){var _=qr8BitByte(r);a.push(_),o=null},f.isDark=function(r,_){if(r<0||$<=r||_<0||$<=_)throw Error(r+","+_);return n[r][_]},f.getModuleCount=function(){return $},f.make=function(){i(!1,c())},f.createTableTag=function(r,_){r=r||2;var t="";t+='<table style="',t+=" border-width: 0px; border-style: none;",t+=" border-collapse: collapse;",t+=" padding: 0px; margin: "+(_=void 0===_?4*r:_)+"px;",t+='">',t+="<tbody>";for(var e=0;e<f.getModuleCount();e+=1){t+="<tr>";for(var n=0;n<f.getModuleCount();n+=1)t+='<td style="',t+=" border-width: 0px; border-style: none;",t+=" border-collapse: collapse;",t+=" padding: 0px; margin: 0px;",t+=" width: "+r+"px;",t+=" height: "+r+"px;",t+=" background-color: ",t+=f.isDark(e,n)?"#000000":"#ffffff",t+=";",t+='"/>';t+="</tr>"}return t+="</tbody>",t+="</table>"},f.createImgTag=function(r,_,t){r=r||2;var e=_=void 0===_?4*r:_,n=f.getModuleCount()*r+_;return createImgTag(t,t,function(_,t){if(!(e<=_)||!(_<n)||!(e<=t)||!(t<n))return 1;var $=Math.floor((_-e)/r),o=Math.floor((t-e)/r);return f.isDark(o,$)?0:1})},f};qrcode.stringToBytes=function(r){for(var _=[],t=0;t<r.length;t+=1){var e=r.charCodeAt(t);_.push(255&e)}return _},qrcode.createStringToBytes=function(r,_){var t=function(){for(var t=base64DecodeInputStream(r),e=function(){var r=t.read();if(-1==r)throw Error();return r},n=0,$={};;){var o=t.read();if(-1==o)break;var a=e(),f=e(),i=e(),u=String.fromCharCode(o<<8|a),c=f<<8|i;$[u]=c,n+=1}if(n!=_)throw Error(n+" != "+_);return $}();return function(r){for(var _=[],e=0;e<r.length;e+=1){var n=r.charCodeAt(e);if(n<128)_.push(n);else{var $=t[r.charAt(e)];"number"==typeof $?(255&$)==$?_.push($):(_.push($>>>8),_.push(255&$)):_.push(63)}}return _}};var QRMode={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},QRErrorCorrectLevel={L:1,M:0,Q:3,H:2},QRMaskPattern={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},QRUtil=function(){var r=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],_={},t=function(r){for(var _=0;0!=r;)_+=1,r>>>=1;return _};return _.getBCHTypeInfo=function(r){for(var _=r<<10;t(_)-t(1335)>=0;)_^=1335<<t(_)-t(1335);return(r<<10|_)^21522},_.getBCHTypeNumber=function(r){for(var _=r<<12;t(_)-t(7973)>=0;)_^=7973<<t(_)-t(7973);return r<<12|_},_.getPatternPosition=function(_){return r[_-1]},_.getMaskFunction=function(r){switch(r){case QRMaskPattern.PATTERN000:return function(r,_){return(r+_)%2==0};case QRMaskPattern.PATTERN001:return function(r,_){return r%2==0};case QRMaskPattern.PATTERN010:return function(r,_){return _%3==0};case QRMaskPattern.PATTERN011:return function(r,_){return(r+_)%3==0};case QRMaskPattern.PATTERN100:return function(r,_){return(Math.floor(r/2)+Math.floor(_/3))%2==0};case QRMaskPattern.PATTERN101:return function(r,_){return r*_%2+r*_%3==0};case QRMaskPattern.PATTERN110:return function(r,_){return(r*_%2+r*_%3)%2==0};case QRMaskPattern.PATTERN111:return function(r,_){return(r*_%3+(r+_)%2)%2==0};default:throw Error("bad maskPattern:"+r)}},_.getErrorCorrectPolynomial=function(r){for(var _=qrPolynomial([1],0),t=0;t<r;t+=1)_=_.multiply(qrPolynomial([1,QRMath.gexp(t)],0));return _},_.getLengthInBits=function(r,_){if(1<=_&&_<10)switch(r){case QRMode.MODE_NUMBER:return 10;case QRMode.MODE_ALPHA_NUM:return 9;case QRMode.MODE_8BIT_BYTE:case QRMode.MODE_KANJI:return 8;default:throw Error("mode:"+r)}else if(_<27)switch(r){case QRMode.MODE_NUMBER:return 12;case QRMode.MODE_ALPHA_NUM:return 11;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 10;default:throw Error("mode:"+r)}else if(_<41)switch(r){case QRMode.MODE_NUMBER:return 14;case QRMode.MODE_ALPHA_NUM:return 13;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 12;default:throw Error("mode:"+r)}else throw Error("type:"+_)},_.getLostPoint=function(r){for(var _=r.getModuleCount(),t=0,e=0;e<_;e+=1)for(var n=0;n<_;n+=1){for(var $=0,o=r.isDark(e,n),a=-1;a<=1;a+=1)if(!(e+a<0)&&!(_<=e+a))for(var f=-1;f<=1;f+=1)!(n+f<0)&&!(_<=n+f)&&(0!=a||0!=f)&&o==r.isDark(e+a,n+f)&&($+=1);$>5&&(t+=3+$-5)}for(var e=0;e<_-1;e+=1)for(var n=0;n<_-1;n+=1){var i=0;r.isDark(e,n)&&(i+=1),r.isDark(e+1,n)&&(i+=1),r.isDark(e,n+1)&&(i+=1),r.isDark(e+1,n+1)&&(i+=1),(0==i||4==i)&&(t+=3)}for(var e=0;e<_;e+=1)for(var n=0;n<_-6;n+=1)r.isDark(e,n)&&!r.isDark(e,n+1)&&r.isDark(e,n+2)&&r.isDark(e,n+3)&&r.isDark(e,n+4)&&!r.isDark(e,n+5)&&r.isDark(e,n+6)&&(t+=40);for(var n=0;n<_;n+=1)for(var e=0;e<_-6;e+=1)r.isDark(e,n)&&!r.isDark(e+1,n)&&r.isDark(e+2,n)&&r.isDark(e+3,n)&&r.isDark(e+4,n)&&!r.isDark(e+5,n)&&r.isDark(e+6,n)&&(t+=40);for(var u=0,n=0;n<_;n+=1)for(var e=0;e<_;e+=1)r.isDark(e,n)&&(u+=1);return t+10*(Math.abs(100*u/_/_-50)/5)},_}(),QRMath=function(){for(var r=Array(256),_=Array(256),t=0;t<8;t+=1)r[t]=1<<t;for(var t=8;t<256;t+=1)r[t]=r[t-4]^r[t-5]^r[t-6]^r[t-8];for(var t=0;t<255;t+=1)_[r[t]]=t;var e={};return e.glog=function(r){if(r<1)throw Error("glog("+r+")");return _[r]},e.gexp=function(_){for(;_<0;)_+=255;for(;_>=256;)_-=255;return r[_]},e}();function qrPolynomial(r,_){if(void 0===r.length)throw Error(r.length+"/"+_);var t=function(){for(var t=0;t<r.length&&0==r[t];)t+=1;for(var e=Array(r.length-t+_),n=0;n<r.length-t;n+=1)e[n]=r[n+t];return e}(),e={};return e.getAt=function(r){return t[r]},e.getLength=function(){return t.length},e.multiply=function(r){for(var _=Array(e.getLength()+r.getLength()-1),t=0;t<e.getLength();t+=1)for(var n=0;n<r.getLength();n+=1)_[t+n]^=QRMath.gexp(QRMath.glog(e.getAt(t))+QRMath.glog(r.getAt(n)));return qrPolynomial(_,0)},e.mod=function(r){if(e.getLength()-r.getLength()<0)return e;for(var _=QRMath.glog(e.getAt(0))-QRMath.glog(r.getAt(0)),t=Array(e.getLength()),n=0;n<e.getLength();n+=1)t[n]=e.getAt(n);for(var n=0;n<r.getLength();n+=1)t[n]^=QRMath.gexp(QRMath.glog(r.getAt(n))+_);return qrPolynomial(t,0).mod(r)},e}var QRRSBlock=function(){var r=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],_=function(r,_){var t={};return t.totalCount=r,t.dataCount=_,t},t={},e=function(_,t){switch(t){case QRErrorCorrectLevel.L:return r[(_-1)*4+0];case QRErrorCorrectLevel.M:return r[(_-1)*4+1];case QRErrorCorrectLevel.Q:return r[(_-1)*4+2];case QRErrorCorrectLevel.H:return r[(_-1)*4+3];default:return}};return t.getRSBlocks=function(r,t){var n=e(r,t);if(void 0===n)throw Error("bad rs block @ typeNumber:"+r+"/errorCorrectLevel:"+t);for(var $=n.length/3,o=[],a=0;a<$;a+=1)for(var f=n[3*a+0],i=n[3*a+1],u=n[3*a+2],c=0;c<f;c+=1)o.push(_(i,u));return o},t}(),qrBitBuffer=function(){var r=[],_=0,t={};return t.getBuffer=function(){return r},t.getAt=function(_){return(r[Math.floor(_/8)]>>>7-_%8&1)==1},t.put=function(r,_){for(var e=0;e<_;e+=1)t.putBit((r>>>_-e-1&1)==1)},t.getLengthInBits=function(){return _},t.putBit=function(t){var e=Math.floor(_/8);r.length<=e&&r.push(0),t&&(r[e]|=128>>>_%8),_+=1},t},qr8BitByte=function(r){for(var _=QRMode.MODE_8BIT_BYTE,t=r,e=[],n={},$=0,o=t.length;$<o;$++){var a=[],f=t.charCodeAt($);f>65536?(a[0]=240|(1835008&f)>>>18,a[1]=128|(258048&f)>>>12,a[2]=128|(4032&f)>>>6,a[3]=128|63&f):f>2048?(a[0]=224|(61440&f)>>>12,a[1]=128|(4032&f)>>>6,a[2]=128|63&f):f>128?(a[0]=192|(1984&f)>>>6,a[1]=128|63&f):a[0]=f,e.push(a)}(e=Array.prototype.concat.apply([],e)).length!=t.length&&(e.unshift(191),e.unshift(187),e.unshift(239));var i=e;return n.getMode=function(){return _},n.getLength=function(r){return i.length},n.write=function(r){for(var _=0;_<i.length;_+=1)r.put(i[_],8)},n},byteArrayOutputStream=function(){var r=[],_={};return _.writeByte=function(_){r.push(255&_)},_.writeShort=function(r){_.writeByte(r),_.writeByte(r>>>8)},_.writeBytes=function(r,t,e){t=t||0,e=e||r.length;for(var n=0;n<e;n+=1)_.writeByte(r[n+t])},_.writeString=function(r){for(var t=0;t<r.length;t+=1)_.writeByte(r.charCodeAt(t))},_.toByteArray=function(){return r},_.toString=function(){var _="";_+="[";for(var t=0;t<r.length;t+=1)t>0&&(_+=","),_+=r[t];return _+"]"},_},base64EncodeOutputStream=function(){var r=0,_=0,t=0,e="",n={},$=function(r){e+=String.fromCharCode(o(63&r))},o=function(r){if(r<0);else if(r<26)return 65+r;else if(r<52)return 97+(r-26);else if(r<62)return 48+(r-52);else if(62==r)return 43;else if(63==r)return 47;throw Error("n:"+r)};return n.writeByte=function(e){for(r=r<<8|255&e,_+=8,t+=1;_>=6;)$(r>>>_-6),_-=6},n.flush=function(){if(_>0&&($(r<<6-_),r=0,_=0),t%3!=0)for(var n=3-t%3,o=0;o<n;o+=1)e+="="},n.toString=function(){return e},n},base64DecodeInputStream=function(r){var _=r,t=0,e=0,n=0,$={};$.read=function(){for(;n<8;){if(t>=_.length){if(0==n)return -1;throw Error("unexpected end of file./"+n)}var r=_.charAt(t);if(t+=1,"="==r)return n=0,-1;!r.match(/^\s$/)&&(e=e<<6|o(r.charCodeAt(0)),n+=6)}var $=e>>>n-8&255;return n-=8,$};var o=function(r){if(65<=r&&r<=90)return r-65;if(97<=r&&r<=122)return r-97+26;if(48<=r&&r<=57)return r-48+52;if(43==r)return 62;if(47==r)return 63;else throw Error("c:"+r)};return $},gifImage=function(r,_){var t=r,e=_,n=Array(r*_),$={};$.setPixel=function(r,_,e){n[_*t+r]=e},$.write=function(r){r.writeString("GIF87a"),r.writeShort(t),r.writeShort(e),r.writeByte(128),r.writeByte(0),r.writeByte(0),r.writeByte(0),r.writeByte(0),r.writeByte(0),r.writeByte(255),r.writeByte(255),r.writeByte(255),r.writeString(","),r.writeShort(0),r.writeShort(0),r.writeShort(t),r.writeShort(e),r.writeByte(0);var _=a(2);r.writeByte(2);for(var n=0;_.length-n>255;)r.writeByte(255),r.writeBytes(_,n,255),n+=255;r.writeByte(_.length-n),r.writeBytes(_,n,_.length-n),r.writeByte(0),r.writeString(";")};var o=function(r){var _=r,t=0,e=0,n={};return n.write=function(r,n){if(r>>>n!=0)throw Error("length over");for(;t+n>=8;)_.writeByte(255&(r<<t|e)),n-=8-t,r>>>=8-t,e=0,t=0;e=r<<t|e,t+=n},n.flush=function(){t>0&&_.writeByte(e)},n},a=function(r){for(var _=1<<r,t=(1<<r)+1,e=r+1,$=f(),a=0;a<_;a+=1)$.add(String.fromCharCode(a));$.add(String.fromCharCode(_)),$.add(String.fromCharCode(t));var i=byteArrayOutputStream(),u=o(i);u.write(_,e);var c=0,g=String.fromCharCode(n[c]);for(c+=1;c<n.length;){var l=String.fromCharCode(n[c]);c+=1,$.contains(g+l)?g+=l:(u.write($.indexOf(g),e),4095>$.size()&&($.size()==1<<e&&(e+=1),$.add(g+l)),g=l)}return u.write($.indexOf(g),e),u.write(t,e),u.flush(),i.toByteArray()},f=function(){var r={},_=0,t={};return t.add=function(e){if(t.contains(e))throw Error("dup key:"+e);r[e]=_,_+=1},t.size=function(){return _},t.indexOf=function(_){return r[_]},t.contains=function(_){return void 0!==r[_]},t};return $},createImgTag=function(r,_,t,e){for(var n=gifImage(r,_),$=0;$<_;$+=1)for(var o=0;o<r;o+=1)n.setPixel(o,$,t(o,$));var a=byteArrayOutputStream();n.write(a);for(var f=base64EncodeOutputStream(),i=a.toByteArray(),u=0;u<i.length;u+=1)f.writeByte(i[u]);f.flush();var c="";return c+="data:image/gif;base64,",c+=f},drawImg=function(r,_){var t,e=(_=_||{}).typeNumber||4,n=_.errorCorrectLevel||"M",$=_.size||500;try{(t=qrcode(e,n||"M")).addData(r),t.make()}catch(o){if(!(e>=40))return drawImg(r,{size:$,errorCorrectLevel:n,typeNumber:e+1});throw Error("Text too long to encode")}var a=parseInt($/t.getModuleCount()),f=parseInt(($-t.getModuleCount()*a)/2);return t.createImgTag(a,f,$)};
@@ -183,9 +183,9 @@ window.addEventListener('load', async () => {
});
`;
- return new Response(js, {
- headers: {
- 'Content-Type': 'application/javascript'
- }
- });
+ return new Response(js, {
+ headers: {
+ "Content-Type": "application/javascript",
+ },
+ });
}
diff --git a/src/routes/(app)/admin/+layout.server.js b/src/routes/(app)/admin/+layout.server.js
index 26449467..e49c6e40 100644
--- a/src/routes/(app)/admin/+layout.server.js
+++ b/src/routes/(app)/admin/+layout.server.js
@@ -1,11 +1,11 @@
-import { auth, get } from '$lib/utils';
-import { redirect } from '@sveltejs/kit';
+import { auth, get } from "$lib/utils";
+import { redirect } from "@sveltejs/kit";
export async function load({ cookies, parent }) {
- let token = cookies.get('token');
- if (!token) throw redirect(307, '/login');
- let user = await get('/me', auth(cookies));
- if (!user) throw redirect(307, '/login');
+ let token = cookies.get("token");
+ if (!token) throw redirect(307, "/login");
+ let user = await get("/me", auth(cookies));
+ if (!user) throw redirect(307, "/login");
- return { user };
+ return { user };
}
diff --git a/src/routes/(app)/admin/+layout.svelte b/src/routes/(app)/admin/+layout.svelte
index d7561e32..33b64b6a 100644
--- a/src/routes/(app)/admin/+layout.svelte
+++ b/src/routes/(app)/admin/+layout.svelte
@@ -1,10 +1,10 @@
<script>
- import { LandingHeader } from '$comp';
- export let data;
- let { user } = data;
+ import { LandingHeader } from "$comp";
+ export let data;
+ let { user } = data;
</script>
<LandingHeader {user} />
<main class="container px-10 md:px-40 mx-auto min-h-[600px] pb-20">
- <slot />
+ <slot />
</main>
diff --git a/src/routes/(app)/admin/+page.server.js b/src/routes/(app)/admin/+page.server.js
index e19f894c..93931aa3 100644
--- a/src/routes/(app)/admin/+page.server.js
+++ b/src/routes/(app)/admin/+page.server.js
@@ -1,10 +1,12 @@
-import { auth, get } from '$lib/utils';
-import { redirect } from '@sveltejs/kit';
+import { auth, get } from "$lib/utils";
+import { redirect } from "@sveltejs/kit";
export async function load({ cookies, parent }) {
- let { user } = await parent();
- if (!user) throw redirect(307, '/login');
+ let { user } = await parent();
+ if (!user) throw redirect(307, "/login");
- let users = (await get('/users', auth(cookies))).sort((a, b) => b.balance - a.balance);
- return { users };
+ let users = (await get("/users", auth(cookies))).sort(
+ (a, b) => b.balance - a.balance
+ );
+ return { users };
}
diff --git a/src/routes/(app)/admin/+page.svelte b/src/routes/(app)/admin/+page.svelte
index 529d2f77..c61a2e45 100644
--- a/src/routes/(app)/admin/+page.svelte
+++ b/src/routes/(app)/admin/+page.svelte
@@ -1,22 +1,22 @@
<script>
- import { post } from '$lib/utils';
- export let data;
- let reset = (username) => post(`/${username}/reset`, { password });
- let { users } = data;
- let password;
+ import { post } from "$lib/utils";
+ export let data;
+ let reset = (username) => post(`/${username}/reset`, { password });
+ let { users } = data;
+ let password;
</script>
{#each users as { balance, username, expected }}
- <div class="grid grid-cols-5">
- <div>{username}</div>
- <div>{balance}</div>
- <div>{expected}</div>
- <div><input bind:value={password} /></div>
- <div>
- <button
- class="text-sm md:text-lg rounded-full border py-2 px-4 hover:opacity-80 min-w-[72px] bg-black text-white"
- on:click={() => reset(username)}>Reset</button
- >
- </div>
- </div>
+ <div class="grid grid-cols-5">
+ <div>{username}</div>
+ <div>{balance}</div>
+ <div>{expected}</div>
+ <div><input bind:value={password} /></div>
+ <div>
+ <button
+ class="text-sm md:text-lg rounded-full border py-2 px-4 hover:opacity-80 min-w-[72px] bg-black text-white"
+ on:click={() => reset(username)}>Reset</button
+ >
+ </div>
+ </div>
{/each}
diff --git a/src/routes/(app)/buy/+page.server.js b/src/routes/(app)/buy/+page.server.js
index fc7ae473..3d3265cb 100644
--- a/src/routes/(app)/buy/+page.server.js
+++ b/src/routes/(app)/buy/+page.server.js
@@ -1,27 +1,28 @@
-import { env } from '$env/dynamic/private';
-import { error, fail, redirect } from '@sveltejs/kit';
-import { post, auth } from '$lib/utils';
-import { createTicket } from '$lib/ticket';
+import { env } from "$env/dynamic/private";
+import { error, fail, redirect } from "@sveltejs/kit";
+import { post, auth } from "$lib/utils";
+import { createTicket } from "$lib/ticket";
export const actions = {
- default: async ({ cookies, request }) => {
- let form = await request.formData();
+ default: async ({ cookies, request }) => {
+ let form = await request.formData();
- let amount = parseFloat(form.get('amount'));
- if (!amount || amount < 0 || amount > 500) return fail(400, { message: 'Invalid amount' });
+ let amount = parseFloat(form.get("amount"));
+ if (!amount || amount < 0 || amount > 500)
+ return fail(400, { message: "Invalid amount" });
- await post(
- '/buy',
- {
- amount: form.get('amount'),
- number: form.get('number').replace(/\s+/g, ''),
- month: form.get('expiry').slice(0, 2),
- year: form.get('expiry').slice(-2),
- cvc: form.get('cvc')
- },
- auth(cookies)
- );
+ await post(
+ "/buy",
+ {
+ amount: form.get("amount"),
+ number: form.get("number").replace(/\s+/g, ""),
+ month: form.get("expiry").slice(0, 2),
+ year: form.get("expiry").slice(-2),
+ cvc: form.get("cvc"),
+ },
+ auth(cookies)
+ );
- throw redirect(307, `/${form.get('username')}/payments`);
- }
+ throw redirect(307, `/${form.get("username")}/payments`);
+ },
};
diff --git a/src/routes/(app)/buy/+page.svelte b/src/routes/(app)/buy/+page.svelte
index 22e7e9ec..2f999555 100644
--- a/src/routes/(app)/buy/+page.svelte
+++ b/src/routes/(app)/buy/+page.svelte
@@ -1,131 +1,135 @@
<script>
- import { enhance } from '$app/forms';
- import { Icon, Lock, Card, Calendar, Spinner } from '$comp';
- import { animatedRate } from '$lib/store';
- import { f } from '$lib/utils';
+ import { enhance } from "$app/forms";
+ import { Icon, Lock, Card, Calendar, Spinner } from "$comp";
+ import { animatedRate } from "$lib/store";
+ import { f } from "$lib/utils";
- export let data, form;
+ export let data, form;
- let { username, currency } = data.user;
- let loading;
+ let { username, currency } = data.user;
+ let loading;
- let amount, n, d, cvc;
+ let amount, n, d, cvc;
</script>
<div class="container px-4 max-w-xl mx-auto mt-10 space-y-5">
- <div class="w-full flex">
- <a href="/" class="mx-auto">
- <Icon icon="logo" />
- </a>
- </div>
+ <div class="w-full flex">
+ <a href="/" class="mx-auto">
+ <Icon icon="logo" />
+ </a>
+ </div>
- <div class="flex justify-center items-center">
- <div class="shadow-xl rounded-3xl px-10 pt-5 pb-10 space-y-5 w-full mx-5">
- <div class="relative">
- <h1 class="text-2xl md:text-3xl font-semibold text-center">Buy Bitcoin</h1>
- </div>
- <p class="text-secondary text-center">Purchase up to $500 worth of Bitcoin</p>
+ <div class="flex justify-center items-center">
+ <div class="shadow-xl rounded-3xl px-10 pt-5 pb-10 space-y-5 w-full mx-5">
+ <div class="relative">
+ <h1 class="text-2xl md:text-3xl font-semibold text-center">
+ Buy Bitcoin
+ </h1>
+ </div>
+ <p class="text-secondary text-center">
+ Purchase up to $500 worth of Bitcoin
+ </p>
- {#if form?.message}
- <div class="text-red-600 text-center">
- {form.message}
- </div>
- {/if}
+ {#if form?.message}
+ <div class="text-red-600 text-center">
+ {form.message}
+ </div>
+ {/if}
- <form
- method="POST"
- class="flex flex-wrap gap-5 w-full p-5 mt-20"
- use:enhance={() => {
- loading = true;
- return ({ update }) => update() && (loading = false);
- }}
- >
- <input type="hidden" name="username" value={username} />
+ <form
+ method="POST"
+ class="flex flex-wrap gap-5 w-full p-5 mt-20"
+ use:enhance={() => {
+ loading = true;
+ return ({ update }) => update() && (loading = false);
+ }}
+ >
+ <input type="hidden" name="username" value={username} />
- <label class="relative w-full flex flex-col">
- <span class="font-bold mb-3">Amount</span>
- <input
- class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
- type="text"
- name="amount"
- placeholder="&bull;&bull;&bull;"
- bind:value={amount}
- />
- <span
- class="absolute bottom-[9px] left-0 translate-x-1/2 w-6 text-black peer-placeholder-shown:text-gray-300
+ <label class="relative w-full flex flex-col">
+ <span class="font-bold mb-3">Amount</span>
+ <input
+ class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
+ type="text"
+ name="amount"
+ placeholder="&bull;&bull;&bull;"
+ bind:value={amount}
+ />
+ <span
+ class="absolute bottom-[9px] left-0 translate-x-1/2 w-6 text-black peer-placeholder-shown:text-gray-300
">$</span
- >
- </label>
+ >
+ </label>
- <label class="relative w-full flex flex-col">
- <span class="font-bold mb-3">Card number</span>
- <input
- class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
- type="text"
- name="number"
- placeholder="0000 0000 0000 0000"
- bind:value={n}
- />
- <Card />
- </label>
+ <label class="relative w-full flex flex-col">
+ <span class="font-bold mb-3">Card number</span>
+ <input
+ class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
+ type="text"
+ name="number"
+ placeholder="0000 0000 0000 0000"
+ bind:value={n}
+ />
+ <Card />
+ </label>
- <label class="relative flex-1 flex flex-col">
- <span class="font-bold mb-3">Expire date</span>
- <input
- class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
- type="text"
- name="expiry"
- placeholder="MM/YY"
- bind:value={d}
- />
- <Calendar />
- </label>
+ <label class="relative flex-1 flex flex-col">
+ <span class="font-bold mb-3">Expire date</span>
+ <input
+ class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
+ type="text"
+ name="expiry"
+ placeholder="MM/YY"
+ bind:value={d}
+ />
+ <Calendar />
+ </label>
- <label class="relative flex-1 flex flex-col">
- <span class="font-bold flex items-center gap-3 mb-3">
- CVC/CVV
- <span class="relative group">
- <span
- class="hidden group-hover:flex justify-center items-center px-2 py-1 text-xs absolute -right-2 transform translate-x-full -translate-y-1/2 w-max top-1/2 bg-black text-white"
- >
- 3-digit number on the back of your card</span
- >
- </span>
- </span>
- <input
- class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
- type="text"
- name="cvc"
- placeholder="&bull;&bull;&bull;"
- bind:value={cvc}
- />
- <Lock />
- </label>
+ <label class="relative flex-1 flex flex-col">
+ <span class="font-bold flex items-center gap-3 mb-3">
+ CVC/CVV
+ <span class="relative group">
+ <span
+ class="hidden group-hover:flex justify-center items-center px-2 py-1 text-xs absolute -right-2 transform translate-x-full -translate-y-1/2 w-max top-1/2 bg-black text-white"
+ >
+ 3-digit number on the back of your card</span
+ >
+ </span>
+ </span>
+ <input
+ class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
+ type="text"
+ name="cvc"
+ placeholder="&bull;&bull;&bull;"
+ bind:value={cvc}
+ />
+ <Lock />
+ </label>
- <div class="w-full flex mt-8">
- <button
- class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 mx-auto text-xl"
- type="submit"
- disabled={loading}
- >
- {#if loading}
- <Spinner />
- {:else}
- Submit
- {/if}
- </button>
- </div>
- </form>
+ <div class="w-full flex mt-8">
+ <button
+ class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 mx-auto text-xl"
+ type="submit"
+ disabled={loading}
+ >
+ {#if loading}
+ <Spinner />
+ {:else}
+ Submit
+ {/if}
+ </button>
+ </div>
+ </form>
- <div class="flex">
- <div class="text-secondary flex mr-auto">
- <div class="flex mr-1">
- <div class="my-auto mr-1">1</div>
- <img src="/images/bitcoin.svg" class="w-5 my-auto" alt="Bitcoin" />
- </div>
- </div>
- <p class="text-secondary">3% credit card fee applies</p>
- </div>
- </div>
- </div>
+ <div class="flex">
+ <div class="text-secondary flex mr-auto">
+ <div class="flex mr-1">
+ <div class="my-auto mr-1">1</div>
+ <img src="/images/bitcoin.svg" class="w-5 my-auto" alt="Bitcoin" />
+ </div>
+ </div>
+ <p class="text-secondary">3% credit card fee applies</p>
+ </div>
+ </div>
+ </div>
</div>
diff --git a/src/routes/(app)/disable2fa/+server.js b/src/routes/(app)/disable2fa/+server.js
index b2bb07ec..1e264398 100644
--- a/src/routes/(app)/disable2fa/+server.js
+++ b/src/routes/(app)/disable2fa/+server.js
@@ -1,13 +1,13 @@
-import { error, json } from '@sveltejs/kit';
-import { auth, post } from '$lib/utils';
+import { error, json } from "@sveltejs/kit";
+import { auth, post } from "$lib/utils";
export async function POST({ cookies, request }) {
- try {
- let body = await request.json();
- let res = await post('/disable2fa', body, auth(cookies));
+ try {
+ let body = await request.json();
+ let res = await post("/disable2fa", body, auth(cookies));
- return json(res);
- } catch (e) {
- throw error(500, 'Problem disabling 2fa');
- }
+ return json(res);
+ } catch (e) {
+ throw error(500, "Problem disabling 2fa");
+ }
}
diff --git a/src/routes/(app)/enable2fa/+server.js b/src/routes/(app)/enable2fa/+server.js
index 344850da..6917ae21 100644
--- a/src/routes/(app)/enable2fa/+server.js
+++ b/src/routes/(app)/enable2fa/+server.js
@@ -1,8 +1,8 @@
-import { auth, post } from '$lib/utils';
+import { auth, post } from "$lib/utils";
export async function POST({ cookies, request }) {
- let body = await request.json();
- let res = await post('/2fa', body, auth(cookies));
+ let body = await request.json();
+ let res = await post("/2fa", body, auth(cookies));
- return new Response(JSON.stringify(res));
+ return new Response(JSON.stringify(res));
}
diff --git a/src/routes/(app)/events/+server.js b/src/routes/(app)/events/+server.js
index decd43cb..95df0bc5 100644
--- a/src/routes/(app)/events/+server.js
+++ b/src/routes/(app)/events/+server.js
@@ -1,6 +1,6 @@
-import { auth, post } from '$lib/utils';
-import { json } from '@sveltejs/kit';
+import { auth, post } from "$lib/utils";
+import { json } from "@sveltejs/kit";
export async function POST({ cookies, request }) {
- return json(post('/event', await request.json(), auth(cookies)));
+ return json(post("/event", await request.json(), auth(cookies)));
}
diff --git a/src/routes/(app)/fund/[id]/+page.server.js b/src/routes/(app)/fund/[id]/+page.server.js
index 4194deed..6c5c943f 100644
--- a/src/routes/(app)/fund/[id]/+page.server.js
+++ b/src/routes/(app)/fund/[id]/+page.server.js
@@ -1,23 +1,23 @@
-import { error, redirect } from '@sveltejs/kit';
-import { auth, get, post } from '$lib/utils';
-import Qr from 'qrcode-base64';
+import { error, redirect } from "@sveltejs/kit";
+import { auth, get, post } from "$lib/utils";
+import Qr from "qrcode-base64";
export let load = async ({ params: { id }, url }) => {
- let { amount, payments } = await get(`/fund/${id}`);
+ let { amount, payments } = await get(`/fund/${id}`);
- let src = Qr.drawImg(url.href, { size: 300 });
- return { amount, payments, src };
+ let src = Qr.drawImg(url.href, { size: 300 });
+ return { amount, payments, src };
};
export const actions = {
- default: async ({ cookies, locals, params, request }) => {
- let { user } = locals;
+ default: async ({ cookies, locals, params, request }) => {
+ let { user } = locals;
- if (user) {
- await post('/redeem', params, auth(cookies));
- throw redirect(307, `/${user.username}/payments`);
- }
+ if (user) {
+ await post("/redeem", params, auth(cookies));
+ throw redirect(307, `/${user.username}/payments`);
+ }
- throw redirect(307, '/register');
- }
+ throw redirect(307, "/register");
+ },
};
diff --git a/src/routes/(app)/fund/[id]/+page.svelte b/src/routes/(app)/fund/[id]/+page.svelte
index d2d3079c..117a4b42 100644
--- a/src/routes/(app)/fund/[id]/+page.svelte
+++ b/src/routes/(app)/fund/[id]/+page.svelte
@@ -1,138 +1,146 @@
<script>
- import { format } from 'date-fns';
- import { enhance } from '$app/forms';
- import { page } from '$app/stores';
- import { Avatar, Icon } from '$comp';
- import { toast } from '@zerodevx/svelte-toast';
- import { t } from '$lib/translations';
- import { types, copy, f, s, sat, sats } from '$lib/utils';
- import { loginRedirect } from '$lib/store';
+ import { format } from "date-fns";
+ import { enhance } from "$app/forms";
+ import { page } from "$app/stores";
+ import { Avatar, Icon } from "$comp";
+ import { toast } from "@zerodevx/svelte-toast";
+ import { t } from "$lib/translations";
+ import { types, copy, f, s, sat, sats } from "$lib/utils";
+ import { loginRedirect } from "$lib/store";
- export let data;
+ export let data;
- let { amount, src, payments, rates, user } = data;
- let { id } = $page.params;
+ let { amount, src, payments, rates, user } = data;
+ let { id } = $page.params;
- toast.pop(0);
+ toast.pop(0);
- let currency = user ? user.currency : 'CAD';
- let rate = rates[currency];
+ let currency = user ? user.currency : "CAD";
+ let rate = rates[currency];
- $: amountFiat = parseFloat(((amount * rate) / sats).toFixed(2));
- $loginRedirect = $page.url.pathname;
+ $: amountFiat = parseFloat(((amount * rate) / sats).toFixed(2));
+ $loginRedirect = $page.url.pathname;
- let show;
- let toggle = () => (show = !show);
+ let show;
+ let toggle = () => (show = !show);
</script>
<div class="container px-4 max-w-4xl mx-auto mt-10 space-y-5">
- <div class="flex justify-center items-center">
- <div class="md:shadow-xl rounded-3xl md:px-10 pt-5 pb-10 space-y-5 w-full md:mx-5">
- <div class="relative flex justify-center gap-2">
- <!-- <p class="absolute right-0 top-1 underline"><Icon icon="settings" /></p> -->
- {#if id.split('-').length === 4}
- <h1 class="text-2xl md:text-3xl font-semibold my-auto">Card {id}</h1>
- {:else}
- <img src="/images/moneypot.jpg" class="w-24" alt="Fund" />
- <div class="my-auto">
- <h1 class="text-2xl font-semibold my-auto">{$t('payments.fund')}</h1>
- <h1 class="text-secondary my-auto">{id}</h1>
- </div>
- {/if}
- </div>
- {#if show}
- <img {src} class="mx-auto" alt={id} />
- {/if}
- <div class="flex justify-center gap-4">
- <div class="my-auto">
- <h2 class="text-2xl md:text-3xl font-semibold">
- {f(amountFiat, currency)}
- </h2>
- <h3 class="text-secondary md:text-lg mt-1">
- {sat(amount)}
- </h3>
- </div>
- </div>
- <div class="flex gap-2" data-sveltekit-prefetch="off">
- <div class="grow">
- <a href={`/send/fund/${id}`}>
- <button
- class="rounded-full border py-3 px-2 font-bold hover:opacity-80 flex gap-1 w-full justify-center"
- >
- <Icon icon="plus" />
- {$t('payments.addFunds')}
- </button>
- </a>
- </div>
- <div class="grow">
- <a href={`/fund/${id}/withdraw`}>
- <button
- class="rounded-full border py-3 px-2 font-bold hover:opacity-80 flex gap-1 w-full justify-center"
- >
- <Icon icon="minus" style="rotate-180" />
- {$t('payments.takeFunds')}
- </button>
- </a>
- </div>
- </div>
- <div class="flex gap-2" data-sveltekit-prefetch="off">
- <button
- class="rounded-full border py-3 px-2 font-bold hover:opacity-80 flex gap-1 w-full justify-center"
- on:click={toggle}
- >
- <Icon icon="qr" style="invert" />
- {show ? $t('payments.hide') : $t('payments.show')} QR
- </button>
- <button
- class="rounded-full border py-3 px-2 font-bold hover:opacity-80 flex gap-1 w-full justify-center"
- on:click={() => copy($page.url.host + $page.url.pathname)}
- >
- <Icon icon="copy" style="w-6" />
- {$t('payments.copyLink')}
- </button>
- </div>
- <div class="text-base">
- {#each payments as p}
- <a href={`/payment/${p.id}`}>
- <div class="grid grid-cols-3 border-b h-24 hover:bg-gray-100 px-4">
- <div class="whitespace-nowrap my-auto">
- <div class="font-bold" class:text-red-800={p.amount > 0}>
- {f(Math.abs(p.amount) * (p.rate / sats), p.currency)}
- {#if p.tip}
- <span class="text-sm text-secondary">
- +{Math.round((p.tip / Math.abs(p.amount)) * 100)}%
- </span>
- {/if}
- </div>
+ <div class="flex justify-center items-center">
+ <div
+ class="md:shadow-xl rounded-3xl md:px-10 pt-5 pb-10 space-y-5 w-full md:mx-5"
+ >
+ <div class="relative flex justify-center gap-2">
+ <!-- <p class="absolute right-0 top-1 underline"><Icon icon="settings" /></p> -->
+ {#if id.split("-").length === 4}
+ <h1 class="text-2xl md:text-3xl font-semibold my-auto">Card {id}</h1>
+ {:else}
+ <img src="/images/moneypot.jpg" class="w-24" alt="Fund" />
+ <div class="my-auto">
+ <h1 class="text-2xl font-semibold my-auto">
+ {$t("payments.fund")}
+ </h1>
+ <h1 class="text-secondary my-auto">{id}</h1>
+ </div>
+ {/if}
+ </div>
+ {#if show}
+ <img {src} class="mx-auto" alt={id} />
+ {/if}
+ <div class="flex justify-center gap-4">
+ <div class="my-auto">
+ <h2 class="text-2xl md:text-3xl font-semibold">
+ {f(amountFiat, currency)}
+ </h2>
+ <h3 class="text-secondary md:text-lg mt-1">
+ {sat(amount)}
+ </h3>
+ </div>
+ </div>
+ <div class="flex gap-2" data-sveltekit-prefetch="off">
+ <div class="grow">
+ <a href={`/send/fund/${id}`}>
+ <button
+ class="rounded-full border py-3 px-2 font-bold hover:opacity-80 flex gap-1 w-full justify-center"
+ >
+ <Icon icon="plus" />
+ {$t("payments.addFunds")}
+ </button>
+ </a>
+ </div>
+ <div class="grow">
+ <a href={`/fund/${id}/withdraw`}>
+ <button
+ class="rounded-full border py-3 px-2 font-bold hover:opacity-80 flex gap-1 w-full justify-center"
+ >
+ <Icon icon="minus" style="rotate-180" />
+ {$t("payments.takeFunds")}
+ </button>
+ </a>
+ </div>
+ </div>
+ <div class="flex gap-2" data-sveltekit-prefetch="off">
+ <button
+ class="rounded-full border py-3 px-2 font-bold hover:opacity-80 flex gap-1 w-full justify-center"
+ on:click={toggle}
+ >
+ <Icon icon="qr" style="invert" />
+ {show ? $t("payments.hide") : $t("payments.show")} QR
+ </button>
+ <button
+ class="rounded-full border py-3 px-2 font-bold hover:opacity-80 flex gap-1 w-full justify-center"
+ on:click={() => copy($page.url.host + $page.url.pathname)}
+ >
+ <Icon icon="copy" style="w-6" />
+ {$t("payments.copyLink")}
+ </button>
+ </div>
+ <div class="text-base">
+ {#each payments as p}
+ <a href={`/payment/${p.id}`}>
+ <div class="grid grid-cols-3 border-b h-24 hover:bg-gray-100 px-4">
+ <div class="whitespace-nowrap my-auto">
+ <div class="font-bold" class:text-red-800={p.amount > 0}>
+ {f(Math.abs(p.amount) * (p.rate / sats), p.currency)}
+ {#if p.tip}
+ <span class="text-sm text-secondary">
+ +{Math.round((p.tip / Math.abs(p.amount)) * 100)}%
+ </span>
+ {/if}
+ </div>
- <div class="text-secondary">
- {sat(Math.abs(p.amount) + (p.tip || 0))}
- </div>
- </div>
+ <div class="text-secondary">
+ {sat(Math.abs(p.amount) + (p.tip || 0))}
+ </div>
+ </div>
- <div class="flex my-auto">
- <div class="flex">
- <div class="my-auto">
- <Avatar user={p.user} size={16} disabled={true} />
- </div>
- <div class="my-auto ml-1 text-secondary">{p.user.username}</div>
- </div>
- </div>
+ <div class="flex my-auto">
+ <div class="flex">
+ <div class="my-auto">
+ <Avatar user={p.user} size={16} disabled={true} />
+ </div>
+ <div class="my-auto ml-1 text-secondary">
+ {p.user.username}
+ </div>
+ </div>
+ </div>
- <div class="text-secondary text-right text-sm my-auto">
- <div>
- {format(new Date(p.created), 'h:mm aaa')}
- </div>
- <div>
- {format(new Date(p.created), 'MMM d')}
- </div>
- </div>
- </div>
- </a>
- {:else}
- <p class="text-secondary text-lg text-center">{$t('payments.empty')}</p>
- {/each}
- </div>
- </div>
- </div>
+ <div class="text-secondary text-right text-sm my-auto">
+ <div>
+ {format(new Date(p.created), "h:mm aaa")}
+ </div>
+ <div>
+ {format(new Date(p.created), "MMM d")}
+ </div>
+ </div>
+ </div>
+ </a>
+ {:else}
+ <p class="text-secondary text-lg text-center">
+ {$t("payments.empty")}
+ </p>
+ {/each}
+ </div>
+ </div>
+ </div>
</div>
diff --git a/src/routes/(app)/fund/[id]/sweep/+page.server.js b/src/routes/(app)/fund/[id]/sweep/+page.server.js
index 9fd244a3..05fb18b6 100644
--- a/src/routes/(app)/fund/[id]/sweep/+page.server.js
+++ b/src/routes/(app)/fund/[id]/sweep/+page.server.js
@@ -1,14 +1,13 @@
-import { get, auth, post } from '$lib/utils';
-import { redirect } from '@sveltejs/kit';
+import { get, auth, post } from "$lib/utils";
+import { redirect } from "@sveltejs/kit";
export async function load({ cookies, params, parent }) {
- let { user } = await parent();
- let { id } = params;
- let { amount } = await get(`/fund/${id}`);
-
+ let { user } = await parent();
+ let { id } = params;
+ let { amount } = await get(`/fund/${id}`);
if (!amount) throw redirect(307, `/fund/${id}`);
- if (!user) throw redirect(307, `/register?redirect=/fund/${id}/sweep`);
- await post('/take', { amount, id }, auth(cookies));
- return { amount, id };
+ if (!user) throw redirect(307, `/register?redirect=/fund/${id}/sweep`);
+ await post("/take", { amount, id }, auth(cookies));
+ return { amount, id };
}
diff --git a/src/routes/(app)/fund/[id]/sweep/+page.svelte b/src/routes/(app)/fund/[id]/sweep/+page.svelte
index 64bed4dd..bbd7013f 100644
--- a/src/routes/(app)/fund/[id]/sweep/+page.svelte
+++ b/src/routes/(app)/fund/[id]/sweep/+page.svelte
@@ -1,37 +1,41 @@
<script>
- import { scale } from 'svelte/transition';
- import { fiat, f, s } from '$lib/utils';
- import { Icon } from '$comp';
- import { t } from '$lib/translations';
+ import { scale } from "svelte/transition";
+ import { fiat, f, s } from "$lib/utils";
+ import { Icon } from "$comp";
+ import { t } from "$lib/translations";
- export let data;
+ export let data;
- let { amount, id, user, rates } = data;
- let { currency } = user;
- $: rate = rates[currency];
+ let { amount, id, user, rates } = data;
+ let { currency } = user;
+ $: rate = rates[currency];
</script>
<div class="container px-4 max-w-xl mx-auto mt-10 space-y-5 text-center">
- <div class="w-full flex">
- <a href="/" class="mx-auto">
- <Icon icon="logo" />
- </a>
- </div>
+ <div class="w-full flex">
+ <a href="/" class="mx-auto">
+ <Icon icon="logo" />
+ </a>
+ </div>
- <div class="flex w-full py-5 max-w-[200px] mx-auto" in:scale={{ start: 0.5 }}>
- <Icon icon="check" style="mx-auto" />
- </div>
- <h1 class="text-3xl md:text-4xl font-bold mb-6">{$t('payments.fundsClaimed')}</h1>
- <h2 class="text-2xl md:text-3xl font-semibold">
- {f(fiat(amount, rate), currency)}
- </h2>
- <h3 class="text-secondary md:text-lg mb-6 mt-1">
- ⚡️{s(amount)}
- </h3>
+ <div class="flex w-full py-5 max-w-[200px] mx-auto" in:scale={{ start: 0.5 }}>
+ <Icon icon="check" style="mx-auto" />
+ </div>
+ <h1 class="text-3xl md:text-4xl font-bold mb-6">
+ {$t("payments.fundsClaimed")}
+ </h1>
+ <h2 class="text-2xl md:text-3xl font-semibold">
+ {f(fiat(amount, rate), currency)}
+ </h2>
+ <h3 class="text-secondary md:text-lg mb-6 mt-1">
+ ⚡️{s(amount)}
+ </h3>
</div>
<a href={`/${user.username}/payments`}>
- <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" />
+ <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" />
</a>
-<div class="fixed bottom-10 w-full text-center">{$t('payments.tapAnywhere')}</div>
+<div class="fixed bottom-10 w-full text-center">
+ {$t("payments.tapAnywhere")}
+</div>
diff --git a/src/routes/(app)/fund/[id]/withdraw/+page.server.js b/src/routes/(app)/fund/[id]/withdraw/+page.server.js
index 476149f2..6fb08f61 100644
--- a/src/routes/(app)/fund/[id]/withdraw/+page.server.js
+++ b/src/routes/(app)/fund/[id]/withdraw/+page.server.js
@@ -1,20 +1,20 @@
-import { fail, json, redirect } from '@sveltejs/kit';
-import { fd, auth, post } from '$lib/utils';
+import { fail, json, redirect } from "@sveltejs/kit";
+import { fd, auth, post } from "$lib/utils";
export async function load({ parent }) {
- let { user } = await parent();
- if (!user) throw redirect(307, '/register');
+ let { user } = await parent();
+ if (!user) throw redirect(307, "/register");
}
export const actions = {
- default: async ({ cookies, request }) => {
- let body = await fd(request);
- try {
- await post('/take', body, auth(cookies));
- } catch (e) {
- return fail(400, { message: e.message });
- }
+ default: async ({ cookies, request }) => {
+ let body = await fd(request);
+ try {
+ await post("/take", body, auth(cookies));
+ } catch (e) {
+ return fail(400, { message: e.message });
+ }
- throw redirect(307, `/fund/${body.id}`);
- }
+ throw redirect(307, `/fund/${body.id}`);
+ },
};
diff --git a/src/routes/(app)/fund/[id]/withdraw/+page.svelte b/src/routes/(app)/fund/[id]/withdraw/+page.svelte
index 9cad945a..6b2010b7 100644
--- a/src/routes/(app)/fund/[id]/withdraw/+page.svelte
+++ b/src/routes/(app)/fund/[id]/withdraw/+page.svelte
@@ -1,58 +1,58 @@
<script>
- import { t } from '$lib/translations';
- import { enhance } from '$app/forms';
- import { Icon, Numpad, Spinner } from '$comp';
- import { page } from '$app/stores';
- import { back } from '$lib/utils';
- import { pin } from '$lib/store';
+ import { t } from "$lib/translations";
+ import { enhance } from "$app/forms";
+ import { Icon, Numpad, Spinner } from "$comp";
+ import { page } from "$app/stores";
+ import { back } from "$lib/utils";
+ import { pin } from "$lib/store";
- export let data;
- export let form;
+ export let data;
+ export let form;
- let { rates } = data;
- let { amount, id } = $page.params;
- let { currency } = data.user;
- let loading;
+ let { rates } = data;
+ let { amount, id } = $page.params;
+ let { currency } = data.user;
+ let loading;
- let submit = () => (loading = true);
- $: update(form);
- let update = () => {
- if (form?.message.includes('pin')) $pin = undefined;
- loading = false;
- };
+ let submit = () => (loading = true);
+ $: update(form);
+ let update = () => {
+ if (form?.message.includes("pin")) $pin = undefined;
+ loading = false;
+ };
- $: rate = rates[currency];
+ $: rate = rates[currency];
</script>
<button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80" on:click={back}>
- <Icon icon="arrow-left" style="w-10" />
+ <Icon icon="arrow-left" style="w-10" />
</button>
{#if form?.message}
- <div class="text-red-600 text-center">
- {form.message}
- </div>
+ <div class="text-red-600 text-center">
+ {form.message}
+ </div>
{/if}
<div class="container px-4 mt-20 max-w-xl mx-auto">
- <Numpad bind:amount bind:currency bind:rate {submit} />
+ <Numpad bind:amount bind:currency bind:rate {submit} />
- <form method="POST" use:enhance on:submit={submit}>
- <input name="id" value={id} type="hidden" />
- <input name="amount" value={amount} type="hidden" />
- <input name="pin" value={$pin} type="hidden" />
+ <form method="POST" use:enhance on:submit={submit}>
+ <input name="id" value={id} type="hidden" />
+ <input name="amount" value={amount} type="hidden" />
+ <input name="pin" value={$pin} type="hidden" />
- <div class="flex w-full">
- <button
- type="submit"
- class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
- >
- {#if loading}
- <Spinner />
- {:else}
- {$t('payments.send')}
- {/if}
- </button>
- </div>
- </form>
+ <div class="flex w-full">
+ <button
+ type="submit"
+ class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
+ >
+ {#if loading}
+ <Spinner />
+ {:else}
+ {$t("payments.send")}
+ {/if}
+ </button>
+ </div>
+ </form>
</div>
diff --git a/src/routes/(app)/fund/[id]/withdraw/+server.js b/src/routes/(app)/fund/[id]/withdraw/+server.js
index 0f9a23f3..96d66413 100644
--- a/src/routes/(app)/fund/[id]/withdraw/+server.js
+++ b/src/routes/(app)/fund/[id]/withdraw/+server.js
@@ -1,6 +1,6 @@
-import { json } from '@sveltejs/kit';
-import { auth, post } from '$lib/utils';
+import { json } from "@sveltejs/kit";
+import { auth, post } from "$lib/utils";
export async function POST({ cookies, request }) {
- return json(await post('/take', await request.json(), auth(cookies)));
+ return json(await post("/take", await request.json(), auth(cookies)));
}
diff --git a/src/routes/(app)/launch/+page.server.js b/src/routes/(app)/launch/+page.server.js
index 2b44dfae..d11af500 100644
--- a/src/routes/(app)/launch/+page.server.js
+++ b/src/routes/(app)/launch/+page.server.js
@@ -1,6 +1,6 @@
-import { auth, get } from '$lib/utils';
+import { auth, get } from "$lib/utils";
export const load = async ({ parent }) => {
- let tickets = await get('/tickets');
- return { ...(await parent()), tickets };
+ let tickets = await get("/tickets");
+ return { ...(await parent()), tickets };
};
diff --git a/src/routes/(app)/launch/+page.svelte b/src/routes/(app)/launch/+page.svelte
index 3469f0bc..028274fa 100644
--- a/src/routes/(app)/launch/+page.svelte
+++ b/src/routes/(app)/launch/+page.svelte
@@ -1,103 +1,118 @@
<script>
- import { LandingHeader } from '$comp';
- import { loginRedirect } from '$lib/store';
- import { beforeNavigate } from '$app/navigation';
- import { info } from '$lib/utils';
- import { Icon } from '$comp';
- import { t } from '$lib/translations';
- import { goto } from '$app/navigation';
- import { scroll } from '$lib/utils';
+ import { LandingHeader } from "$comp";
+ import { loginRedirect } from "$lib/store";
+ import { beforeNavigate } from "$app/navigation";
+ import { info } from "$lib/utils";
+ import { Icon } from "$comp";
+ import { t } from "$lib/translations";
+ import { goto } from "$app/navigation";
+ import { scroll } from "$lib/utils";
- export let data;
- let { user, tickets } = data;
- $loginRedirect = '/launch/purchase';
+ export let data;
+ let { user, tickets } = data;
+ $loginRedirect = "/launch/purchase";
- let hasTicket = !!user?.accounts.find((a) => tickets.includes(a.asset));
- $: link = hasTicket ? '/launch/ticket' : user ? $loginRedirect : '/register';
- $: text = (hasTicket ? 'View' : 'Purchase') + ' Ticket';
+ let hasTicket = !!user?.accounts.find((a) => tickets.includes(a.asset));
+ $: link = hasTicket ? "/launch/ticket" : user ? $loginRedirect : "/register";
+ $: text = (hasTicket ? "View" : "Purchase") + " Ticket";
- beforeNavigate(({ to }) => {
- if (to?.routeId === 'register') info('Please sign in so we can get you a ticket');
- });
+ beforeNavigate(({ to }) => {
+ if (to?.routeId === "register")
+ info("Please sign in so we can get you a ticket");
+ });
- let showMobileMenu = false;
- const mobileMenuButtonClick = (section) => {
- showMobileMenu = false;
- scroll(section);
- };
+ let showMobileMenu = false;
+ const mobileMenuButtonClick = (section) => {
+ showMobileMenu = false;
+ scroll(section);
+ };
</script>
-<header class="w-full lg:w-5/6 py-5 mx-auto fixed md:sticky z-10 top-0 bg-white/90">
- <nav class="block xl:flex flex-wrap justify-between items-center">
- <div class="flex justify-start md:justify-center items-center md:space-x-10">
- <a href="/" class="hidden md:block">
- <Icon icon="logo" style="mb-5 xl:mb-0" />
- </a>
- <Icon icon="logo" style="block md:hidden ml-5 w-48" />
- </div>
- </nav>
+<header
+ class="w-full lg:w-5/6 py-5 mx-auto fixed md:sticky z-10 top-0 bg-white/90"
+>
+ <nav class="block xl:flex flex-wrap justify-between items-center">
+ <div
+ class="flex justify-start md:justify-center items-center md:space-x-10"
+ >
+ <a href="/" class="hidden md:block">
+ <Icon icon="logo" style="mb-5 xl:mb-0" />
+ </a>
+ <Icon icon="logo" style="block md:hidden ml-5 w-48" />
+ </div>
+ </nav>
</header>
<div class="mx-auto flex items-center justify-center px-8 mt-20">
- <div class="flex flex-col w-full bg-white rounded shadow-lg sm:w-3/4 lg:w-3/5">
- <div class="flex flex-col w-full md:flex-row">
- <div
- class="flex flex-row justify-around p-4 font-bold leading-none text-gray-800 uppercase bg-gray-400 rounded md:flex-col md:items-center md:justify-center md:w-1/4"
- >
- <div class="md:text-3xl">Oct</div>
- <div class="md:text-6xl">26</div>
- <div class="md:text-xl">6 pm</div>
- </div>
- <div class="p-4 font-normal text-gray-800 md:w-3/4 space-y-5">
- <h1 class="mb-4 text-4xl font-bold leading-none tracking-tight text-gray-800">
- Launch Party!
- </h1>
+ <div
+ class="flex flex-col w-full bg-white rounded shadow-lg sm:w-3/4 lg:w-3/5"
+ >
+ <div class="flex flex-col w-full md:flex-row">
+ <div
+ class="flex flex-row justify-around p-4 font-bold leading-none text-gray-800 uppercase bg-gray-400 rounded md:flex-col md:items-center md:justify-center md:w-1/4"
+ >
+ <div class="md:text-3xl">Oct</div>
+ <div class="md:text-6xl">26</div>
+ <div class="md:text-xl">6 pm</div>
+ </div>
+ <div class="p-4 font-normal text-gray-800 md:w-3/4 space-y-5">
+ <h1
+ class="mb-4 text-4xl font-bold leading-none tracking-tight text-gray-800"
+ >
+ Launch Party!
+ </h1>
- <p class="leading-normal mb-2">
- Come join us at our launch party celebrating our new design and 10 years serving the
- Vancouver Bitcoin community!
- </p>
+ <p class="leading-normal mb-2">
+ Come join us at our launch party celebrating our new design and 10
+ years serving the Vancouver Bitcoin community!
+ </p>
- <div class="flex">
- <div class="space-y-5">
- <div class="ml-8">
- <p><b>When</b></p>
- <p>6pm October 26</p>
- </div>
- <div class="ml-8">
- <p><b>Where</b></p>
- <p>The Loft Lounge</p>
- <p>1184 Denman Street, #203</p>
- <p>Vancouver, BC</p>
- </div>
- </div>
+ <div class="flex">
+ <div class="space-y-5">
+ <div class="ml-8">
+ <p><b>When</b></p>
+ <p>6pm October 26</p>
+ </div>
+ <div class="ml-8">
+ <p><b>Where</b></p>
+ <p>The Loft Lounge</p>
+ <p>1184 Denman Street, #203</p>
+ <p>Vancouver, BC</p>
+ </div>
+ </div>
- <img src="/icons/party.svg" alt="Partygoers" class="w-[120px] mx-auto" />
- </div>
+ <img
+ src="/icons/party.svg"
+ alt="Partygoers"
+ class="w-[120px] mx-auto"
+ />
+ </div>
- <p>
- We'll introduce our new interface and then network and enjoy tasty food and drinks from <a
- href="https://loftondenman.com/"
- class="underline">The Loft</a
- >!
- </p>
+ <p>
+ We'll introduce our new interface and then network and enjoy tasty
+ food and drinks from <a
+ href="https://loftondenman.com/"
+ class="underline">The Loft</a
+ >!
+ </p>
- <p>
- The Loft will be accepting Lightning payments at the event and going forward into the
- future as well! We're charging $6 for for an NFT ticket that gets you entry, appies, and
- the chance to win some swag and sats.
- </p>
+ <p>
+ The Loft will be accepting Lightning payments at the event and going
+ forward into the future as well! We're charging $6 for for an NFT
+ ticket that gets you entry, appies, and the chance to win some swag
+ and sats.
+ </p>
- <div class="flex w-full my-8">
- <a href={link} class="mx-auto">
- <button
- class="bg-white text-black border rounded-full px-8 py-4 font-bold hover:opacity-80 text-2xl"
- >
- {text}
- </button>
- </a>
- </div>
- </div>
- </div>
- </div>
+ <div class="flex w-full my-8">
+ <a href={link} class="mx-auto">
+ <button
+ class="bg-white text-black border rounded-full px-8 py-4 font-bold hover:opacity-80 text-2xl"
+ >
+ {text}
+ </button>
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
</div>
diff --git a/src/routes/(app)/launch/purchase/+page.server.js b/src/routes/(app)/launch/purchase/+page.server.js
index a3043d9c..58669606 100644
--- a/src/routes/(app)/launch/purchase/+page.server.js
+++ b/src/routes/(app)/launch/purchase/+page.server.js
@@ -1,45 +1,49 @@
-import { auth, get, post } from '$lib/utils';
-import tickets from '$lib/tickets';
-import { redirect } from '@sveltejs/kit';
-import { env } from '$env/dynamic/private';
-import { invalidateAll } from '$app/navigation';
-import { browser } from '$app/environment';
-import { createTicket } from '$lib/ticket';
+import { auth, get, post } from "$lib/utils";
+import tickets from "$lib/tickets";
+import { redirect } from "@sveltejs/kit";
+import { env } from "$env/dynamic/private";
+import { invalidateAll } from "$app/navigation";
+import { browser } from "$app/environment";
+import { createTicket } from "$lib/ticket";
export const load = async ({ parent }) => {
- let tickets = await get('/tickets');
- let { user } = await parent();
- let hasTicket = !!user?.accounts.find((a) => tickets.includes(a.asset));
- if (hasTicket) throw redirect(307, '/launch/ticket');
- return { tickets };
+ let tickets = await get("/tickets");
+ let { user } = await parent();
+ let hasTicket = !!user?.accounts.find((a) => tickets.includes(a.asset));
+ if (hasTicket) throw redirect(307, "/launch/ticket");
+ return { tickets };
};
export const actions = {
- internal: async ({ cookies, request }) => {
- let form = await request.formData();
- let asset = await createTicket(cookies, form.get('username'));
- await post('/payments', { amount: 22000, username: 'launch' }, auth(cookies));
+ internal: async ({ cookies, request }) => {
+ let form = await request.formData();
+ let asset = await createTicket(cookies, form.get("username"));
+ await post(
+ "/payments",
+ { amount: 22000, username: "launch" },
+ auth(cookies)
+ );
- throw redirect(307, `/launch/thanks/${asset}`);
- },
+ throw redirect(307, `/launch/thanks/${asset}`);
+ },
- lightning: async ({ cookies, request }) => {
- let form = await request.formData();
+ lightning: async ({ cookies, request }) => {
+ let form = await request.formData();
- let rates = await get('/rates');
+ let rates = await get("/rates");
- let invoice = {
- amount: 22000,
- memo: 'launch',
- type: 'lightning',
- prompt: false,
- rate: rates[form.get('currency')]
- };
+ let invoice = {
+ amount: 22000,
+ memo: "launch",
+ type: "lightning",
+ prompt: false,
+ rate: rates[form.get("currency")],
+ };
- let user = { username: form.get('username') };
+ let user = { username: form.get("username") };
- let { id } = await post('/invoice', { invoice, user }, auth(cookies));
+ let { id } = await post("/invoice", { invoice, user }, auth(cookies));
- throw redirect(307, `/${user.username}/invoice/${id}`);
- }
+ throw redirect(307, `/${user.username}/invoice/${id}`);
+ },
};
diff --git a/src/routes/(app)/launch/purchase/+page.svelte b/src/routes/(app)/launch/purchase/+page.svelte
index fbaa58c0..49a51973 100644
--- a/src/routes/(app)/launch/purchase/+page.svelte
+++ b/src/routes/(app)/launch/purchase/+page.svelte
@@ -1,66 +1,74 @@
<script>
- import { onMount } from 'svelte';
- import { enhance } from '$app/forms';
- import { post } from '$lib/utils';
- import { goto } from '$app/navigation';
+ import { onMount } from "svelte";
+ import { enhance } from "$app/forms";
+ import { post } from "$lib/utils";
+ import { goto } from "$app/navigation";
- export let data;
+ export let data;
- let amount = 22000;
- let {
- currency,
- account: { balance },
- username
- } = data.user;
+ let amount = 22000;
+ let {
+ currency,
+ account: { balance },
+ username,
+ } = data.user;
- let { tickets } = data;
- let ticket = tickets.length;
+ let { tickets } = data;
+ let ticket = tickets.length;
</script>
<div class="mx-auto h-screen flex items-center justify-center px-4">
- <div class="font-normal text-gray-800 mx-auto mb-8 space-y-8">
- <h1 class="mb-4 text-4xl font-bold leading-none tracking-tight text-gray-800 text-center">
- Purchase Ticket
- </h1>
+ <div class="font-normal text-gray-800 mx-auto mb-8 space-y-8">
+ <h1
+ class="mb-4 text-4xl font-bold leading-none tracking-tight text-gray-800 text-center"
+ >
+ Purchase Ticket
+ </h1>
- <img
- src={`/tickets/${(ticket + 1).toString().padStart(2, '0')}.png`}
- class="w-full max-w-[800px]"
- alt="Ticket"
- />
+ <img
+ src={`/tickets/${(ticket + 1).toString().padStart(2, "0")}.png`}
+ class="w-full max-w-[800px]"
+ alt="Ticket"
+ />
- {#if balance >= amount}
- <form action="?/internal" method="POST" use:enhance>
- <input type="hidden" name="username" value={username} />
+ {#if balance >= amount}
+ <form action="?/internal" method="POST" use:enhance>
+ <input type="hidden" name="username" value={username} />
- <div class="w-full flex">
- <button
- class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 mx-auto text-2xl"
- >
- Count me in
- </button>
- </div>
- </form>
- {:else}
- <h1 class="mb-4 text-xl font-bold leading-none tracking-tight text-gray-800 text-center">
- Payment Options
- </h1>
+ <div class="w-full flex">
+ <button
+ class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 mx-auto text-2xl"
+ >
+ Count me in
+ </button>
+ </div>
+ </form>
+ {:else}
+ <h1
+ class="mb-4 text-xl font-bold leading-none tracking-tight text-gray-800 text-center"
+ >
+ Payment Options
+ </h1>
- <div class="flex justify-center gap-4">
- <form action="?/lightning" method="POST">
- <input type="hidden" name="currency" value={currency} />
- <input type="hidden" name="username" value={username} />
- <button class="border rounded-full px-8 py-4 font-bold hover:opacity-80 mx-auto text-2xl">
- Lightning
- </button>
- </form>
+ <div class="flex justify-center gap-4">
+ <form action="?/lightning" method="POST">
+ <input type="hidden" name="currency" value={currency} />
+ <input type="hidden" name="username" value={username} />
+ <button
+ class="border rounded-full px-8 py-4 font-bold hover:opacity-80 mx-auto text-2xl"
+ >
+ Lightning
+ </button>
+ </form>
- <a href={`purchase/card`}>
- <button class="border rounded-full px-8 py-4 font-bold hover:opacity-80 mx-auto text-2xl">
- Credit Card
- </button>
- </a>
- </div>
- {/if}
- </div>
+ <a href={`purchase/card`}>
+ <button
+ class="border rounded-full px-8 py-4 font-bold hover:opacity-80 mx-auto text-2xl"
+ >
+ Credit Card
+ </button>
+ </a>
+ </div>
+ {/if}
+ </div>
</div>
diff --git a/src/routes/(app)/launch/purchase/card/+page.server.js b/src/routes/(app)/launch/purchase/card/+page.server.js
index 1f564f78..bca601d3 100644
--- a/src/routes/(app)/launch/purchase/card/+page.server.js
+++ b/src/routes/(app)/launch/purchase/card/+page.server.js
@@ -1,44 +1,52 @@
-import { env } from '$env/dynamic/private';
-import { error, redirect } from '@sveltejs/kit';
-import { post, auth } from '$lib/utils';
-import { createTicket } from '$lib/ticket';
+import { env } from "$env/dynamic/private";
+import { error, redirect } from "@sveltejs/kit";
+import { post, auth } from "$lib/utils";
+import { createTicket } from "$lib/ticket";
-let stripe = 'https://api.stripe.com/v1';
+let stripe = "https://api.stripe.com/v1";
export const actions = {
- default: async ({ cookies, request }) => {
- let form = await request.formData();
+ default: async ({ cookies, request }) => {
+ let form = await request.formData();
- let headers = new Headers();
- headers.set('Authorization', 'Basic ' + new Buffer(env.STRIPE + ':').toString('base64'));
+ let headers = new Headers();
+ headers.set(
+ "Authorization",
+ "Basic " + new Buffer(env.STRIPE + ":").toString("base64")
+ );
- let body = new URLSearchParams({
- 'card[number]': form.get('number').replace(/\s+/g, ''),
- 'card[exp_month]': form.get('expiry').slice(0, 2),
- 'card[exp_year]': form.get('expiry').slice(-2),
- 'card[cvc]': form.get('cvc')
- });
+ let body = new URLSearchParams({
+ "card[number]": form.get("number").replace(/\s+/g, ""),
+ "card[exp_month]": form.get("expiry").slice(0, 2),
+ "card[exp_year]": form.get("expiry").slice(-2),
+ "card[cvc]": form.get("cvc"),
+ });
- let method = 'POST';
+ let method = "POST";
- let { id: source } = await fetch(`${stripe}/tokens`, { body, headers, method }).then((r) =>
- r.json()
- );
+ let { id: source } = await fetch(`${stripe}/tokens`, {
+ body,
+ headers,
+ method,
+ }).then((r) => r.json());
- body = new URLSearchParams({
- amount: 600,
- currency: 'cad',
- source,
- description: 'launch ticket'
- });
+ body = new URLSearchParams({
+ amount: 600,
+ currency: "cad",
+ source,
+ description: "launch ticket",
+ });
- let { status } = await fetch(`${stripe}/charges`, { body, headers, method }).then((r) =>
- r.json()
- );
+ let { status } = await fetch(`${stripe}/charges`, {
+ body,
+ headers,
+ method,
+ }).then((r) => r.json());
- if (status !== 'succeeded') throw error(500, { message: 'Card payment failed' });
- let asset = await createTicket(cookies, form.get('username'));
+ if (status !== "succeeded")
+ throw error(500, { message: "Card payment failed" });
+ let asset = await createTicket(cookies, form.get("username"));
- throw redirect(307, `/launch/thanks/${asset}`);
- }
+ throw redirect(307, `/launch/thanks/${asset}`);
+ },
};
diff --git a/src/routes/(app)/launch/purchase/card/+page.svelte b/src/routes/(app)/launch/purchase/card/+page.svelte
index 05d09025..037a65b2 100644
--- a/src/routes/(app)/launch/purchase/card/+page.svelte
+++ b/src/routes/(app)/launch/purchase/card/+page.svelte
@@ -1,77 +1,77 @@
<script>
- import { enhance } from '$app/forms';
- import { Lock, Card, Calendar, Spinner } from '$comp';
+ import { enhance } from "$app/forms";
+ import { Lock, Card, Calendar, Spinner } from "$comp";
- export let data;
+ export let data;
- let { username } = data.user;
- let loading;
+ let { username } = data.user;
+ let loading;
</script>
<div class="container max-w-lg px-4 mx-auto space-y-5">
- <form
- method="POST"
- class="flex flex-wrap gap-5 w-full p-5 mt-20"
- use:enhance={() => {
- loading = true;
- return ({ update }) => update() && (loading = false);
- }}
- >
- <input type="hidden" name="username" value={username} />
- <label class="relative w-full flex flex-col">
- <span class="font-bold mb-3">Card number</span>
- <input
- class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
- type="text"
- name="number"
- placeholder="0000 0000 0000 0000"
- />
- <Card />
- </label>
+ <form
+ method="POST"
+ class="flex flex-wrap gap-5 w-full p-5 mt-20"
+ use:enhance={() => {
+ loading = true;
+ return ({ update }) => update() && (loading = false);
+ }}
+ >
+ <input type="hidden" name="username" value={username} />
+ <label class="relative w-full flex flex-col">
+ <span class="font-bold mb-3">Card number</span>
+ <input
+ class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
+ type="text"
+ name="number"
+ placeholder="0000 0000 0000 0000"
+ />
+ <Card />
+ </label>
- <label class="relative flex-1 flex flex-col">
- <span class="font-bold mb-3">Expire date</span>
- <input
- class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
- type="text"
- name="expiry"
- placeholder="MM/YY"
- />
- <Calendar />
- </label>
+ <label class="relative flex-1 flex flex-col">
+ <span class="font-bold mb-3">Expire date</span>
+ <input
+ class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
+ type="text"
+ name="expiry"
+ placeholder="MM/YY"
+ />
+ <Calendar />
+ </label>
- <label class="relative flex-1 flex flex-col">
- <span class="font-bold flex items-center gap-3 mb-3">
- CVC/CVV
- <span class="relative group">
- <span
- class="hidden group-hover:flex justify-center items-center px-2 py-1 text-xs absolute -right-2 transform translate-x-full -translate-y-1/2 w-max top-1/2 bg-black text-white"
- >
- 3-digit number on the back of your card</span
- >
- </span>
- </span>
- <input
- class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
- type="text"
- name="cvc"
- placeholder="&bull;&bull;&bull;"
- />
- <Lock />
- </label>
+ <label class="relative flex-1 flex flex-col">
+ <span class="font-bold flex items-center gap-3 mb-3">
+ CVC/CVV
+ <span class="relative group">
+ <span
+ class="hidden group-hover:flex justify-center items-center px-2 py-1 text-xs absolute -right-2 transform translate-x-full -translate-y-1/2 w-max top-1/2 bg-black text-white"
+ >
+ 3-digit number on the back of your card</span
+ >
+ </span>
+ </span>
+ <input
+ class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300"
+ type="text"
+ name="cvc"
+ placeholder="&bull;&bull;&bull;"
+ />
+ <Lock />
+ </label>
- <div class="w-full flex mt-8">
- <button
- class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 mx-auto text-xl"
- type="submit"
- disabled={loading}
- >
- {#if loading}
- <Spinner />
- {:else}
- Submit
- {/if}
- </button>
- </div>
- </form>
+ <div class="w-full flex mt-8">
+ <button
+ class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 mx-auto text-xl"
+ type="submit"
+ disabled={loading}
+ >
+ {#if loading}
+ <Spinner />
+ {:else}
+ Submit
+ {/if}
+ </button>
+ </div>
+ </form>
</div>
diff --git a/src/routes/(app)/launch/thanks/[asset]/+page.svelte b/src/routes/(app)/launch/thanks/[asset]/+page.svelte
index 43f10057..9b37002b 100644
--- a/src/routes/(app)/launch/thanks/[asset]/+page.svelte
+++ b/src/routes/(app)/launch/thanks/[asset]/+page.svelte
@@ -1,29 +1,31 @@
<script>
- import { page } from '$app/stores';
+ import { page } from "$app/stores";
- export let data;
+ export let data;
- let { user } = data;
+ let { user } = data;
- let { asset } = $page.params;
+ let { asset } = $page.params;
</script>
<div class="container mx-auto max-w-lg px-4 mt-20">
- <div class="font-normal text-gray-800 mx-auto mb-8 space-y-8">
- <h1 class="mb-4 text-4xl font-bold leading-none tracking-tight text-gray-800 text-center">
- Thanks!
- </h1>
+ <div class="font-normal text-gray-800 mx-auto mb-8 space-y-8">
+ <h1
+ class="mb-4 text-4xl font-bold leading-none tracking-tight text-gray-800 text-center"
+ >
+ Thanks!
+ </h1>
- <p>We look forward to seeing you!</p>
- </div>
+ <p>We look forward to seeing you!</p>
+ </div>
- <div class="w-full flex">
- <a href="/launch/ticket" class="mx-auto">
- <button
- class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 text-2xl"
- >
- Done
- </button>
- </a>
- </div>
+ <div class="w-full flex">
+ <a href="/launch/ticket" class="mx-auto">
+ <button
+ class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 text-2xl"
+ >
+ Done
+ </button>
+ </a>
+ </div>
</div>
diff --git a/src/routes/(app)/launch/ticket/+page.server.js b/src/routes/(app)/launch/ticket/+page.server.js
index 0362eb7d..7b51e679 100644
--- a/src/routes/(app)/launch/ticket/+page.server.js
+++ b/src/routes/(app)/launch/ticket/+page.server.js
@@ -1,9 +1,11 @@
-import { get } from '$lib/utils';
+import { get } from "$lib/utils";
export const load = async ({ cookies, parent }) => {
- let { user } = await parent();
- let tickets = await get('/tickets');
- let ticket = tickets.findIndex((t) => user.accounts.find((a) => a.asset === t));
- let asset = tickets[ticket];
- return { asset, ticket };
+ let { user } = await parent();
+ let tickets = await get("/tickets");
+ let ticket = tickets.findIndex((t) =>
+ user.accounts.find((a) => a.asset === t)
+ );
+ let asset = tickets[ticket];
+ return { asset, ticket };
};
diff --git a/src/routes/(app)/launch/ticket/+page.svelte b/src/routes/(app)/launch/ticket/+page.svelte
index 3879abcd..6b16e804 100644
--- a/src/routes/(app)/launch/ticket/+page.svelte
+++ b/src/routes/(app)/launch/ticket/+page.svelte
@@ -1,35 +1,37 @@
<script>
- export let data;
- let { asset, ticket } = data;
+ export let data;
+ let { asset, ticket } = data;
</script>
<div class="mx-auto h-screen flex items-center justify-center px-4">
- <div class="font-normal text-gray-800 mx-auto mb-8 space-y-8">
- <h1 class="mb-4 text-4xl font-bold leading-none tracking-tight text-gray-800 text-center">
- Your Ticket
- </h1>
+ <div class="font-normal text-gray-800 mx-auto mb-8 space-y-8">
+ <h1
+ class="mb-4 text-4xl font-bold leading-none tracking-tight text-gray-800 text-center"
+ >
+ Your Ticket
+ </h1>
- <p>
- We've minted your ticket as an <a
- href={`https://blockstream.info/liquid/asset/${asset}`}
- class="underline">NFT on the Liquid Blockchain</a
- > as a proof of purchase and commemorative token.
- </p>
+ <p>
+ We've minted your ticket as an <a
+ href={`https://blockstream.info/liquid/asset/${asset}`}
+ class="underline">NFT on the Liquid Blockchain</a
+ > as a proof of purchase and commemorative token.
+ </p>
- <img
- src={`/tickets/${(ticket + 1).toString().padStart(2, '0')}.png`}
- class="w-full max-w-[800px]"
- alt="Ticket"
- />
+ <img
+ src={`/tickets/${(ticket + 1).toString().padStart(2, "0")}.png`}
+ class="w-full max-w-[800px]"
+ alt="Ticket"
+ />
- <div class="w-full flex">
- <a href="/" class="mx-auto">
- <button
- class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 text-2xl"
- >
- Done
- </button>
- </a>
- </div>
- </div>
+ <div class="w-full flex">
+ <a href="/" class="mx-auto">
+ <button
+ class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 text-2xl"
+ >
+ Done
+ </button>
+ </a>
+ </div>
+ </div>
</div>
diff --git a/src/routes/(app)/ln/[lnurl]/+page.server.js b/src/routes/(app)/ln/[lnurl]/+page.server.js
index ddd714bf..332bc9e3 100644
--- a/src/routes/(app)/ln/[lnurl]/+page.server.js
+++ b/src/routes/(app)/ln/[lnurl]/+page.server.js
@@ -1,63 +1,75 @@
-import { fd, auth, get, post } from '$lib/utils';
-import { error, fail, redirect } from '@sveltejs/kit';
+import { fd, auth, get, post } from "$lib/utils";
+import { error, fail, redirect } from "@sveltejs/kit";
export async function load({ params, parent }) {
- let { user } = await parent();
- if (!user) throw redirect(307, '/register');
+ let { user } = await parent();
+ if (!user) throw redirect(307, "/register");
- let data;
+ let data;
- try {
- let { lnurl } = params;
- data = await get(`/decode?text=${lnurl}`);
- } catch (e) {
- throw error(500, e.message);
- }
+ try {
+ let { lnurl } = params;
+ data = await get(`/decode?text=${lnurl}`);
+ } catch (e) {
+ throw error(500, e.message);
+ }
- if (!['payRequest', 'withdrawRequest'].includes(data.tag))
- throw error(500, 'We only support LNURLp and LNURLw at this time');
+ if (!["payRequest", "withdrawRequest"].includes(data.tag))
+ throw error(500, "We only support LNURLp and LNURLw at this time");
- return data;
+ return data;
}
export const actions = {
- pay: async ({ cookies, fetch, request }) => {
- let error;
+ pay: async ({ cookies, fetch, request }) => {
+ let error;
- let { callback, amount, minSendable, maxSendable } = await fd(request);
- minSendable = Math.round(minSendable / 1000);
- maxSendable = Math.round(maxSendable / 1000);
+ let { callback, amount, minSendable, maxSendable } = await fd(request);
+ minSendable = Math.round(minSendable / 1000);
+ maxSendable = Math.round(maxSendable / 1000);
- if (amount < minSendable) error = `Amount must be at least ${minSendable} sats`;
- if (amount > maxSendable) error = `Amount must be at most ${maxSendable} sats`;
- if (error) return fail(400, { error });
+ if (amount < minSendable)
+ error = `Amount must be at least ${minSendable} sats`;
+ if (amount > maxSendable)
+ error = `Amount must be at most ${maxSendable} sats`;
+ if (error) return fail(400, { error });
- let { pr } = await fetch(`${callback}?amount=${amount * 1000}`).then((r) => r.json());
- throw redirect(307, `/send/lightning/${pr}`);
- },
+ let { pr } = await fetch(`${callback}?amount=${amount * 1000}`).then((r) =>
+ r.json()
+ );
+ throw redirect(307, `/send/lightning/${pr}`);
+ },
- withdraw: async ({ cookies, fetch, request }) => {
- let error;
+ withdraw: async ({ cookies, fetch, request }) => {
+ let error;
- let { callback, amount, username, currency, minWithdrawable, maxWithdrawable, k1 } = await fd(
- request
- );
+ let {
+ callback,
+ amount,
+ username,
+ currency,
+ minWithdrawable,
+ maxWithdrawable,
+ k1,
+ } = await fd(request);
- minWithdrawable = Math.round(minWithdrawable / 1000);
- maxWithdrawable = Math.round(maxWithdrawable / 1000);
+ minWithdrawable = Math.round(minWithdrawable / 1000);
+ maxWithdrawable = Math.round(maxWithdrawable / 1000);
- if (amount < minWithdrawable) error = `Amount must be at least ${minWithdrawable} sats`;
- if (amount > maxWithdrawable) error = `Amount must be at most ${maxWithdrawable} sats`;
- if (error) return fail(400, { error });
+ if (amount < minWithdrawable)
+ error = `Amount must be at least ${minWithdrawable} sats`;
+ if (amount > maxWithdrawable)
+ error = `Amount must be at most ${maxWithdrawable} sats`;
+ if (error) return fail(400, { error });
- let invoice = { amount, type: 'lightning' };
- let user = { username, currency };
+ let invoice = { amount, type: "lightning" };
+ let user = { username, currency };
- let { text: pr } = await post('/invoice', { invoice, user }, auth(cookies));
+ let { text: pr } = await post("/invoice", { invoice, user }, auth(cookies));
- let c = callback.includes('?') ? '&' : '?';
- await fetch(`${callback}${c}k1=${k1}&pr=${pr}`).then((r) => r.json());
+ let c = callback.includes("?") ? "&" : "?";
+ await fetch(`${callback}${c}k1=${k1}&pr=${pr}`).then((r) => r.json());
- throw redirect(307, `/ln/withdrawal/sent`);
- }
+ throw redirect(307, `/ln/withdrawal/sent`);
+ },
};
diff --git a/src/routes/(app)/ln/[lnurl]/+page.svelte b/src/routes/(app)/ln/[lnurl]/+page.svelte
index 7656c9fb..9b733f89 100644
--- a/src/routes/(app)/ln/[lnurl]/+page.svelte
+++ b/src/routes/(app)/ln/[lnurl]/+page.svelte
@@ -1,11 +1,11 @@
<script>
- import { Lnurlp, Lnurlw } from '$comp';
- export let data;
- let { tag } = data;
+ import { Lnurlp, Lnurlw } from "$comp";
+ export let data;
+ let { tag } = data;
</script>
-{#if tag === 'payRequest'}
- <Lnurlp {data} />
-{:else if tag === 'withdrawRequest'}
- <Lnurlw {data} />
+{#if tag === "payRequest"}
+ <Lnurlp {data} />
+{:else if tag === "withdrawRequest"}
+ <Lnurlw {data} />
{/if}
diff --git a/src/routes/(app)/ln/withdrawal/sent/+page.svelte b/src/routes/(app)/ln/withdrawal/sent/+page.svelte
index 84e6a7e0..c527c017 100644
--- a/src/routes/(app)/ln/withdrawal/sent/+page.svelte
+++ b/src/routes/(app)/ln/withdrawal/sent/+page.svelte
@@ -1,23 +1,28 @@
<script>
- import { scale } from 'svelte/transition';
- import { Icon } from '$comp';
- import { toast } from '@zerodevx/svelte-toast';
- import { t } from '$lib/translations';
+ import { scale } from "svelte/transition";
+ import { Icon } from "$comp";
+ import { toast } from "@zerodevx/svelte-toast";
+ import { t } from "$lib/translations";
- export let data;
- let { user } = data;
- toast.pop(0);
+ export let data;
+ let { user } = data;
+ toast.pop(0);
</script>
<div class="text-center mt-20 md:mt-0">
- <div class="flex w-full max-w-[200px] mx-auto py-20" in:scale={{ start: 0.5 }}>
- <Icon icon="check" style="mx-auto" />
- </div>
- <h1 class="text-3xl md:text-4xl font-bold mb-6">Request Sent!</h1>
+ <div
+ class="flex w-full max-w-[200px] mx-auto py-20"
+ in:scale={{ start: 0.5 }}
+ >
+ <Icon icon="check" style="mx-auto" />
+ </div>
+ <h1 class="text-3xl md:text-4xl font-bold mb-6">Request Sent!</h1>
</div>
<a href={`/${user.username}/payments`}>
- <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" />
+ <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" />
</a>
-<div class="fixed bottom-10 w-full text-center">{$t('payments.tapAnywhere')}</div>
+<div class="fixed bottom-10 w-full text-center">
+ {$t("payments.tapAnywhere")}
+</div>
diff --git a/src/routes/(app)/locations/+server.js b/src/routes/(app)/locations/+server.js
index dd9e984b..8a046475 100644
--- a/src/routes/(app)/locations/+server.js
+++ b/src/routes/(app)/locations/+server.js
@@ -1,15 +1,15 @@
-import { get } from '$lib/utils';
-import { json } from '@sveltejs/kit';
+import { get } from "$lib/utils";
+import { json } from "@sveltejs/kit";
export async function GET({ fetch, setHeaders }) {
- let locations = [];
+ let locations = [];
- try {
- ({ locations } = await get('/locations'));
- setHeaders({ 'cache-control': 'public, max-age=600' });
- } catch (e) {
- console.log('failed to fetch locations');
- }
+ try {
+ ({ locations } = await get("/locations"));
+ setHeaders({ "cache-control": "public, max-age=600" });
+ } catch (e) {
+ console.log("failed to fetch locations");
+ }
- return json({ locations });
+ return json({ locations });
}
diff --git a/src/routes/(app)/login/+page.server.js b/src/routes/(app)/login/+page.server.js
index a9cf867b..6876c93d 100644
--- a/src/routes/(app)/login/+page.server.js
+++ b/src/routes/(app)/login/+page.server.js
@@ -1,25 +1,29 @@
-import { fail, redirect } from '@sveltejs/kit';
-import { login, fd } from '$lib/utils';
+import { fail, redirect } from "@sveltejs/kit";
+import { login, fd } from "$lib/utils";
export const load = async ({ parent }) => {
- let { user } = await parent();
- if (user?.pubkey) throw redirect(307, `/${user.username}`);
+ let { user } = await parent();
+ if (user?.pubkey) throw redirect(307, `/${user.username}`);
};
export const actions = {
- default: async ({ cookies, request }) => {
- let form = await fd(request);
- let { username, password, token, loginRedirect } = form;
- let user = { username, password, token };
+ default: async ({ cookies, request }) => {
+ let form = await fd(request);
+ let { username, password, token, loginRedirect } = form;
+ let user = { username, password, token };
- if (loginRedirect === 'undefined') loginRedirect = undefined;
+ if (loginRedirect === "undefined") loginRedirect = undefined;
- try {
- let r = await login(user, cookies, request.headers.get('cf-connecting-ip'));
- } catch (e) {
- return fail(400, { error: 'Login failed', message: e.message, ...form });
- }
+ try {
+ let r = await login(
+ user,
+ cookies,
+ request.headers.get("cf-connecting-ip")
+ );
+ } catch (e) {
+ return fail(400, { error: "Login failed", message: e.message, ...form });
+ }
- throw redirect(303, loginRedirect || `/${user.username}`);
- }
+ throw redirect(303, loginRedirect || `/${user.username}`);
+ },
};
diff --git a/src/routes/(app)/login/+page.svelte b/src/routes/(app)/login/+page.svelte
index 84ecc3ad..6190ae0d 100644
--- a/src/routes/(app)/login/+page.svelte
+++ b/src/routes/(app)/login/+page.svelte
@@ -1,16 +1,16 @@
<script>
- import { RegisterOrLogin } from '$comp';
- import { onMount } from 'svelte';
- import { animatedRate, pin } from '$lib/store';
+ import { RegisterOrLogin } from "$comp";
+ import { onMount } from "svelte";
+ import { animatedRate, pin } from "$lib/store";
- onMount(() => {
- $animatedRate = 0;
- $pin = undefined;
- localStorage.clear();
- sessionStorage.clear();
- });
+ onMount(() => {
+ $animatedRate = 0;
+ $pin = undefined;
+ localStorage.clear();
+ sessionStorage.clear();
+ });
- export let form;
+ export let form;
</script>
<RegisterOrLogin pageID="signIn" bind:form />
diff --git a/src/routes/(app)/logout/+page.server.js b/src/routes/(app)/logout/+page.server.js
index 2dabc9d4..60750dfa 100644
--- a/src/routes/(app)/logout/+page.server.js
+++ b/src/routes/(app)/logout/+page.server.js
@@ -1,12 +1,12 @@
-import { redirect } from '@sveltejs/kit';
+import { redirect } from "@sveltejs/kit";
const opts = {
- expires: new Date(0),
- path: '/'
+ expires: new Date(0),
+ path: "/",
};
export async function load({ cookies }) {
- cookies.set('username', '', opts);
- cookies.set('token', '', opts);
- throw redirect(307, '/login');
+ cookies.set("username", "", opts);
+ cookies.set("token", "", opts);
+ throw redirect(307, "/login");
}
diff --git a/src/routes/(app)/otpsecret/+server.js b/src/routes/(app)/otpsecret/+server.js
index 7d5c1ad9..63d06849 100644
--- a/src/routes/(app)/otpsecret/+server.js
+++ b/src/routes/(app)/otpsecret/+server.js
@@ -1,15 +1,17 @@
-import { auth, post } from '$lib/utils';
-import Qr from 'qrcode-base64';
-import { error } from '@sveltejs/kit';
+import { auth, post } from "$lib/utils";
+import Qr from "qrcode-base64";
+import { error } from "@sveltejs/kit";
export async function POST({ cookies, request }) {
- try {
- let body = await request.json();
- let { username, secret } = await post('/otpsecret', body, auth(cookies));
- let uri = `otpauth://totp/coinos:${username}?secret=${secret}&period=30&digits=6&algorithm=SHA1&issuer=coinos`;
- return new Response(JSON.stringify({ secret, uri, qr: Qr.drawImg(uri, { size: 600 }) }));
- } catch (e) {
- if (e.message.includes('pin')) throw e;
- throw new error(500, 'Problem saving secret');
- }
+ try {
+ let body = await request.json();
+ let { username, secret } = await post("/otpsecret", body, auth(cookies));
+ let uri = `otpauth://totp/coinos:${username}?secret=${secret}&period=30&digits=6&algorithm=SHA1&issuer=coinos`;
+ return new Response(
+ JSON.stringify({ secret, uri, qr: Qr.drawImg(uri, { size: 600 }) })
+ );
+ } catch (e) {
+ if (e.message.includes("pin")) throw e;
+ throw new error(500, "Problem saving secret");
+ }
}
diff --git a/src/routes/(app)/password/+server.js b/src/routes/(app)/password/+server.js
index b4a81740..26ed3858 100644
--- a/src/routes/(app)/password/+server.js
+++ b/src/routes/(app)/password/+server.js
@@ -1,7 +1,7 @@
-import { json } from '@sveltejs/kit';
-import { auth, post } from '$lib/utils';
+import { json } from "@sveltejs/kit";
+import { auth, post } from "$lib/utils";
export async function POST({ cookies, request }) {
- let result = await post('/password', await request.json(), auth(cookies));
- return json(result);
+ let result = await post("/password", await request.json(), auth(cookies));
+ return json(result);
}
diff --git a/src/routes/(app)/payment/[id]/+page.server.js b/src/routes/(app)/payment/[id]/+page.server.js
index 1c90ab69..5a364c65 100644
--- a/src/routes/(app)/payment/[id]/+page.server.js
+++ b/src/routes/(app)/payment/[id]/+page.server.js
@@ -1,6 +1,6 @@
-import { auth, get } from '$lib/utils';
+import { auth, get } from "$lib/utils";
export async function load({ cookies, params }) {
- let payment = await get(`/payments/${params.id}`, auth(cookies));
- return { payment };
+ let payment = await get(`/payments/${params.id}`, auth(cookies));
+ return { payment };
}
diff --git a/src/routes/(app)/payment/[id]/+page.svelte b/src/routes/(app)/payment/[id]/+page.svelte
index 98c5a68b..7e3c6792 100644
--- a/src/routes/(app)/payment/[id]/+page.svelte
+++ b/src/routes/(app)/payment/[id]/+page.svelte
@@ -1,158 +1,173 @@
<script>
- import { t } from '$lib/translations';
- import { back, copy, f, s, fiat, post, sats, success, types } from '$lib/utils';
- import { Avatar, Icon } from '$comp';
- import { format } from 'date-fns';
- import { PUBLIC_EXPLORER as expl } from '$env/static/public';
+ import { t } from "$lib/translations";
+ import {
+ back,
+ copy,
+ f,
+ s,
+ fiat,
+ post,
+ sats,
+ success,
+ types,
+ } from "$lib/utils";
+ import { Avatar, Icon } from "$comp";
+ import { format } from "date-fns";
+ import { PUBLIC_EXPLORER as expl } from "$env/static/public";
- export let data;
- let { user, payment: p } = data;
+ export let data;
+ let { user, payment: p } = data;
- let { username } = user;
- let { id, amount, created, rate, type, ref, tip, ourfee, fee, currency } = p;
- let a = Math.abs(amount);
+ let { username } = user;
+ let { id, amount, created, rate, type, ref, tip, ourfee, fee, currency } = p;
+ let a = Math.abs(amount);
- let print = async () => {
- await post(`/payment/${id}/print`, { id });
- success('Printing!');
- };
+ let print = async () => {
+ await post(`/payment/${id}/print`, { id });
+ success("Printing!");
+ };
- fee = fee || 0;
+ fee = fee || 0;
- let direction = amount > 0 ? $t('payments.from') : $t('payments.to')
- direction = direction[0].toUpperCase() + direction.substr(1, direction.length);
+ let direction = amount > 0 ? $t("payments.from") : $t("payments.to");
+ direction =
+ direction[0].toUpperCase() + direction.substr(1, direction.length);
</script>
<a href={`/${username}/payments`}>
- <button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80">
- <Icon icon="arrow-left" style="w-10" />
- </button>
+ <button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80">
+ <Icon icon="arrow-left" style="w-10" />
+ </button>
</a>
<div class="container mx-auto max-w-lg px-4 space-y-8 break-all text-2xl">
- <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10">
- {$t(amount < 0 ? 'payments.sent' : 'payments.received')}
- </h1>
+ <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10">
+ {$t(amount < 0 ? "payments.sent" : "payments.received")}
+ </h1>
- {#if p.with}
- <div>
- <span class="text-lg text-secondary my-auto mr-2">{direction}</span>
- <a href={`/${p.with.username}`}>
- <div class="flex">
- <div class="my-auto">
- <Avatar user={p.with} size={20} />
- </div>
- <div class="my-auto ml-1">{p.with.username}</div>
- </div>
- </a>
- </div>
- {/if}
- {#if p.type === types.fund}
- <div>
- <span class="text-lg text-secondary my-auto mr-2">{direction}</span>
- <a href={`/fund/${p.memo}`}>
- <div class="flex">
- <div class="my-auto">
- <img src="/images/moneypot.png" class="w-12" alt="Pot" />
- </div>
- <div class="my-auto ml-1">{p.memo}</div>
- </div>
- </a>
- </div>
- {/if}
- <div>
- <span class="text-lg text-secondary">{$t('payments.amount')}</span>
- <div>
- {f(fiat(a, rate), currency)}
- <span class="text-secondary text-lg">
- ⚡️{s(a)}
- </span>
- </div>
- </div>
+ {#if p.with}
+ <div>
+ <span class="text-lg text-secondary my-auto mr-2">{direction}</span>
+ <a href={`/${p.with.username}`}>
+ <div class="flex">
+ <div class="my-auto">
+ <Avatar user={p.with} size={20} />
+ </div>
+ <div class="my-auto ml-1">{p.with.username}</div>
+ </div>
+ </a>
+ </div>
+ {/if}
+ {#if p.type === types.fund}
+ <div>
+ <span class="text-lg text-secondary my-auto mr-2">{direction}</span>
+ <a href={`/fund/${p.memo}`}>
+ <div class="flex">
+ <div class="my-auto">
+ <img src="/images/moneypot.png" class="w-12" alt="Pot" />
+ </div>
+ <div class="my-auto ml-1">{p.memo}</div>
+ </div>
+ </a>
+ </div>
+ {/if}
+ <div>
+ <span class="text-lg text-secondary">{$t("payments.amount")}</span>
+ <div>
+ {f(fiat(a, rate), currency)}
+ <span class="text-secondary text-lg">
+ ⚡️{s(a)}
+ </span>
+ </div>
+ </div>
- {#if tip}
- <div>
- <span class="text-lg text-secondary">{$t('invoice.tip')}</span>
- <div>
- {f(fiat(tip, rate), currency)}
- ({Math.round((tip * 100) / Math.abs(a))}%)
- <span class="text-secondary text-lg">
- ⚡️{s(tip)}
- </span>
- </div>
- </div>
+ {#if tip}
+ <div>
+ <span class="text-lg text-secondary">{$t("invoice.tip")}</span>
+ <div>
+ {f(fiat(tip, rate), currency)}
+ ({Math.round((tip * 100) / Math.abs(a))}%)
+ <span class="text-secondary text-lg">
+ ⚡️{s(tip)}
+ </span>
+ </div>
+ </div>
- <div>
- <span class="text-lg text-secondary">Total</span>
- <div>
- {f(fiat(a + (tip || 0), rate), currency)}
- <span class="text-secondary text-lg">⚡️{`${s(a + (tip || 0))}`} </span>
- </div>
- </div>
- {/if}
+ <div>
+ <span class="text-lg text-secondary">Total</span>
+ <div>
+ {f(fiat(a + (tip || 0), rate), currency)}
+ <span class="text-secondary text-lg">⚡️{`${s(a + (tip || 0))}`} </span>
+ </div>
+ </div>
+ {/if}
- <div>
- <span class="text-lg text-secondary">Network fee</span>
- <div>
- {f(fiat(fee, rate), currency)}
- <span class="text-secondary text-lg">⚡️{`${s(fee)}`}</span>
- </div>
- </div>
+ <div>
+ <span class="text-lg text-secondary">Network fee</span>
+ <div>
+ {f(fiat(fee, rate), currency)}
+ <span class="text-secondary text-lg">⚡️{`${s(fee)}`}</span>
+ </div>
+ </div>
- {#if ourfee > 0}
- <div>
- <span class="text-lg text-secondary">Platform fee</span>
- <div>
- {f(fiat(ourfee, rate), currency)}
- <span class="text-secondary">⚡️{`${s(ourfee)}`}</span>
- </div>
- </div>
- {/if}
+ {#if ourfee > 0}
+ <div>
+ <span class="text-lg text-secondary">Platform fee</span>
+ <div>
+ {f(fiat(ourfee, rate), currency)}
+ <span class="text-secondary">⚡️{`${s(ourfee)}`}</span>
+ </div>
+ </div>
+ {/if}
- <div>
- <span class="text-lg text-secondary">Date</span>
- <div>
- {format(new Date(created), 'MMMM d')},
- {format(new Date(created), 'h:mm aaa')}
- </div>
- </div>
+ <div>
+ <span class="text-lg text-secondary">Date</span>
+ <div>
+ {format(new Date(created), "MMMM d")},
+ {format(new Date(created), "h:mm aaa")}
+ </div>
+ </div>
- {#if type === 'bitcoin'}
- <div>
- <span class="text-lg text-secondary">Txid</span>
- <div class="flex">
- <div>
- <a href={`${expl}/tx/${id}`} target="_blank" rel="noreferrer">{id}</a>
- </div>
- <button class="flex font-bold hover:opacity-80 mb-auto my-auto" on:click={() => copy(id)}
- ><Icon icon="copy" style="ml-2 w-20 my-auto" />
- </button>
- </div>
- </div>
- {/if}
+ {#if type === "bitcoin"}
+ <div>
+ <span class="text-lg text-secondary">Txid</span>
+ <div class="flex">
+ <div>
+ <a href={`${expl}/tx/${id}`} target="_blank" rel="noreferrer">{id}</a>
+ </div>
+ <button
+ class="flex font-bold hover:opacity-80 mb-auto my-auto"
+ on:click={() => copy(id)}
+ ><Icon icon="copy" style="ml-2 w-20 my-auto" />
+ </button>
+ </div>
+ </div>
+ {/if}
- {#if user.hasprinter}
- <div>
- <button
- class="rounded-full border py-3 px-6 hover:opacity-80 flex w-full md:w-60"
- on:click={print}
- >
- <div class="mx-auto flex">
- <Icon icon="printer" style="my-auto h-6 mr-2" />
- <div class="my-auto mt-1 text-base">{$t('payments.print')}</div>
- </div>
- </button>
- </div>
- {:else}
- <div>
- <a href={`/payment/${id}/plain`}>
- <button class="rounded-full border py-3 px-6 hover:opacity-80 flex w-full md:w-60">
- <div class="mx-auto flex">
- <Icon icon="printer" style="my-auto h-6 mr-2" />
- <div class="my-auto mt-1 text-base">{$t('payments.print')}</div>
- </div>
- </button>
- </a>
- </div>
- {/if}
+ {#if user.hasprinter}
+ <div>
+ <button
+ class="rounded-full border py-3 px-6 hover:opacity-80 flex w-full md:w-60"
+ on:click={print}
+ >
+ <div class="mx-auto flex">
+ <Icon icon="printer" style="my-auto h-6 mr-2" />
+ <div class="my-auto mt-1 text-base">{$t("payments.print")}</div>
+ </div>
+ </button>
+ </div>
+ {:else}
+ <div>
+ <a href={`/payment/${id}/plain`}>
+ <button
+ class="rounded-full border py-3 px-6 hover:opacity-80 flex w-full md:w-60"
+ >
+ <div class="mx-auto flex">
+ <Icon icon="printer" style="my-auto h-6 mr-2" />
+ <div class="my-auto mt-1 text-base">{$t("payments.print")}</div>
+ </div>
+ </button>
+ </a>
+ </div>
+ {/if}
</div>
diff --git a/src/routes/(app)/payment/[id]/plain/+page.server.js b/src/routes/(app)/payment/[id]/plain/+page.server.js
index 1c90ab69..5a364c65 100644
--- a/src/routes/(app)/payment/[id]/plain/+page.server.js
+++ b/src/routes/(app)/payment/[id]/plain/+page.server.js
@@ -1,6 +1,6 @@
-import { auth, get } from '$lib/utils';
+import { auth, get } from "$lib/utils";
export async function load({ cookies, params }) {
- let payment = await get(`/payments/${params.id}`, auth(cookies));
- return { payment };
+ let payment = await get(`/payments/${params.id}`, auth(cookies));
+ return { payment };
}
diff --git a/src/routes/(app)/payment/[id]/plain/+page.svelte b/src/routes/(app)/payment/[id]/plain/+page.svelte
index 5ffd1656..27e8d0ed 100644
--- a/src/routes/(app)/payment/[id]/plain/+page.svelte
+++ b/src/routes/(app)/payment/[id]/plain/+page.svelte
@@ -1,77 +1,87 @@
<script>
- import { goto } from '$app/navigation';
- import { onMount } from 'svelte';
- import { browser } from '$app/environment';
- import { t } from '$lib/translations';
- import { back, copy, f, s, fiat, post, sats, success, types } from '$lib/utils';
- import { Avatar, Icon } from '$comp';
- import { format } from 'date-fns';
- import { PUBLIC_EXPLORER as expl } from '$env/static/public';
+ import { goto } from "$app/navigation";
+ import { onMount } from "svelte";
+ import { browser } from "$app/environment";
+ import { t } from "$lib/translations";
+ import {
+ back,
+ copy,
+ f,
+ s,
+ fiat,
+ post,
+ sats,
+ success,
+ types,
+ } from "$lib/utils";
+ import { Avatar, Icon } from "$comp";
+ import { format } from "date-fns";
+ import { PUBLIC_EXPLORER as expl } from "$env/static/public";
- export let data;
- let { user, payment: p } = data;
+ export let data;
+ let { user, payment: p } = data;
- let { username } = user;
- let { id, amount, created, rate, type, ref, tip, ourfee, fee, currency } = p;
- let a = Math.abs(amount);
+ let { username } = user;
+ let { id, amount, created, rate, type, ref, tip, ourfee, fee, currency } = p;
+ let a = Math.abs(amount);
- onMount(() => {
- if (browser) {
- let main = document.querySelector('main');
- main.style.paddingBottom = '0';
- window.onfocus = () => goto(`/payment/${id}`);
- window.print();
- }
- });
+ onMount(() => {
+ if (browser) {
+ let main = document.querySelector("main");
+ main.style.paddingBottom = "0";
+ window.onfocus = () => goto(`/payment/${id}`);
+ window.print();
+ }
+ });
- fee = fee || 0;
+ fee = fee || 0;
</script>
<div class="space-y-5">
- <div>
- <span class="text-lg text-secondary">Amount</span>
- <div>
- {f(fiat(a, rate), currency)}
- </div>
- </div>
+ <div>
+ <span class="text-lg text-secondary">Amount</span>
+ <div>
+ {f(fiat(a, rate), currency)}
+ </div>
+ </div>
- {#if tip}
- <div>
- <span class="text-lg text-secondary">Tip</span>
- <div>
- {f(fiat(tip, rate), currency)}
- ({Math.round((tip * 100) / Math.abs(a))}%)
- </div>
- </div>
+ {#if tip}
+ <div>
+ <span class="text-lg text-secondary">Tip</span>
+ <div>
+ {f(fiat(tip, rate), currency)}
+ ({Math.round((tip * 100) / Math.abs(a))}%)
+ </div>
+ </div>
- <div>
- <span class="text-lg text-secondary">Total</span>
- <div>
- {f(fiat(a + (tip || 0), rate), currency)}
- </div>
- </div>
- {/if}
+ <div>
+ <span class="text-lg text-secondary">Total</span>
+ <div>
+ {f(fiat(a + (tip || 0), rate), currency)}
+ </div>
+ </div>
+ {/if}
- <div>
- <span class="text-lg text-secondary">Date</span>
- <div>
- {format(new Date(created), 'MMMM d')},
- {format(new Date(created), 'h:mm aaa')}
- </div>
- </div>
+ <div>
+ <span class="text-lg text-secondary">Date</span>
+ <div>
+ {format(new Date(created), "MMMM d")},
+ {format(new Date(created), "h:mm aaa")}
+ </div>
+ </div>
- {#if type === 'bitcoin'}
- <div>
- <span class="text-lg text-secondary">Txid</span>
- <div class="flex">
- <div>
- {id}
- </div>
- </div>
- </div>
- {/if}
+ {#if type === "bitcoin"}
+ <div>
+ <span class="text-lg text-secondary">Txid</span>
+ <div class="flex">
+ <div>
+ {id}
+ </div>
+ </div>
+ </div>
+ {/if}
</div>
<a href={`/payment/${id}`}>
- <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" />
+ <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" />
</a>
diff --git a/src/routes/(app)/payment/[id]/print/+server.js b/src/routes/(app)/payment/[id]/print/+server.js
index fde834d1..2fdfc8c1 100644
--- a/src/routes/(app)/payment/[id]/print/+server.js
+++ b/src/routes/(app)/payment/[id]/print/+server.js
@@ -1,7 +1,7 @@
-import { auth, post } from '$lib/utils';
-import { json } from '@sveltejs/kit';
+import { auth, post } from "$lib/utils";
+import { json } from "@sveltejs/kit";
export async function POST({ cookies, request }) {
- let body = await request.json();
- return json(post('/print', body, auth(cookies)));
+ let body = await request.json();
+ return json(post("/print", body, auth(cookies)));
}
diff --git a/src/routes/(app)/pin/+server.js b/src/routes/(app)/pin/+server.js
index 9f63f545..76d407bf 100644
--- a/src/routes/(app)/pin/+server.js
+++ b/src/routes/(app)/pin/+server.js
@@ -1,7 +1,7 @@
-import { json } from '@sveltejs/kit';
-import { auth, post } from '$lib/utils';
+import { json } from "@sveltejs/kit";
+import { auth, post } from "$lib/utils";
export async function POST({ cookies, request }) {
- let result = await post('/pin', await request.json(), auth(cookies));
- return json(result);
+ let result = await post("/pin", await request.json(), auth(cookies));
+ return json(result);
}
diff --git a/src/routes/(app)/qr/[text]/+page.server.js b/src/routes/(app)/qr/[text]/+page.server.js
index 3ab0725f..5eaafe57 100644
--- a/src/routes/(app)/qr/[text]/+page.server.js
+++ b/src/routes/(app)/qr/[text]/+page.server.js
@@ -1,8 +1,8 @@
-import Qr from 'qrcode-base64';
+import Qr from "qrcode-base64";
export async function load({ params }) {
- let { text } = params;
- let src = Qr.drawImg(text, { size: 600 });
+ let { text } = params;
+ let src = Qr.drawImg(text, { size: 600 });
- return { src, text };
+ return { src, text };
}
diff --git a/src/routes/(app)/qr/[text]/+page.svelte b/src/routes/(app)/qr/[text]/+page.svelte
index f3cd791a..d943b0b5 100644
--- a/src/routes/(app)/qr/[text]/+page.svelte
+++ b/src/routes/(app)/qr/[text]/+page.svelte
@@ -1,24 +1,24 @@
<script>
- import { Icon, Qr } from '$comp';
+ import { Icon, Qr } from "$comp";
import { back } from "$lib/utils";
- export let data;
- let { src, text } = data;
+ export let data;
+ let { src, text } = data;
</script>
-
<button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80" on:click={back}>
<Icon icon="arrow-left" style="w-10" />
</button>
-
<div class="container px-4 max-w-4xl mx-auto mt-10 space-y-5">
- <div class="flex justify-center items-center">
- <div class="md:shadow-xl rounded-3xl md:px-10 pt-5 pb-10 space-y-5 w-full md:mx-5">
+ <div class="flex justify-center items-center">
+ <div
+ class="md:shadow-xl rounded-3xl md:px-10 pt-5 pb-10 space-y-5 w-full md:mx-5"
+ >
<Qr {src} />
<div class="text-center w-full text-2xl py-10 break-all">
{text}
</div>
- </div>
- </div>
+ </div>
+ </div>
</div>
diff --git a/src/routes/(app)/redeem/[redeemcode]/+page.server.js b/src/routes/(app)/redeem/[redeemcode]/+page.server.js
index 4f685d68..f227f22e 100644
--- a/src/routes/(app)/redeem/[redeemcode]/+page.server.js
+++ b/src/routes/(app)/redeem/[redeemcode]/+page.server.js
@@ -1,17 +1,17 @@
-import { error, redirect } from '@sveltejs/kit';
-import { auth, post } from '$lib/utils';
+import { error, redirect } from "@sveltejs/kit";
+import { auth, post } from "$lib/utils";
export async function load({ cookies, locals, params }) {
- let { user } = locals;
+ let { user } = locals;
- if (user) {
- try {
- await post('/redeem', params, auth(cookies));
- } catch (e) {
- throw redirect(307, '/');
- }
- throw redirect(307, `/${user.username}/payments`);
- }
+ if (user) {
+ try {
+ await post("/redeem", params, auth(cookies));
+ } catch (e) {
+ throw redirect(307, "/");
+ }
+ throw redirect(307, `/${user.username}/payments`);
+ }
- throw redirect(307, '/login');
+ throw redirect(307, "/login");
}
diff --git a/src/routes/(app)/redeem/[redeemcode]/+page.svelte b/src/routes/(app)/redeem/[redeemcode]/+page.svelte
index a4609307..b2fcf2f4 100644
--- a/src/routes/(app)/redeem/[redeemcode]/+page.svelte
+++ b/src/routes/(app)/redeem/[redeemcode]/+page.svelte
@@ -1,4 +1,4 @@
<script>
- import { loginRedirect } from '$lib/store';
- $loginRedirect = undefined;
+ import { loginRedirect } from "$lib/store";
+ $loginRedirect = undefined;
</script>
diff --git a/src/routes/(app)/register/+page.server.js b/src/routes/(app)/register/+page.server.js
index 45344907..e986c73d 100644
--- a/src/routes/(app)/register/+page.server.js
+++ b/src/routes/(app)/register/+page.server.js
@@ -1,41 +1,42 @@
-import { fail, redirect } from '@sveltejs/kit';
-import { fd, post, login } from '$lib/utils';
+import { fail, redirect } from "@sveltejs/kit";
+import { fd, post, login } from "$lib/utils";
export const load = async ({ parent }) => {
- let { user } = await parent();
- if (user) throw redirect(307, `/${user.username}`);
+ let { user } = await parent();
+ if (user) throw redirect(307, `/${user.username}`);
- let index = Math.floor(Math.random() * 64) + 1;
+ let index = Math.floor(Math.random() * 64) + 1;
- return { index };
+ return { index };
};
export const actions = {
- default: async ({ cookies, request }) => {
- let ip = request.headers.get('cf-connecting-ip');
+ default: async ({ cookies, request }) => {
+ let ip = request.headers.get("cf-connecting-ip");
- let form = await fd(request);
- let { profile, username, password, cipher, salt, pubkey, loginRedirect } = form;
- let user = { profile, username, password, cipher, salt, pubkey };
- let error;
+ let form = await fd(request);
+ let { profile, username, password, cipher, salt, pubkey, loginRedirect } =
+ form;
+ let user = { profile, username, password, cipher, salt, pubkey };
+ let error;
- if (loginRedirect === 'undefined') loginRedirect = undefined;
+ if (loginRedirect === "undefined") loginRedirect = undefined;
- try {
- await post('/register', { user }, { 'cf-connecting-ip': ip });
- } catch (e) {
- console.log(e);
- if (e.message.includes('taken')) error = e.message;
- }
+ try {
+ await post("/register", { user }, { "cf-connecting-ip": ip });
+ } catch (e) {
+ console.log(e);
+ if (e.message.includes("taken")) error = e.message;
+ }
- try {
- await login(user, cookies, ip);
- error = null;
- } catch (e) {
- error ||= e.message;
- }
+ try {
+ await login(user, cookies, ip);
+ error = null;
+ } catch (e) {
+ error ||= e.message;
+ }
- if (error) return fail(400, { error });
- throw redirect(307, loginRedirect || `/${user.username}`);
- }
+ if (error) return fail(400, { error });
+ throw redirect(307, loginRedirect || `/${user.username}`);
+ },
};
diff --git a/src/routes/(app)/register/+page.svelte b/src/routes/(app)/register/+page.svelte
index a9f9d047..8310aed1 100644
--- a/src/routes/(app)/register/+page.svelte
+++ b/src/routes/(app)/register/+page.svelte
@@ -1,287 +1,311 @@
<script>
- import punks from '$lib/punks';
- import { upload } from '$lib/upload';
- import { afterNavigate, invalidateAll } from '$app/navigation';
- import { applyAction, deserialize } from '$app/forms';
- import { tick } from 'svelte';
- import { fly } from 'svelte/transition';
- import { enhance } from '$app/forms';
- import { Pin, Icon, Spinner } from '$comp';
- import { focus, fail } from '$lib/utils';
- import { avatar, password, loginRedirect } from '$lib/store';
- import { t } from '$lib/translations';
- import { page } from '$app/stores';
- import { generate } from '$lib/nostr';
- import {
- NumberDictionary,
- uniqueNamesGenerator,
- colors,
- adjectives,
- animals
- } from 'unique-names-generator';
+ import punks from "$lib/punks";
+ import { upload } from "$lib/upload";
+ import { afterNavigate, invalidateAll } from "$app/navigation";
+ import { applyAction, deserialize } from "$app/forms";
+ import { tick } from "svelte";
+ import { fly } from "svelte/transition";
+ import { enhance } from "$app/forms";
+ import { Pin, Icon, Spinner } from "$comp";
+ import { focus, fail } from "$lib/utils";
+ import { avatar, password, loginRedirect } from "$lib/store";
+ import { t } from "$lib/translations";
+ import { page } from "$app/stores";
+ import { generate } from "$lib/nostr";
+ import {
+ NumberDictionary,
+ uniqueNamesGenerator,
+ colors,
+ adjectives,
+ animals,
+ } from "unique-names-generator";
- export let form;
- export let data;
+ export let form;
+ export let data;
- $: data && ($avatar = undefined);
- let username;
- let { index } = data;
+ $: data && ($avatar = undefined);
+ let username;
+ let { index } = data;
- let cleared;
- let clear = () => {
- if (!cleared) {
- cleared = true;
- username = '';
- $password = '';
- revealPassword = false;
- }
- };
+ let cleared;
+ let clear = () => {
+ if (!cleared) {
+ cleared = true;
+ username = "";
+ $password = "";
+ revealPassword = false;
+ }
+ };
- let refresh = async () => {
- username = uniqueNamesGenerator({
- dictionaries: [animals, NumberDictionary.generate({ min: 10, max: 99 })],
- length: 2,
- separator: ''
- });
+ let refresh = async () => {
+ username = uniqueNamesGenerator({
+ dictionaries: [animals, NumberDictionary.generate({ min: 10, max: 99 })],
+ length: 2,
+ separator: "",
+ });
- $password = uniqueNamesGenerator({
- dictionaries: [colors, NumberDictionary.generate({ min: 100, max: 999 })],
- length: 2,
- separator: ''
- });
+ $password = uniqueNamesGenerator({
+ dictionaries: [colors, NumberDictionary.generate({ min: 100, max: 999 })],
+ length: 2,
+ separator: "",
+ });
- revealPassword = true;
- };
+ revealPassword = true;
+ };
- afterNavigate(async () => {
- try {
- await invalidateAll();
- } catch (e) {
- console.log(e);
- }
- });
+ afterNavigate(async () => {
+ try {
+ await invalidateAll();
+ } catch (e) {
+ console.log(e);
+ }
+ });
- let token, formElement;
- let code = [];
- let redirect;
+ let token, formElement;
+ let code = [];
+ let redirect;
- $: need2fa = form?.message === '2fa';
- let cancel = () => (need2fa = false);
- $: if (form?.message === '2fa' && form.token === token) token = '';
+ $: need2fa = form?.message === "2fa";
+ let cancel = () => (need2fa = false);
+ $: if (form?.message === "2fa" && form.token === token) token = "";
- let email, btn;
+ let email, btn;
- $: update(form);
- let update = (form) => form && ({ username, password: $password } = form);
+ $: update(form);
+ let update = (form) => form && ({ username, password: $password } = form);
- $: token && token?.length === 6 && tick().then(() => btn.click());
+ $: token && token?.length === 6 && tick().then(() => btn.click());
- let revealPassword;
+ let revealPassword;
- let loading;
- async function handleSubmit(e) {
- loading = true;
+ let loading;
+ async function handleSubmit(e) {
+ loading = true;
- let data = new FormData(this);
- let user = Object.fromEntries(data);
- await generate(user);
+ let data = new FormData(this);
+ let user = Object.fromEntries(data);
+ await generate(user);
- for (let k in user) {
- data.set(k, user[k]);
- }
+ for (let k in user) {
+ data.set(k, user[k]);
+ }
- data.set('profile', punks[index]);
- if ($avatar) {
- try {
- let { hash } = JSON.parse(
- await upload($avatar.file, $avatar.type, $avatar.progress, token)
- );
+ data.set("profile", punks[index]);
+ if ($avatar) {
+ try {
+ let { hash } = JSON.parse(
+ await upload($avatar.file, $avatar.type, $avatar.progress, token)
+ );
- data.set('profile', hash);
- await fetch(`/api/public/${hash}.webp`, { cache: 'reload', mode: 'no-cors' });
- } catch (e) {
- console.log('problem uploading avatar', e);
- }
- }
+ data.set("profile", hash);
+ await fetch(`/api/public/${hash}.webp`, {
+ cache: "reload",
+ mode: "no-cors",
+ });
+ } catch (e) {
+ console.log("problem uploading avatar", e);
+ }
+ }
- const response = await fetch('/register', {
- method: 'POST',
- body: data
- });
+ const response = await fetch("/register", {
+ method: "POST",
+ body: data,
+ });
- const result = deserialize(await response.text());
+ const result = deserialize(await response.text());
- if (result.type === 'success') {
- await invalidateAll();
- }
+ if (result.type === "success") {
+ await invalidateAll();
+ }
- applyAction(result);
- loading = false;
- }
+ applyAction(result);
+ loading = false;
+ }
- let avatarInput;
- $: src = `/api/public/${punks[index]}.webp`;
- let decr = () => (index = index <= 0 ? 63 : index - 1);
- let incr = () => (index = index >= 63 ? 0 : index + 1);
- let selectAvatar = () => avatarInput.click();
+ let avatarInput;
+ $: src = `/api/public/${punks[index]}.webp`;
+ let decr = () => (index = index <= 0 ? 63 : index - 1);
+ let incr = () => (index = index >= 63 ? 0 : index + 1);
+ let selectAvatar = () => avatarInput.click();
- let progress;
- let handleFile = async ({ target }) => {
- let type = 'profile';
- let file = target.files[0];
- if (!file) return;
+ let progress;
+ let handleFile = async ({ target }) => {
+ let type = "profile";
+ let file = target.files[0];
+ if (!file) return;
- if (file.size > 10000000) form.error = 'File too large';
- $avatar = { file, type, progress };
+ if (file.size > 10000000) form.error = "File too large";
+ $avatar = { file, type, progress };
- var reader = new FileReader();
- reader.onload = async (e) => {
- src = e.target.result;
- };
+ var reader = new FileReader();
+ reader.onload = async (e) => {
+ src = e.target.result;
+ };
- reader.readAsDataURL(file);
- };
+ reader.readAsDataURL(file);
+ };
</script>
{#if need2fa}
- <Pin bind:value={token} title="Enter 2FA Code" {cancel} notify={false} />
+ <Pin bind:value={token} title="Enter 2FA Code" {cancel} notify={false} />
{/if}
<div class="pt-4">
- <div class="w-[243px] mx-auto">
- <a href="/">
- <Icon icon="logo" />
- </a>
- </div>
+ <div class="w-[243px] mx-auto">
+ <a href="/">
+ <Icon icon="logo" />
+ </a>
+ </div>
- <div class="flex justify-center items-center">
- <div class="shadow-xl rounded-3xl p-4 pt-0 space-y-5 w-full mx-5 md:mx-0 md:w-[400px]">
- <input
- type="file"
- class="hidden"
- bind:this={avatarInput}
- on:change={(e) => handleFile(e, 'profile')}
- />
+ <div class="flex justify-center items-center">
+ <div
+ class="shadow-xl rounded-3xl p-4 pt-0 space-y-5 w-full mx-5 md:mx-0 md:w-[400px]"
+ >
+ <input
+ type="file"
+ class="hidden"
+ bind:this={avatarInput}
+ on:change={(e) => handleFile(e, "profile")}
+ />
- <div class="relative">
- <button class="absolute w-8 h-12 left-12 bg-white rounded top-12" on:click={decr}>
- <Icon icon="chevron-left" style="w-8 " />
- </button>
- <div class="relative w-32 mx-auto" on:click={selectAvatar}>
- <button
- class="w-32 h-32 rounded-full border-4 border-white overflow-hidden flex mx-auto relative"
- >
- <img
- {src}
- class="w-full h-full object-cover object-center overflow-hidden"
- alt={username}
- />
- </button>
- <button
- class="absolute bg-white rounded-full p-2 mx-auto right-0 bottom-0 z-10 bg-opacity-80"
- >
- <Icon icon="upload" style="w-8" />
- </button>
- </div>
- <button class="absolute w-8 h-12 right-12 bg-white rounded top-12" on:click={incr}>
- <Icon icon="chevron-left" style="w-8 rotate-180" />
- </button>
- </div>
+ <div class="relative">
+ <button
+ class="absolute w-8 h-12 left-12 bg-white rounded top-12"
+ on:click={decr}
+ >
+ <Icon icon="chevron-left" style="w-8 " />
+ </button>
+ <div class="relative w-32 mx-auto" on:click={selectAvatar}>
+ <button
+ class="w-32 h-32 rounded-full border-4 border-white overflow-hidden flex mx-auto relative"
+ >
+ <img
+ {src}
+ class="w-full h-full object-cover object-center overflow-hidden"
+ alt={username}
+ />
+ </button>
+ <button
+ class="absolute bg-white rounded-full p-2 mx-auto right-0 bottom-0 z-10 bg-opacity-80"
+ >
+ <Icon icon="upload" style="w-8" />
+ </button>
+ </div>
+ <button
+ class="absolute w-8 h-12 right-12 bg-white rounded top-12"
+ on:click={incr}
+ >
+ <Icon icon="chevron-left" style="w-8 rotate-180" />
+ </button>
+ </div>
- {#if form?.error}
- <div class="text-red-600 text-center" in:fly>
- {form.error}
- </div>
- {/if}
+ {#if form?.error}
+ <div class="text-red-600 text-center" in:fly>
+ {form.error}
+ </div>
+ {/if}
- <form class="space-y-5" on:submit|preventDefault={handleSubmit} method="POST">
- <input
- type="hidden"
- name="loginRedirect"
- value={$loginRedirect || $page.url.searchParams.get('redirect')}
- />
- <input type="hidden" name="token" value={token} />
+ <form
+ class="space-y-5"
+ on:submit|preventDefault={handleSubmit}
+ method="POST"
+ >
+ <input
+ type="hidden"
+ name="loginRedirect"
+ value={$loginRedirect || $page.url.searchParams.get("redirect")}
+ />
+ <input type="hidden" name="token" value={token} />
- <div class="relative">
- <label for="username" class="font-semibold">{$t('login.username')}</label>
- <input
- use:focus
- tabindex="1"
- name="username"
- type="text"
- required
- class="bg-primary"
- bind:value={username}
- on:focus={clear}
- autocapitalize="none"
- />
- <button type="button" on:click={refresh} class="absolute right-0 top-4" tabindex="5">
- <Icon icon="random" style="w-10 m-4" />
- </button>
- </div>
+ <div class="relative">
+ <label for="username" class="font-semibold"
+ >{$t("login.username")}</label
+ >
+ <input
+ use:focus
+ tabindex="1"
+ name="username"
+ type="text"
+ required
+ class="bg-primary"
+ bind:value={username}
+ on:focus={clear}
+ autocapitalize="none"
+ />
+ <button
+ type="button"
+ on:click={refresh}
+ class="absolute right-0 top-4"
+ tabindex="5"
+ >
+ <Icon icon="random" style="w-10 m-4" />
+ </button>
+ </div>
- <div class="relative">
- <label for="password" class="block font-semibold">{$t('login.password')}</label>
- {#if revealPassword}
- <input
- tabindex="2"
- name="password"
- type="text"
- required
- class="bg-primary"
- bind:value={$password}
- autocapitalize="none"
- />
- {:else}
- <input
- tabindex="2"
- name="password"
- type="password"
- required
- class="bg-primary"
- bind:value={$password}
- autocapitalize="none"
- on:focus={clear}
- />
- {/if}
- <button
- type="button"
- tabindex="4"
- on:click={() => (revealPassword = !revealPassword)}
- class="absolute right-0 top-4"
- >
- <Icon icon={revealPassword ? 'eye' : 'eye-off'} style="m-6" />
- </button>
- </div>
+ <div class="relative">
+ <label for="password" class="block font-semibold"
+ >{$t("login.password")}</label
+ >
+ {#if revealPassword}
+ <input
+ tabindex="2"
+ name="password"
+ type="text"
+ required
+ class="bg-primary"
+ bind:value={$password}
+ autocapitalize="none"
+ />
+ {:else}
+ <input
+ tabindex="2"
+ name="password"
+ type="password"
+ required
+ class="bg-primary"
+ bind:value={$password}
+ autocapitalize="none"
+ on:focus={clear}
+ />
+ {/if}
+ <button
+ type="button"
+ tabindex="4"
+ on:click={() => (revealPassword = !revealPassword)}
+ class="absolute right-0 top-4"
+ >
+ <Icon icon={revealPassword ? "eye" : "eye-off"} style="m-6" />
+ </button>
+ </div>
- <p class="text-secondary text-sm">
- {$t('login.passwordRecommendation')}
- </p>
+ <p class="text-secondary text-sm">
+ {$t("login.passwordRecommendation")}
+ </p>
- <button
- tabindex="3"
- type="submit"
- class="bg-black text-white w-full rounded-2xl p-4 font-semibold hover:opacity-80"
- disabled={loading}
- bind:this={btn}
- >
- {#if loading}
- <Spinner />
- {:else}
- {$t('login.register')}
- {/if}
- </button>
- </form>
+ <button
+ tabindex="3"
+ type="submit"
+ class="bg-black text-white w-full rounded-2xl p-4 font-semibold hover:opacity-80"
+ disabled={loading}
+ bind:this={btn}
+ >
+ {#if loading}
+ <Spinner />
+ {:else}
+ {$t("login.register")}
+ {/if}
+ </button>
+ </form>
- <p class="text-secondary text-center font-medium">
- {$t('login.haveAccount')}
- <a
- href={'/login'}
- class="block md:inline text-black underline underline-offset-4 hover:opacity-80"
- >
- {$t('login.signIn')}
- </a>
- </p>
- </div>
- </div>
+ <p class="text-secondary text-center font-medium">
+ {$t("login.haveAccount")}
+ <a
+ href={"/login"}
+ class="block md:inline text-black underline underline-offset-4 hover:opacity-80"
+ >
+ {$t("login.signIn")}
+ </a>
+ </p>
+ </div>
+ </div>
</div>
diff --git a/src/routes/(app)/scan/+page.svelte b/src/routes/(app)/scan/+page.svelte
index 8c5ea0a5..033eb28b 100644
--- a/src/routes/(app)/scan/+page.svelte
+++ b/src/routes/(app)/scan/+page.svelte
@@ -1,44 +1,51 @@
<script>
- import { Icon } from '$comp';
- import QrScanner from 'qr-scanner';
- import { onMount, onDestroy } from 'svelte';
- import { back } from '$lib/utils';
- import { goto } from '$app/navigation';
+ import { Icon } from "$comp";
+ import QrScanner from "qr-scanner";
+ import { onMount, onDestroy } from "svelte";
+ import { back } from "$lib/utils";
+ import { goto } from "$app/navigation";
- let scanner, vid;
- onMount(() => {
- scanner = new QrScanner(vid, ({ data }) => scanner.stop() || goto(`/send/${encodeURI(data)}`), {
- highlightScanRegion: true,
- highlightCodeOutline: true
- });
- scanner.start();
- });
+ let scanner, vid;
+ onMount(() => {
+ scanner = new QrScanner(
+ vid,
+ ({ data }) => scanner.stop() || goto(`/send/${encodeURI(data)}`),
+ {
+ highlightScanRegion: true,
+ highlightCodeOutline: true,
+ }
+ );
+ scanner.start();
+ });
- onDestroy(() => scanner?.stop());
+ onDestroy(() => scanner?.stop());
</script>
<button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80" on:click={back}>
- <Icon icon="arrow-left" style="w-10" />
+ <Icon icon="arrow-left" style="w-10" />
</button>
<div class="flex w-full mb-4 px-4">
- <div class="bg-black mx-auto rounded-3xl">
- <video
- bind:this={vid}
- class="border-4 rounded-3xl border-black md:max-w-[600px] !max-h-[80vh] !min-w-[300px]"
- />
- </div>
+ <div class="bg-black mx-auto rounded-3xl">
+ <video
+ bind:this={vid}
+ class="border-4 rounded-3xl border-black md:max-w-[600px] !max-h-[80vh] !min-w-[300px]"
+ />
+ </div>
</div>
<div class="flex w-full mb-4">
- <button class="mx-auto border rounded-full px-6 py-2 font-bold hover:opacity-80" on:click={back}>
- Cancel
- </button>
+ <button
+ class="mx-auto border rounded-full px-6 py-2 font-bold hover:opacity-80"
+ on:click={back}
+ >
+ Cancel
+ </button>
</div>
<style>
- video {
- transition: transform 0.3s ease-out;
- transform: scale(1);
- }
+ video {
+ transition: transform 0.3s ease-out;
+ transform: scale(1);
+ }
</style>
diff --git a/src/routes/(app)/send/+layout.svelte b/src/routes/(app)/send/+layout.svelte
index 47c11f1c..1a587edf 100644
--- a/src/routes/(app)/send/+layout.svelte
+++ b/src/routes/(app)/send/+layout.svelte
@@ -1,16 +1,16 @@
<script>
- import { goto } from '$app/navigation';
- import { Pin } from '$comp';
- import { pin } from '$lib/store';
+ import { goto } from "$app/navigation";
+ import { Pin } from "$comp";
+ import { pin } from "$lib/store";
- export let data;
+ export let data;
- let { user } = data;
- let cancel = () => goto(`/${user.username}`);
+ let { user } = data;
+ let cancel = () => goto(`/${user.username}`);
</script>
{#if user.haspin && (!$pin || $pin?.length !== 6)}
- <Pin bind:value={$pin} {cancel} />
+ <Pin bind:value={$pin} {cancel} />
{/if}
<slot />
diff --git a/src/routes/(app)/send/[...text]/+page.server.js b/src/routes/(app)/send/[...text]/+page.server.js
index 3ca50bca..9a76f21d 100644
--- a/src/routes/(app)/send/[...text]/+page.server.js
+++ b/src/routes/(app)/send/[...text]/+page.server.js
@@ -1,100 +1,100 @@
-import { validate } from 'bitcoin-address-validation';
-import bip21 from 'bip21';
-import { auth, get, post } from '$lib/utils';
-import { fail, redirect } from '@sveltejs/kit';
-import { PUBLIC_DOMAIN } from '$env/static/public';
+import { validate } from "bitcoin-address-validation";
+import bip21 from "bip21";
+import { auth, get, post } from "$lib/utils";
+import { fail, redirect } from "@sveltejs/kit";
+import { PUBLIC_DOMAIN } from "$env/static/public";
let parse = async (t, host) => {
- if (!t) return;
+ if (!t) return;
- if (t.startsWith('http')) throw redirect(307, t);
- if (t.startsWith(host)) throw redirect(307, `http://${t}`);
+ if (t.startsWith("http")) throw redirect(307, t);
+ if (t.startsWith(host)) throw redirect(307, `http://${t}`);
- let amount, user, id;
+ let amount, user, id;
- t = t.trim();
- t.toLowerCase().startsWith('bitcoin:') &&
- ({
- address: t,
- options: { amount }
- } = bip21.decode(t));
+ t = t.trim();
+ t.toLowerCase().startsWith("bitcoin:") &&
+ ({
+ address: t,
+ options: { amount },
+ } = bip21.decode(t));
- if (t.endsWith(`@${PUBLIC_DOMAIN}`)) t = t.split('@')[0];
- if (t.includes('@') && t.includes('.')) {
- try {
- t = await get(`/encode?address=${t}`);
- } catch (e) {}
- }
+ if (t.endsWith(`@${PUBLIC_DOMAIN}`)) t = t.split("@")[0];
+ if (t.includes("@") && t.includes(".")) {
+ try {
+ t = await get(`/encode?address=${t}`);
+ } catch (e) {}
+ }
- if (t.endsWith('@classic')) {
- ({ uuid: id } = await get(`/invoice/classic/${t.replace('@classic', '')}`));
- if (id) throw redirect(307, `/send/invoice/${id}`);
- }
+ if (t.endsWith("@classic")) {
+ ({ uuid: id } = await get(`/invoice/classic/${t.replace("@classic", "")}`));
+ if (id) throw redirect(307, `/send/invoice/${id}`);
+ }
- if (t.includes('/fund')) throw redirect(307, t.substring(t.indexOf('/fund')));
+ if (t.includes("/fund")) throw redirect(307, t.substring(t.indexOf("/fund")));
- if (t.toLowerCase().startsWith('lnurl')) throw redirect(307, `/ln/${t}`);
- if (t.includes(':')) t = t.split(':')[1];
+ if (t.toLowerCase().startsWith("lnurl")) throw redirect(307, `/ln/${t}`);
+ if (t.includes(":")) t = t.split(":")[1];
- // lightning
- if (t.toLowerCase().startsWith('ln')) {
- try {
- ({ id } = await get(`/invoice/${t}`));
- } catch (e) {
- throw redirect(307, `/send/lightning/${t}`);
- }
- }
+ // lightning
+ if (t.toLowerCase().startsWith("ln")) {
+ try {
+ ({ id } = await get(`/invoice/${t}`));
+ } catch (e) {
+ throw redirect(307, `/send/lightning/${t}`);
+ }
+ }
- // bitcoin
- if (validate(t)) {
- try {
- ({ id, user } = await get(`/invoice/${t}`));
- } catch (e) {
- let r = `/send/bitcoin/${t}`;
- if (amount) r += '/' + amount;
- throw redirect(307, r);
- }
+ // bitcoin
+ if (validate(t)) {
+ try {
+ ({ id, user } = await get(`/invoice/${t}`));
+ } catch (e) {
+ let r = `/send/bitcoin/${t}`;
+ if (amount) r += "/" + amount;
+ throw redirect(307, r);
+ }
- if (user) throw redirect(307, `/send/${user.username}`);
- else if (id) throw redirect(307, `/send/${id}`);
- }
+ if (user) throw redirect(307, `/send/${user.username}`);
+ else if (id) throw redirect(307, `/send/${id}`);
+ }
- // user
- try {
- user = await get(`/users/${t.split('/')[0]}`);
- if (user.anon) user = null;
- } catch (e) {}
+ // user
+ try {
+ user = await get(`/users/${t.split("/")[0]}`);
+ if (user.anon) user = null;
+ } catch (e) {}
- if (user) throw redirect(307, `/send/user/${t}`);
+ if (user) throw redirect(307, `/send/user/${t}`);
- let fund;
- try {
- fund = await get(`/fund/${t}`);
- } catch (e) {}
+ let fund;
+ try {
+ fund = await get(`/fund/${t}`);
+ } catch (e) {}
- if (fund) throw redirect(307, `/send/fund/${t}`);
+ if (fund) throw redirect(307, `/send/fund/${t}`);
- // invoice
- let invoice;
- try {
- invoice = await get(`/invoice/${t}`);
- } catch (e) {}
+ // invoice
+ let invoice;
+ try {
+ invoice = await get(`/invoice/${t}`);
+ } catch (e) {}
- if (invoice) throw redirect(307, `/send/invoice/${invoice.hash}`);
+ if (invoice) throw redirect(307, `/send/invoice/${invoice.hash}`);
};
export async function load({ cookies, params, request, url }) {
- let { text } = params;
- await parse(text, url.host);
- let contacts = await get('/contacts', auth(cookies));
- return { contacts };
+ let { text } = params;
+ await parse(text, url.host);
+ let contacts = await get("/contacts", auth(cookies));
+ return { contacts };
}
export const actions = {
- default: async ({ request, url }) => {
- const form = await request.formData();
- let t = form.get('text');
- await parse(t, url.host);
- return fail(400, { error: true });
- }
+ default: async ({ request, url }) => {
+ const form = await request.formData();
+ let t = form.get("text");
+ await parse(t, url.host);
+ return fail(400, { error: true });
+ },
};
diff --git a/src/routes/(app)/send/[...text]/+page.svelte b/src/routes/(app)/send/[...text]/+page.svelte
index 74afd846..67d4afaa 100644
--- a/src/routes/(app)/send/[...text]/+page.svelte
+++ b/src/routes/(app)/send/[...text]/+page.svelte
@@ -1,125 +1,129 @@
<script>
- import { page } from '$app/stores';
- import { fly } from 'svelte/transition';
- import { enhance } from '$app/forms';
- import { tick } from 'svelte';
- import { browser } from '$app/environment';
- import { t } from '$lib/translations';
- import { AppHeader, Avatar, Icon, Spinner } from '$comp';
- import { back, fail } from '$lib/utils';
+ import { page } from "$app/stores";
+ import { fly } from "svelte/transition";
+ import { enhance } from "$app/forms";
+ import { tick } from "svelte";
+ import { browser } from "$app/environment";
+ import { t } from "$lib/translations";
+ import { AppHeader, Avatar, Icon, Spinner } from "$comp";
+ import { back, fail } from "$lib/utils";
- export let data;
- export let form;
- data.subject = data.user;
+ export let data;
+ export let form;
+ data.subject = data.user;
- let { contacts } = data;
- let w;
+ let { contacts } = data;
+ let w;
- let el, textarea, text;
- let placeholder = $t('user.send.placeholder');
+ let el, textarea, text;
+ let placeholder = $t("user.send.placeholder");
- $: $page && setTimeout(() => w > 800 && textarea?.focus(), 0);
- let keypress = (e) => e.key === 'Enter' && (e.preventDefault() || el.click());
+ $: $page && setTimeout(() => w > 800 && textarea?.focus(), 0);
+ let keypress = (e) => e.key === "Enter" && (e.preventDefault() || el.click());
- let paste = async () => {
- text = await navigator.clipboard.readText();
- };
+ let paste = async () => {
+ text = await navigator.clipboard.readText();
+ };
</script>
<svelte:window bind:innerWidth={w} />
<AppHeader {data} />
<div class="container px-4 max-w-lg mx-auto space-y-5 mt-20">
- <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold">
- {$t('payments.send')}
- </h1>
+ <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold">
+ {$t("payments.send")}
+ </h1>
- <form method="POST" use:enhance>
- {#if form?.error}
- <div class="mb-5">
- <div class="text-red-600">
- {$t('error.unrecognizedInput')}
- </div>
- </div>
- {/if}
+ <form method="POST" use:enhance>
+ {#if form?.error}
+ <div class="mb-5">
+ <div class="text-red-600">
+ {$t("error.unrecognizedInput")}
+ </div>
+ </div>
+ {/if}
- <div class="mb-2">
- <input type="hidden" name="text" bind:value={text} />
+ <div class="mb-2">
+ <input type="hidden" name="text" bind:value={text} />
- <textarea
- {placeholder}
- on:keypress={keypress}
- class="w-full p-4 border rounded-xl h-48"
- bind:value={text}
- bind:this={textarea}
- />
- </div>
+ <textarea
+ {placeholder}
+ on:keypress={keypress}
+ class="w-full p-4 border rounded-xl h-48"
+ bind:value={text}
+ bind:this={textarea}
+ />
+ </div>
- <div class="flex justify-end">
- {#if text?.length}
- <a href={`/qr/${encodeURIComponent(text)}`}>
- <button
- type="button"
- class="flex border rounded-full px-6 py-2 font-bold hover:opacity-80 mr-1"
- >
- <Icon icon="qr" style="mr-2 w-6 my-auto invert" />
- <div class="my-auto">{$t('user.send.makeQR')}</div>
- </button>
- </a>
- {/if}
+ <div class="flex justify-end">
+ {#if text?.length}
+ <a href={`/qr/${encodeURIComponent(text)}`}>
+ <button
+ type="button"
+ class="flex border rounded-full px-6 py-2 font-bold hover:opacity-80 mr-1"
+ >
+ <Icon icon="qr" style="mr-2 w-6 my-auto invert" />
+ <div class="my-auto">{$t("user.send.makeQR")}</div>
+ </button>
+ </a>
+ {/if}
- <a href="/scan">
- <button
- type="button"
- class="flex border rounded-full px-6 py-2 font-bold hover:opacity-80 mr-1"
- >
- <Icon icon="camera" style="mr-2 w-6 my-auto" />
- <div class="my-auto">{$t('user.send.scan')}</div>
- </button>
- </a>
+ <a href="/scan">
+ <button
+ type="button"
+ class="flex border rounded-full px-6 py-2 font-bold hover:opacity-80 mr-1"
+ >
+ <Icon icon="camera" style="mr-2 w-6 my-auto" />
+ <div class="my-auto">{$t("user.send.scan")}</div>
+ </button>
+ </a>
- <button
- type="button"
- class="flex border rounded-full px-6 py-2 font-bold hover:opacity-80 mr-1"
- on:click={paste}
- >
- <Icon icon="paste" style="mr-2 w-6 my-auto" />
- <div class="my-auto">{$t('user.send.paste')}</div>
- </button>
+ <button
+ type="button"
+ class="flex border rounded-full px-6 py-2 font-bold hover:opacity-80 mr-1"
+ on:click={paste}
+ >
+ <Icon icon="paste" style="mr-2 w-6 my-auto" />
+ <div class="my-auto">{$t("user.send.paste")}</div>
+ </button>
- <button
- bind:this={el}
- type="submit"
- class="{!text
- ? 'opacity-50'
- : 'opacity-100 hover:opacity-80'} bg-black text-white border rounded-full px-6 py-2 font-bold"
- >
- {$t('user.send.next')}
- </button>
- </div>
- </form>
+ <button
+ bind:this={el}
+ type="submit"
+ class="{!text
+ ? 'opacity-50'
+ : 'opacity-100 hover:opacity-80'} bg-black text-white border rounded-full px-6 py-2 font-bold"
+ >
+ {$t("user.send.next")}
+ </button>
+ </div>
+ </form>
- {#if contacts.length}
- <div class="space-y-5">
- <h1 class="px-3 md:px-0 text-xl font-semibold mt-10">{$t('user.send.contacts')}</h1>
- <div>
- {#each contacts as c}
- <a href={`/send/${c.username}`}>
- <div class="border-b p-2 last:border-b-0 hover:bg-gray-100 rounded-2xl">
- <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>
- </div>
- </div>
- </a>
- {/each}
- </div>
- </div>
- {/if}
+ {#if contacts.length}
+ <div class="space-y-5">
+ <h1 class="px-3 md:px-0 text-xl font-semibold mt-10">
+ {$t("user.send.contacts")}
+ </h1>
+ <div>
+ {#each contacts as c}
+ <a href={`/send/${c.username}`}>
+ <div
+ class="border-b p-2 last:border-b-0 hover:bg-gray-100 rounded-2xl"
+ >
+ <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>
+ </div>
+ </div>
+ </a>
+ {/each}
+ </div>
+ </div>
+ {/if}
</div>
diff --git a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js
index 92a754ae..5cb86aec 100644
--- a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js
+++ b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js
@@ -1,33 +1,32 @@
-import { fail, redirect } from '@sveltejs/kit';
-import { auth, post, fd } from '$lib/utils';
+import { fail, redirect } from "@sveltejs/kit";
+import { auth, post, fd } from "$lib/utils";
let seen = {};
export const actions = {
- default: async ({ cookies, request }) => {
- try {
- let { address, amount, confirmed, feeRate, subtract, pin, stale, ts } = await await fd(
- request
- );
- if (seen[ts]) return fail(400, { message: 'Duplicate form submission' });
+ default: async ({ cookies, request }) => {
+ try {
+ let { address, amount, confirmed, feeRate, subtract, pin, stale, ts } =
+ await await fd(request);
+ if (seen[ts]) return fail(400, { message: "Duplicate form submission" });
- let estimate = await post(
- '/bitcoin/fee',
- { address, amount, feeRate, subtract },
- auth(cookies)
- );
+ let estimate = await post(
+ "/bitcoin/fee",
+ { address, amount, feeRate, subtract },
+ auth(cookies)
+ );
- let { tx } = estimate;
+ let { tx } = estimate;
- if (stale || !confirmed) return fail(400, estimate);
+ if (stale || !confirmed) return fail(400, estimate);
- seen[ts] = 1;
- setTimeout(() => delete seen[ts], 30000);
+ seen[ts] = 1;
+ setTimeout(() => delete seen[ts], 30000);
- await post('/bitcoin/send', { pin, subtract, tx }, auth(cookies));
- } catch (e) {
- return fail(400, { message: e.message });
- }
+ await post("/bitcoin/send", { pin, subtract, tx }, auth(cookies));
+ } catch (e) {
+ return fail(400, { message: e.message });
+ }
- throw redirect(307, '/sent');
- }
+ throw redirect(307, "/sent");
+ },
};
diff --git a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte
index 342eeebe..da11a22c 100644
--- a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte
+++ b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte
@@ -1,142 +1,159 @@
<script>
- import { tick } from 'svelte';
- import { t } from '$lib/translations';
- import { enhance } from '$app/forms';
- import { Toggle, Slider, Icon, Numpad, Spinner } from '$comp';
- import { page } from '$app/stores';
- import { fiat as toFiat, f, back, s, sat } from '$lib/utils';
- import { pin } from '$lib/store';
+ import { tick } from "svelte";
+ import { t } from "$lib/translations";
+ import { enhance } from "$app/forms";
+ import { Toggle, Slider, Icon, Numpad, Spinner } from "$comp";
+ import { page } from "$app/stores";
+ import { fiat as toFiat, f, back, s, sat } from "$lib/utils";
+ import { pin } from "$lib/store";
- export let data;
- export let form;
+ export let data;
+ export let form;
- let { rates } = data;
- let { address, amount } = $page.params;
- let { balance, currency } = data.user;
- let loading, submit, fiat, confirmed, feeRate, min, max, fee, ourfee, stale, subtract, adjusting;
+ let { rates } = data;
+ let { address, amount } = $page.params;
+ let { balance, currency } = data.user;
+ let loading,
+ submit,
+ fiat,
+ confirmed,
+ feeRate,
+ min,
+ max,
+ fee,
+ ourfee,
+ stale,
+ subtract,
+ adjusting;
- $: rate = rates[currency];
+ $: rate = rates[currency];
- let toggle = () => (loading = true);
- let toggleAdjusting = () => (adjusting = !adjusting);
+ let toggle = () => (loading = true);
+ let toggleAdjusting = () => (adjusting = !adjusting);
- $: update(form);
- let update = () => {
- loading = false;
- if (form?.message?.includes('pin')) $pin = undefined;
- if (form) ({ min, max, feeRate, fee, ourfee, subtract } = form);
- if (!ourfee) ourfee = 0;
- if (feeRate) confirmed = true;
- stale = false;
- };
+ $: update(form);
+ let update = () => {
+ loading = false;
+ if (form?.message?.includes("pin")) $pin = undefined;
+ if (form) ({ min, max, feeRate, fee, ourfee, subtract } = form);
+ if (!ourfee) ourfee = 0;
+ if (feeRate) confirmed = true;
+ stale = false;
+ };
- let setMax = () => {
- fiat = false;
- amount = balance;
- subtract = true;
- };
+ let setMax = () => {
+ fiat = false;
+ amount = balance;
+ subtract = true;
+ };
- $: if (amount + fee > balance) subtract = true;
+ $: if (amount + fee > balance) subtract = true;
- let timeout;
- let handle = (e) => {
- stale = true;
- feeRate = e.target.value;
- clearTimeout(timeout);
- timeout = setTimeout(() => {
- tick().then(() => submit.click());
- }, 200);
- };
+ let timeout;
+ let handle = (e) => {
+ stale = true;
+ feeRate = e.target.value;
+ clearTimeout(timeout);
+ timeout = setTimeout(() => {
+ tick().then(() => submit.click());
+ }, 200);
+ };
</script>
<button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80" on:click={back}>
- <Icon icon="arrow-left" style="w-10" />
+ <Icon icon="arrow-left" style="w-10" />
</button>
{#if form?.message}
- <div class="text-red-600 text-center">
- {form.message}
- </div>
+ <div class="text-red-600 text-center">
+ {form.message}
+ </div>
{/if}
<div class="container px-4 mt-20 max-w-xl mx-auto space-y-5 text-center">
- <h1 class="text-3xl md:text-4xl font-semibold mb-2">{$t('payments.send')}</h1>
+ <h1 class="text-3xl md:text-4xl font-semibold mb-2">{$t("payments.send")}</h1>
- <div class="text-lg text-secondary break-all">{address}</div>
+ <div class="text-lg text-secondary break-all">{address}</div>
- {#if confirmed}
- <div class="text-center">
- <h2 class="text-2xl md:text-3xl font-semibold">
- {f(toFiat(amount, rate), currency)}
- </h2>
- <h3 class="text-secondary md:text-lg mb-6 mt-1">⚡️{s(amount)}</h3>
- </div>
+ {#if confirmed}
+ <div class="text-center">
+ <h2 class="text-2xl md:text-3xl font-semibold">
+ {f(toFiat(amount, rate), currency)}
+ </h2>
+ <h3 class="text-secondary md:text-lg mb-6 mt-1">⚡️{s(amount)}</h3>
+ </div>
- <div class="text-center">
- <h2 class="text-secondary mb-5 text-lg">with fee</h2>
+ <div class="text-center">
+ <h2 class="text-secondary mb-5 text-lg">with fee</h2>
- <div class="relative">
- <h2 class="text-xl font-semibold">
- {f(toFiat(fee + ourfee, rate), currency)}
- </h2>
- <h3 class="text-secondary mb-6 mt-1">⚡️{s(fee + ourfee)}</h3>
- <button class="absolute right-1/4 top-0 text-secondary p-4" on:click={toggleAdjusting}
- ><Icon icon="settings" style="opacity-30" /></button
- >
- </div>
- </div>
- {:else}
- <Numpad bind:amount bind:fiat {currency} {submit} bind:rate />
- {/if}
+ <div class="relative">
+ <h2 class="text-xl font-semibold">
+ {f(toFiat(fee + ourfee, rate), currency)}
+ </h2>
+ <h3 class="text-secondary mb-6 mt-1">⚡️{s(fee + ourfee)}</h3>
+ <button
+ class="absolute right-1/4 top-0 text-secondary p-4"
+ on:click={toggleAdjusting}
+ ><Icon icon="settings" style="opacity-30" /></button
+ >
+ </div>
+ </div>
+ {:else}
+ <Numpad bind:amount bind:fiat {currency} {submit} bind:rate />
+ {/if}
- <form method="POST" use:enhance on:submit={toggle}>
- <input name="ts" value={Date.now()} type="hidden" />
- <input name="address" value={address} type="hidden" />
- <input name="amount" value={amount} type="hidden" />
- <input name="pin" value={$pin} type="hidden" />
- <input name="confirmed" value={confirmed} type="hidden" />
- <input name="feeRate" value={feeRate} type="hidden" />
- <input name="stale" value={stale} type="hidden" />
- <input name="subtract" value={subtract} type="hidden" />
+ <form method="POST" use:enhance on:submit={toggle}>
+ <input name="ts" value={Date.now()} type="hidden" />
+ <input name="address" value={address} type="hidden" />
+ <input name="amount" value={amount} type="hidden" />
+ <input name="pin" value={$pin} type="hidden" />
+ <input name="confirmed" value={confirmed} type="hidden" />
+ <input name="feeRate" value={feeRate} type="hidden" />
+ <input name="stale" value={stale} type="hidden" />
+ <input name="subtract" value={subtract} type="hidden" />
- {#if confirmed && adjusting}
- <div class="relative mt-10 mb-20">
- <div class="absolute top-5 left-0 text-secondary">Slower (~40 blocks)</div>
- <div class="absolute top-5 right-0 text-secondary">Faster (~1 block)</div>
- <Slider bind:value={feeRate} {handle} {min} {max} />
- </div>
- <div class="flex justify-center gap-2 mb-10">
- <div class="my-auto">
- <Toggle id="subtract" checked={subtract} bind:value={subtract} />
- </div>
- <label for="subtract" class="text-secondary text-lg my-auto mb-2"
- >Take fee from amount</label
- >
- </div>
- {/if}
+ {#if confirmed && adjusting}
+ <div class="relative mt-10 mb-20">
+ <div class="absolute top-5 left-0 text-secondary">
+ Slower (~40 blocks)
+ </div>
+ <div class="absolute top-5 right-0 text-secondary">
+ Faster (~1 block)
+ </div>
+ <Slider bind:value={feeRate} {handle} {min} {max} />
+ </div>
+ <div class="flex justify-center gap-2 mb-10">
+ <div class="my-auto">
+ <Toggle id="subtract" checked={subtract} bind:value={subtract} />
+ </div>
+ <label for="subtract" class="text-secondary text-lg my-auto mb-2"
+ >Take fee from amount</label
+ >
+ </div>
+ {/if}
- <div class="flex justify-center gap-2">
- {#if !confirmed}
- <button
- type="button"
- class="hover:opacity-80 bg-black text-white rounded-2xl py-3 px-4 mt-2 border font-bold"
- on:click|preventDefault={setMax}
- on:keydown={setMax}>Max ⚡️{s(balance)}</button
- >
- {/if}
+ <div class="flex justify-center gap-2">
+ {#if !confirmed}
+ <button
+ type="button"
+ class="hover:opacity-80 bg-black text-white rounded-2xl py-3 px-4 mt-2 border font-bold"
+ on:click|preventDefault={setMax}
+ on:keydown={setMax}>Max ⚡️{s(balance)}</button
+ >
+ {/if}
- <button
- bind:this={submit}
- type="submit"
- class="opacity-100 hover:opacity-80 rounded-2xl border py-3 font-bold mt-2 bg-black text-white px-4 w-24"
- disabled={loading}
- >
- {#if confirmed}
- {$t('payments.send')}
- {:else}
- {$t('payments.next')}
- {/if}
- </button>
- </div>
- </form>
+ <button
+ bind:this={submit}
+ type="submit"
+ class="opacity-100 hover:opacity-80 rounded-2xl border py-3 font-bold mt-2 bg-black text-white px-4 w-24"
+ disabled={loading}
+ >
+ {#if confirmed}
+ {$t("payments.send")}
+ {:else}
+ {$t("payments.next")}
+ {/if}
+ </button>
+ </div>
+ </form>
</div>
diff --git a/src/routes/(app)/send/fund/[name]/[...amount]/+page.server.js b/src/routes/(app)/send/fund/[name]/[...amount]/+page.server.js
index 2cb3c4a9..3318a60b 100644
--- a/src/routes/(app)/send/fund/[name]/[...amount]/+page.server.js
+++ b/src/routes/(app)/send/fund/[name]/[...amount]/+page.server.js
@@ -1,17 +1,17 @@
-import { fail, redirect } from '@sveltejs/kit';
-import { auth, fd, post } from '$lib/utils';
+import { fail, redirect } from "@sveltejs/kit";
+import { auth, fd, post } from "$lib/utils";
export const actions = {
- default: async ({ cookies, request }) => {
- let body = await fd(request);
- let p;
+ default: async ({ cookies, request }) => {
+ let body = await fd(request);
+ let p;
- try {
- p = await post('/payments', body, auth(cookies));
- } catch (e) {
- return fail(400, { message: e.message });
- }
+ try {
+ p = await post("/payments", body, auth(cookies));
+ } catch (e) {
+ return fail(400, { message: e.message });
+ }
- if (p) throw redirect(307, `/fund/${body.name}`);
- }
+ if (p) throw redirect(307, `/fund/${body.name}`);
+ },
};
diff --git a/src/routes/(app)/send/fund/[name]/[...amount]/+page.svelte b/src/routes/(app)/send/fund/[name]/[...amount]/+page.svelte
index 6aecf8f3..886e93d2 100644
--- a/src/routes/(app)/send/fund/[name]/[...amount]/+page.svelte
+++ b/src/routes/(app)/send/fund/[name]/[...amount]/+page.svelte
@@ -1,58 +1,58 @@
<script>
- import { t } from '$lib/translations';
- import { enhance } from '$app/forms';
- import { Icon, Numpad, Spinner } from '$comp';
- import { page } from '$app/stores';
- import { back } from '$lib/utils';
- import { pin } from '$lib/store';
+ import { t } from "$lib/translations";
+ import { enhance } from "$app/forms";
+ import { Icon, Numpad, Spinner } from "$comp";
+ import { page } from "$app/stores";
+ import { back } from "$lib/utils";
+ import { pin } from "$lib/store";
- export let data;
- export let form;
+ export let data;
+ export let form;
- let { amount, name } = $page.params;
- let { rates } = data;
- let { currency } = data.user;
- let loading;
+ let { amount, name } = $page.params;
+ let { rates } = data;
+ let { currency } = data.user;
+ let loading;
- $: rate = rates[currency];
+ $: rate = rates[currency];
- let submit = () => (loading = true);
- $: update(form);
- let update = () => {
- if (form?.message?.includes('pin')) $pin = undefined;
- loading = false;
- };
+ let submit = () => (loading = true);
+ $: update(form);
+ let update = () => {
+ if (form?.message?.includes("pin")) $pin = undefined;
+ loading = false;
+ };
</script>
<button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80" on:click={back}>
- <Icon icon="arrow-left" style="w-10" />
+ <Icon icon="arrow-left" style="w-10" />
</button>
{#if form?.message}
- <div class="text-red-600 text-center">
- {form.message}
- </div>
+ <div class="text-red-600 text-center">
+ {form.message}
+ </div>
{/if}
<div class="container px-4 mt-20 max-w-xl mx-auto">
- <Numpad bind:amount {currency} bind:rate />
+ <Numpad bind:amount {currency} bind:rate />
- <form method="POST" use:enhance on:submit={submit}>
- <input name="name" value={name} type="hidden" />
- <input name="amount" value={amount} type="hidden" />
- <input name="pin" value={$pin} type="hidden" />
+ <form method="POST" use:enhance on:submit={submit}>
+ <input name="name" value={name} type="hidden" />
+ <input name="amount" value={amount} type="hidden" />
+ <input name="pin" value={$pin} type="hidden" />
- <div class="flex w-full">
- <button
- type="submit"
- class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
- >
- {#if loading}
- <Spinner />
- {:else}
- {$t('payments.send')}
- {/if}
- </button>
- </div>
- </form>
+ <div class="flex w-full">
+ <button
+ type="submit"
+ class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
+ >
+ {#if loading}
+ <Spinner />
+ {:else}
+ {$t("payments.send")}
+ {/if}
+ </button>
+ </div>
+ </form>
</div>
diff --git a/src/routes/(app)/send/invoice/[id]/+page.server.js b/src/routes/(app)/send/invoice/[id]/+page.server.js
index 4cbbb03f..aa284328 100644
--- a/src/routes/(app)/send/invoice/[id]/+page.server.js
+++ b/src/routes/(app)/send/invoice/[id]/+page.server.js
@@ -1,37 +1,48 @@
-import { error, fail, redirect } from '@sveltejs/kit';
-import { fd, auth, get, post } from '$lib/utils';
+import { error, fail, redirect } from "@sveltejs/kit";
+import { fd, auth, get, post } from "$lib/utils";
export async function load({ params: { id }, parent, url }) {
- let { user } = await parent();
+ let { user } = await parent();
- let {
- amount,
- address,
- currency,
- hash,
- rate,
- prompt,
- tip,
- text,
- user: recipient
- } = await get(`/invoice/${id}`);
+ let {
+ amount,
+ address,
+ currency,
+ hash,
+ rate,
+ prompt,
+ tip,
+ text,
+ user: recipient,
+ } = await get(`/invoice/${id}`);
- if (prompt && tip === null) throw redirect(307, `/${recipient.username}/invoice/${id}/tip`);
- if (recipient.username === user.username) throw error(500, { message: 'Cannot send to self' });
- return { amount, address, currency, hash, tip, rate, payreq: text, recipient };
+ if (prompt && tip === null)
+ throw redirect(307, `/${recipient.username}/invoice/${id}/tip`);
+ if (recipient.username === user.username)
+ throw error(500, { message: "Cannot send to self" });
+ return {
+ amount,
+ address,
+ currency,
+ hash,
+ tip,
+ rate,
+ payreq: text,
+ recipient,
+ };
}
export const actions = {
- default: async ({ cookies, params: { id }, request }) => {
- try {
- let body = await fd(request);
- body.hash = id;
+ default: async ({ cookies, params: { id }, request }) => {
+ try {
+ let body = await fd(request);
+ body.hash = id;
- await post('/payments', body, auth(cookies));
- } catch (e) {
- return fail(400, { message: e.message });
- }
+ await post("/payments", body, auth(cookies));
+ } catch (e) {
+ return fail(400, { message: e.message });
+ }
- throw redirect(307, '/sent');
- }
+ throw redirect(307, "/sent");
+ },
};
diff --git a/src/routes/(app)/send/invoice/[id]/+page.svelte b/src/routes/(app)/send/invoice/[id]/+page.svelte
index 02ab9d41..d29f1c96 100644
--- a/src/routes/(app)/send/invoice/[id]/+page.svelte
+++ b/src/routes/(app)/send/invoice/[id]/+page.svelte
@@ -1,154 +1,168 @@
<script>
- import { onDestroy, onMount } from 'svelte';
- import { t } from '$lib/translations';
- import { goto, invalidateAll } from '$app/navigation';
- import { pin } from '$lib/store';
- import { enhance } from '$app/forms';
- import { Avatar, Icon, Numpad, Spinner } from '$comp';
- import { page } from '$app/stores';
- import { post, back, f, fiat as toFiat, s, sats } from '$lib/utils';
- export let data;
- export let form;
+ import { onDestroy, onMount } from "svelte";
+ import { t } from "$lib/translations";
+ import { goto, invalidateAll } from "$app/navigation";
+ import { pin } from "$lib/store";
+ import { enhance } from "$app/forms";
+ import { Avatar, Icon, Numpad, Spinner } from "$comp";
+ import { page } from "$app/stores";
+ import { post, back, f, fiat as toFiat, s, sats } from "$lib/utils";
+ export let data;
+ export let form;
- let { address, hash, rate, payreq, recipient, tip, rates, user } = data;
- let { currency } = user;
+ let { address, hash, rate, payreq, recipient, tip, rates, user } = data;
+ let { currency } = user;
- let amount = form?.amount || data.amount;
- let a,
- af,
- amountFiat = amount * (rate / sats),
- fiat = !amount;
+ let amount = form?.amount || data.amount;
+ let a,
+ af,
+ amountFiat = amount * (rate / sats),
+ fiat = !amount;
- let setAmount = () => {
- amount = a;
- amountFiat = af;
- };
+ let setAmount = () => {
+ amount = a;
+ amountFiat = af;
+ };
- let loading;
- let submit = () => (loading = true);
+ let loading;
+ let submit = () => (loading = true);
- let external = async () => {
- let invoice = { type: 'lightning', amount, currency };
- let { id } = await post(`/${recipient.username}/invoice`, {
- invoice,
- user: { username: recipient.username }
- });
+ let external = async () => {
+ let invoice = { type: "lightning", amount, currency };
+ let { id } = await post(`/${recipient.username}/invoice`, {
+ invoice,
+ user: { username: recipient.username },
+ });
- goto(`/${recipient.username}/invoice/${id}?options=true`, { invalidateAll: true });
- };
+ goto(`/${recipient.username}/invoice/${id}?options=true`, {
+ invalidateAll: true,
+ });
+ };
- $: rate = data.rate * (rates[user.currency] / rates[data.currency]);
+ $: rate = data.rate * (rates[user.currency] / rates[data.currency]);
- $: update(form);
- let update = () => {
- if (form?.message.includes('pin')) $pin = undefined;
- loading = false;
- };
+ $: update(form);
+ let update = () => {
+ if (form?.message.includes("pin")) $pin = undefined;
+ loading = false;
+ };
- // onMount(async () => {
- // if (browser && window.NDEFReader) {
- // try {
- // let ndef = new NDEFReader();
- // await ndef.scan();
- //
- // ndef.addEventListener('readingerror', (e) => {
- // console.log('nfc error', e);
- // });
- //
- // ndef.addEventListener('reading', ({ message, serialNumber }) => {
- // console.log(message, serialNumber);
- // });
- // } catch (e) {
- // console.log('NFC error', e);
- // }
- // }
- // });
+ // onMount(async () => {
+ // if (browser && window.NDEFReader) {
+ // try {
+ // let ndef = new NDEFReader();
+ // await ndef.scan();
+ //
+ // ndef.addEventListener('readingerror', (e) => {
+ // console.log('nfc error', e);
+ // });
+ //
+ // ndef.addEventListener('reading', ({ message, serialNumber }) => {
+ // console.log(message, serialNumber);
+ // });
+ // } catch (e) {
+ // console.log('NFC error', e);
+ // }
+ // }
+ // });
</script>
<button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80" on:click={back}>
- <Icon icon="arrow-left" style="w-10" />
+ <Icon icon="arrow-left" style="w-10" />
</button>
{#if form?.message}
- <div class="text-red-600 text-center">
- {form.message}
- </div>
+ <div class="text-red-600 text-center">
+ {form.message}
+ </div>
{/if}
<div class="container px-4 mt-20 max-w-xl mx-auto">
- {#if amount}
- <div class="text-center mb-8">
- <h1 class="text-xl md:text-2xl text-secondary mb-2">
- {$t('payments.send')}
- </h1>
- <h2 class="text-2xl md:text-3xl font-semibold">
- {f(toFiat(amount, rate), currency)}
- {#if tip}
- <span class="text-lg">
- + {f(toFiat(tip, rate), currency)}
- </span>
- {/if}
- </h2>
- <h3 class="text-secondary md:text-lg mb-6 mt-1">
- ⚡️{s(amount)}
+ {#if amount}
+ <div class="text-center mb-8">
+ <h1 class="text-xl md:text-2xl text-secondary mb-2">
+ {$t("payments.send")}
+ </h1>
+ <h2 class="text-2xl md:text-3xl font-semibold">
+ {f(toFiat(amount, rate), currency)}
+ {#if tip}
+ <span class="text-lg">
+ + {f(toFiat(tip, rate), currency)}
+ </span>
+ {/if}
+ </h2>
+ <h3 class="text-secondary md:text-lg mb-6 mt-1">
+ ⚡️{s(amount)}
- {#if tip}
- <span class="text-lg">
- + ⚡️{s(tip)}
- </span>
- {/if}
- </h3>
+ {#if tip}
+ <span class="text-lg">
+ + ⚡️{s(tip)}
+ </span>
+ {/if}
+ </h3>
- <h1 class="text-xl md:text-2xl text-secondary mb-2">{$t('payments.to')}</h1>
+ <h1 class="text-xl md:text-2xl text-secondary mb-2">
+ {$t("payments.to")}
+ </h1>
- <div class="flex p-1 gap-2 justify-center">
- {#if recipient.username.includes('@classic')}
- <img
- src="/images/classic.png"
- class="w-24 border-4 border-transparent mr-1"
- alt="Bitcoin"
- />
- {:else}
- <Avatar user={recipient} size={'20'} />
- {/if}
- <p class="text-4xl break-words my-auto">{recipient.username.replace('@classic', '')}</p>
- </div>
- </div>
- {:else}
- <Numpad bind:amount={a} bind:amountFiat={af} {currency} bind:fiat bind:rate />
- {/if}
+ <div class="flex p-1 gap-2 justify-center">
+ {#if recipient.username.includes("@classic")}
+ <img
+ src="/images/classic.png"
+ class="w-24 border-4 border-transparent mr-1"
+ alt="Bitcoin"
+ />
+ {:else}
+ <Avatar user={recipient} size={"20"} />
+ {/if}
+ <p class="text-4xl break-words my-auto">
+ {recipient.username.replace("@classic", "")}
+ </p>
+ </div>
+ </div>
+ {:else}
+ <Numpad
+ bind:amount={a}
+ bind:amountFiat={af}
+ {currency}
+ bind:fiat
+ bind:rate
+ />
+ {/if}
- <form method="POST" use:enhance on:submit={submit}>
- <input name="address" value={address} type="hidden" />
- <input name="amount" value={amount} type="hidden" />
- <input name="payreq" value={payreq} type="hidden" />
- <input name="username" value={recipient.username} type="hidden" />
- <input name="pin" value={$pin} type="hidden" />
+ <form method="POST" use:enhance on:submit={submit}>
+ <input name="address" value={address} type="hidden" />
+ <input name="amount" value={amount} type="hidden" />
+ <input name="payreq" value={payreq} type="hidden" />
+ <input name="username" value={recipient.username} type="hidden" />
+ <input name="pin" value={$pin} type="hidden" />
- <div class="flex w-full">
- {#if amount}
- <button
- type="submit"
- class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
- >
- {#if loading}
- <Spinner />
- {:else}
- {$t('payments.send')}
- {/if}
- </button>
- {:else}
- <button
- type="button"
- class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
- on:click={setAmount}
- >
- {$t('payments.next')}
- </button>
- {/if}
- </div>
- </form>
- <div class="flex my-20">
- <button class="mx-auto" on:click={external}>{$t('payments.moreOptions')}</button>
- </div>
+ <div class="flex w-full">
+ {#if amount}
+ <button
+ type="submit"
+ class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
+ >
+ {#if loading}
+ <Spinner />
+ {:else}
+ {$t("payments.send")}
+ {/if}
+ </button>
+ {:else}
+ <button
+ type="button"
+ class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
+ on:click={setAmount}
+ >
+ {$t("payments.next")}
+ </button>
+ {/if}
+ </div>
+ </form>
+ <div class="flex my-20">
+ <button class="mx-auto" on:click={external}
+ >{$t("payments.moreOptions")}</button
+ >
+ </div>
</div>
diff --git a/src/routes/(app)/send/lightning/[payreq]/+page.server.js b/src/routes/(app)/send/lightning/[payreq]/+page.server.js
index d358bbca..d9e791e1 100644
--- a/src/routes/(app)/send/lightning/[payreq]/+page.server.js
+++ b/src/routes/(app)/send/lightning/[payreq]/+page.server.js
@@ -1,24 +1,24 @@
-import { fail, redirect } from '@sveltejs/kit';
-import { fd, auth, post } from '$lib/utils';
+import { fail, redirect } from "@sveltejs/kit";
+import { fd, auth, post } from "$lib/utils";
export async function load({ params }) {
- return post('/parse', params);
+ return post("/parse", params);
}
export const actions = {
- setAmount: async ({ cookies, request }) => fd(request),
+ setAmount: async ({ cookies, request }) => fd(request),
- send: async ({ cookies, request }) => {
- try {
- let body = await fd(request);
+ send: async ({ cookies, request }) => {
+ try {
+ let body = await fd(request);
- await post('/payments', body, auth(cookies));
- } catch (e) {
- if (e.message.includes('unusable'))
- e.message = 'Failed to route payment, try sending a lower amount';
- return fail(400, { message: e.message });
- }
+ await post("/payments", body, auth(cookies));
+ } catch (e) {
+ if (e.message.includes("unusable"))
+ e.message = "Failed to route payment, try sending a lower amount";
+ return fail(400, { message: e.message });
+ }
- throw redirect(307, '/sent');
- }
+ throw redirect(307, "/sent");
+ },
};
diff --git a/src/routes/(app)/send/lightning/[payreq]/+page.svelte b/src/routes/(app)/send/lightning/[payreq]/+page.svelte
index e232ef05..185597c7 100644
--- a/src/routes/(app)/send/lightning/[payreq]/+page.svelte
+++ b/src/routes/(app)/send/lightning/[payreq]/+page.svelte
@@ -1,105 +1,120 @@
<script>
- import { t } from '$lib/translations';
- import { enhance } from '$app/forms';
- import { Icon, Numpad, Spinner } from '$comp';
- import { page } from '$app/stores';
- import { back, s } from '$lib/utils';
- import { pin } from '$lib/store';
+ import { t } from "$lib/translations";
+ import { enhance } from "$app/forms";
+ import { Icon, Numpad, Spinner } from "$comp";
+ import { page } from "$app/stores";
+ import { back, s } from "$lib/utils";
+ import { pin } from "$lib/store";
- export let data;
- export let form;
+ export let data;
+ export let form;
- let { payreq } = $page.params;
- let { alias, rates } = data;
+ let { payreq } = $page.params;
+ let { alias, rates } = data;
- let { currency } = data.user;
- let a;
- let maxfee = 500;
+ let { currency } = data.user;
+ let a;
+ let maxfee = 500;
- $: amount = form?.amount || data.amount;
- $: rate = rates[currency];
+ $: amount = form?.amount || data.amount;
+ $: rate = rates[currency];
- let loading;
- let submit = () => (loading = true);
- $: update(form);
- let update = () => {
- if (form?.message?.includes('pin')) $pin = undefined;
- loading = false;
- };
+ let loading;
+ let submit = () => (loading = true);
+ $: update(form);
+ let update = () => {
+ if (form?.message?.includes("pin")) $pin = undefined;
+ loading = false;
+ };
- let show;
- let toggle = () => (show = !show);
+ let show;
+ let toggle = () => (show = !show);
</script>
<button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80" on:click={back}>
- <Icon icon="arrow-left" style="w-10" />
+ <Icon icon="arrow-left" style="w-10" />
</button>
{#if form?.message}
- <div class="text-red-600 text-center">
- {form.message}
- </div>
+ <div class="text-red-600 text-center">
+ {form.message}
+ </div>
{/if}
<div class="container px-4 mt-20 max-w-xl mx-auto">
- {#if amount}
- <div class="text-center mb-8">
- <h1 class="text-xl md:text-2xl text-secondary mb-2">{$t('payments.send')}</h1>
- <p class="text-6xl break-words mb-4">
- ⚡️{s(amount)}
- </p>
- <h1 class="text-xl md:text-2xl text-secondary mb-2">{$t('payments.to')}</h1>
- <p class="text-4xl break-words">{alias}</p>
- </div>
+ {#if amount}
+ <div class="text-center mb-8">
+ <h1 class="text-xl md:text-2xl text-secondary mb-2">
+ {$t("payments.send")}
+ </h1>
+ <p class="text-6xl break-words mb-4">
+ ⚡️{s(amount)}
+ </p>
+ <h1 class="text-xl md:text-2xl text-secondary mb-2">
+ {$t("payments.to")}
+ </h1>
+ <p class="text-4xl break-words">{alias}</p>
+ </div>
- <form method="POST" use:enhance on:submit={submit} action="?/send" class="space-y-5">
- <input name="payreq" value={payreq} type="hidden" />
- <input name="amount" value={amount} type="hidden" />
- <input name="pin" value={$pin} type="hidden" />
- <input name="maxfee" value={maxfee} type="hidden" />
+ <form
+ method="POST"
+ use:enhance
+ on:submit={submit}
+ action="?/send"
+ class="space-y-5"
+ >
+ <input name="payreq" value={payreq} type="hidden" />
+ <input name="amount" value={amount} type="hidden" />
+ <input name="pin" value={$pin} type="hidden" />
+ <input name="maxfee" value={maxfee} type="hidden" />
- <div class="flex w-full">
- <button
- type="submit"
- class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
- disabled={loading}
- >
- {#if loading}
- <Spinner />
- {:else}
- {$t('payments.send')}
- {/if}
- </button>
- </div>
- </form>
+ <div class="flex w-full">
+ <button
+ type="submit"
+ class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
+ disabled={loading}
+ >
+ {#if loading}
+ <Spinner />
+ {:else}
+ {$t("payments.send")}
+ {/if}
+ </button>
+ </div>
+ </form>
- {#if show}
- <div class="relative w-96 mx-auto">
- <label for="maxfee">Max fee</label>
- <input name="maxfee" bind:value={maxfee} />
+ {#if show}
+ <div class="relative w-96 mx-auto">
+ <label for="maxfee">Max fee</label>
+ <input name="maxfee" bind:value={maxfee} />
- <div
- class="absolute right-[2px] top-[25px] text-gray-600 rounded-r-2xl p-4 h-[54px] my-auto border-l "
- >
- ⚡️
- </div>
- </div>
- {:else}
- <div class="flex w-full mt-5">
- <button class="mx-auto" on:click={toggle}>Settings</button>
- </div>
- {/if}
- {:else}
- <form method="POST" action="?/setAmount" class="w-[300px] mx-auto" use:enhance>
- <input type="hidden" value={a} name="amount" />
- <Numpad bind:amount={a} {currency} {rate} />
- <button
- type="submit"
- class="bg-black text-white rounded-xl h-[48px] flex w-full justify-center items-center font-semibold
+ <div
+ class="absolute right-[2px] top-[25px] text-gray-600 rounded-r-2xl p-4 h-[54px] my-auto border-l"
+ >
+ ⚡️
+ </div>
+ </div>
+ {:else}
+ <div class="flex w-full mt-5">
+ <button class="mx-auto" on:click={toggle}>Settings</button>
+ </div>
+ {/if}
+ {:else}
+ <form
+ method="POST"
+ action="?/setAmount"
+ class="w-[300px] mx-auto"
+ use:enhance
+ >
+ <input type="hidden" value={a} name="amount" />
+ <Numpad bind:amount={a} {currency} {rate} />
+ <button
+ type="submit"
+ class="bg-black text-white rounded-xl h-[48px] flex w-full justify-center items-center font-semibold
opacity-100 hover:opacity-80"
- >
- Next
- </button>
- </form>
- {/if}
+ >
+ Next
+ </button>
+ </form>
+ {/if}
</div>
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 a01cdcfc..1f3a0420 100644
--- a/src/routes/(app)/send/user/[username]/[...amount]/+page.server.js
+++ b/src/routes/(app)/send/user/[username]/[...amount]/+page.server.js
@@ -1,46 +1,47 @@
-import { error, fail, redirect } from '@sveltejs/kit';
-import { types, sats, fd, auth, get, post } from '$lib/utils';
+import { error, fail, redirect } from "@sveltejs/kit";
+import { types, sats, fd, auth, get, post } from "$lib/utils";
export async function load({ cookies, params, parent }) {
- let subject = await get(`/users/${params.username}`);
+ let subject = await get(`/users/${params.username}`);
- let { rates, user } = await parent();
- let { username } = params;
- let [amount, currency] = params.amount.split('/');
+ let { rates, user } = await parent();
+ let { username } = params;
+ let [amount, currency] = params.amount.split("/");
- if (subject.username === user?.username) throw error(500, { message: 'Cannot send to self' });
+ if (subject.username === user?.username)
+ throw error(500, { message: "Cannot send to self" });
- let rate;
- if (amount) {
- rate = rates[currency ? currency.toUpperCase() : subject.currency];
- if (!rate) throw error(500, 'Invalid currency symbol');
- if (currency) amount = (amount * sats) / rate;
+ let rate;
+ if (amount) {
+ rate = rates[currency ? currency.toUpperCase() : subject.currency];
+ if (!rate) throw error(500, "Invalid currency symbol");
+ if (currency) amount = (amount * sats) / rate;
- let { hash } = await post(
- '/invoice',
- {
- invoice: { amount, prompt: false, type: types.lightning },
- user: { username }
- },
- auth(cookies)
- );
+ let { hash } = await post(
+ "/invoice",
+ {
+ invoice: { amount, prompt: false, type: types.lightning },
+ user: { username },
+ },
+ auth(cookies)
+ );
- throw redirect(307, `/${username}/invoice/${hash}`);
- }
+ throw redirect(307, `/${username}/invoice/${hash}`);
+ }
- return { amount, rate, subject };
+ return { amount, rate, subject };
}
export const actions = {
- default: async ({ cookies, request }) => {
- try {
- let body = await fd(request);
- await post('/payments', body, auth(cookies));
- } catch (e) {
- console.log(e);
- return fail(400, { message: e.message });
- }
+ default: async ({ cookies, request }) => {
+ try {
+ let body = await fd(request);
+ await post("/payments", body, auth(cookies));
+ } catch (e) {
+ console.log(e);
+ return fail(400, { message: e.message });
+ }
- throw redirect(307, '/sent');
- }
+ throw redirect(307, "/sent");
+ },
};
diff --git a/src/routes/(app)/send/user/[username]/[...amount]/+page.svelte b/src/routes/(app)/send/user/[username]/[...amount]/+page.svelte
index 32a13939..cb458323 100644
--- a/src/routes/(app)/send/user/[username]/[...amount]/+page.svelte
+++ b/src/routes/(app)/send/user/[username]/[...amount]/+page.svelte
@@ -1,103 +1,103 @@
<script>
- import { t } from '$lib/translations';
- import { goto } from '$app/navigation';
- import { pin } from '$lib/store';
- import { enhance } from '$app/forms';
- import { AppHeader, Avatar, Icon, Numpad, Spinner } from '$comp';
- import { page } from '$app/stores';
- import { f, post, s, sat, sats } from '$lib/utils';
+ import { t } from "$lib/translations";
+ import { goto } from "$app/navigation";
+ import { pin } from "$lib/store";
+ import { enhance } from "$app/forms";
+ import { AppHeader, Avatar, Icon, Numpad, Spinner } from "$comp";
+ import { page } from "$app/stores";
+ import { f, post, s, sat, sats } from "$lib/utils";
- export let data;
- export let form;
+ export let data;
+ export let form;
- let { subject, rate, rates, user } = data;
- let currency = user.currency || subject.currency || 'USD';
- let next;
+ let { subject, rate, rates, user } = data;
+ let currency = user.currency || subject.currency || "USD";
+ let next;
- let amount = form?.amount || data.amount;
- let a,
- af,
- fiat = !amount,
- hash,
- amountFiat = 0;
+ let amount = form?.amount || data.amount;
+ let a,
+ af,
+ fiat = !amount,
+ hash,
+ amountFiat = 0;
- $: r = rate || rates[user.currency || subject.currency];
+ $: r = rate || rates[user.currency || subject.currency];
- let setAmount = async () => {
- amount = a;
- amountFiat = parseFloat(af).toFixed(2);
- rate = fiat ? (sats * amountFiat) / amount : r;
- ({ hash } = await post(`/${subject.username}/invoice`, {
- invoice: {
- amount,
- hash: 'internal',
- rate: rates[subject.currency],
- prompt: subject.prompt,
- type: 'internal'
- },
- user: subject
- }));
+ let setAmount = async () => {
+ amount = a;
+ amountFiat = parseFloat(af).toFixed(2);
+ rate = fiat ? (sats * amountFiat) / amount : r;
+ ({ hash } = await post(`/${subject.username}/invoice`, {
+ invoice: {
+ amount,
+ hash: "internal",
+ rate: rates[subject.currency],
+ prompt: subject.prompt,
+ type: "internal",
+ },
+ user: subject,
+ }));
- goto(`/${subject.username}/invoice/${hash}`);
- };
+ goto(`/${subject.username}/invoice/${hash}`);
+ };
- let loading;
- let submit = () => (loading = true);
+ let loading;
+ let submit = () => (loading = true);
- $: update(form);
- let update = () => {
- if (form?.message?.includes('pin')) $pin = undefined;
- loading = false;
- };
+ $: update(form);
+ let update = () => {
+ if (form?.message?.includes("pin")) $pin = undefined;
+ loading = false;
+ };
</script>
<AppHeader {data} />
{#if form?.message}
- <div class="text-red-600 text-center">
- {form.message}
- </div>
+ <div class="text-red-600 text-center">
+ {form.message}
+ </div>
{/if}
<div class="container px-4 mt-20 max-w-xl mx-auto space-y-8">
- <Numpad
- bind:amount={a}
- bind:amountFiat={af}
- {currency}
- bind:fiat
- bind:rate={r}
- bind:submit={next}
- />
+ <Numpad
+ bind:amount={a}
+ bind:amountFiat={af}
+ {currency}
+ bind:fiat
+ bind:rate={r}
+ bind:submit={next}
+ />
- <form method="POST" use:enhance on:submit={submit}>
- <input name="amount" value={amount} type="hidden" />
- <input name="username" value={subject.username} type="hidden" />
- <input name="pin" value={$pin} type="hidden" />
- <input name="hash" value={hash} type="hidden" />
+ <form method="POST" use:enhance on:submit={submit}>
+ <input name="amount" value={amount} type="hidden" />
+ <input name="username" value={subject.username} type="hidden" />
+ <input name="pin" value={$pin} type="hidden" />
+ <input name="hash" value={hash} type="hidden" />
- <div class="flex w-full">
- {#if amount}
- <button
- type="submit"
- class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
- >
- {#if loading}
- <Spinner />
- {:else}
- {$t('payments.send')}
- {/if}
- </button>
- {:else}
- <button
- type="button"
- bind:this={next}
- class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
- on:click={setAmount}
- on:keydown={setAmount}
- >
- Next
- </button>
- {/if}
- </div>
- </form>
+ <div class="flex w-full">
+ {#if amount}
+ <button
+ type="submit"
+ class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
+ >
+ {#if loading}
+ <Spinner />
+ {:else}
+ {$t("payments.send")}
+ {/if}
+ </button>
+ {:else}
+ <button
+ type="button"
+ bind:this={next}
+ class="opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold mx-auto mt-2 bg-black text-white px-4 w-24"
+ on:click={setAmount}
+ on:keydown={setAmount}
+ >
+ Next
+ </button>
+ {/if}
+ </div>
+ </form>
</div>
diff --git a/src/routes/(app)/sent/+page.server.js b/src/routes/(app)/sent/+page.server.js
index 381ff32f..62a4d9b4 100644
--- a/src/routes/(app)/sent/+page.server.js
+++ b/src/routes/(app)/sent/+page.server.js
@@ -1,6 +1,6 @@
-import { get, auth } from '$lib/utils';
+import { get, auth } from "$lib/utils";
export let load = async ({ cookies }) => {
- let { payments } = await get('/payments', auth(cookies));
- return { payments };
+ let { payments } = await get("/payments", auth(cookies));
+ return { payments };
};
diff --git a/src/routes/(app)/sent/+page.svelte b/src/routes/(app)/sent/+page.svelte
index dbd18c2e..400e8904 100644
--- a/src/routes/(app)/sent/+page.svelte
+++ b/src/routes/(app)/sent/+page.svelte
@@ -1,47 +1,52 @@
<script>
- import { fiat, f, s, sat, sats } from '$lib/utils';
- import { scale } from 'svelte/transition';
- import { Icon } from '$comp';
- import { toast } from '@zerodevx/svelte-toast';
- import { t } from '$lib/translations';
+ import { fiat, f, s, sat, sats } from "$lib/utils";
+ import { scale } from "svelte/transition";
+ import { Icon } from "$comp";
+ import { toast } from "@zerodevx/svelte-toast";
+ import { t } from "$lib/translations";
- export let data;
- let { payments, user } = data;
- let { currency, username } = user;
+ export let data;
+ let { payments, user } = data;
+ let { currency, username } = user;
- let payment = payments[0];
- let { amount, rate, tip } = payment;
- amount = Math.abs(amount);
+ let payment = payments[0];
+ let { amount, rate, tip } = payment;
+ amount = Math.abs(amount);
- toast.pop(0);
+ toast.pop(0);
</script>
<div class="text-center mt-20 md:mt-0">
- <div class="flex w-full max-w-[200px] mx-auto py-20" in:scale={{ start: 0.5 }}>
- <Icon icon="check" style="mx-auto" />
- </div>
- <h1 class="text-3xl md:text-4xl font-bold mb-6">{$t('payments.sent')}!</h1>
- <h2 class="text-2xl md:text-3xl font-semibold">
- {f(fiat(amount, rate), currency)}
- {#if tip}
- <span class="text-lg">
- + {f(fiat(tip, rate), currency)}
- </span>
- {/if}
- </h2>
- <h3 class="text-secondary md:text-lg mb-6 mt-1">
- ⚡️{s(amount)}
+ <div
+ class="flex w-full max-w-[200px] mx-auto py-20"
+ in:scale={{ start: 0.5 }}
+ >
+ <Icon icon="check" style="mx-auto" />
+ </div>
+ <h1 class="text-3xl md:text-4xl font-bold mb-6">{$t("payments.sent")}!</h1>
+ <h2 class="text-2xl md:text-3xl font-semibold">
+ {f(fiat(amount, rate), currency)}
+ {#if tip}
+ <span class="text-lg">
+ + {f(fiat(tip, rate), currency)}
+ </span>
+ {/if}
+ </h2>
+ <h3 class="text-secondary md:text-lg mb-6 mt-1">
+ ⚡️{s(amount)}
- {#if tip}
- <span class="text-lg">
- + ⚡️{s(tip)}
- </span>
- {/if}
- </h3>
+ {#if tip}
+ <span class="text-lg">
+ + ⚡️{s(tip)}
+ </span>
+ {/if}
+ </h3>
</div>
<a href={`/${username}/payments`}>
- <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" />
+ <div class="opacity-0 w-screen h-screen fixed top-0 left-0 z-50" />
</a>
-<div class="fixed bottom-10 w-full text-center">{$t('payments.tapAnywhere')}</div>
+<div class="fixed bottom-10 w-full text-center">
+ {$t("payments.tapAnywhere")}
+</div>
diff --git a/src/routes/(app)/support/+page.svelte b/src/routes/(app)/support/+page.svelte
index b2f82e89..b31b4389 100644
--- a/src/routes/(app)/support/+page.svelte
+++ b/src/routes/(app)/support/+page.svelte
@@ -1,122 +1,138 @@
<script>
- import { onDestroy } from 'svelte';
- import { fail, success, post } from '$lib/utils';
- import { scale } from 'svelte/transition';
- import { AppHeader, Icon } from '$comp';
- import { t } from '$lib/translations';
+ import { onDestroy } from "svelte";
+ import { fail, success, post } from "$lib/utils";
+ import { scale } from "svelte/transition";
+ import { AppHeader, Icon } from "$comp";
+ import { t } from "$lib/translations";
- export let data;
+ export let data;
- let { user } = data;
- $: if (user) {
- data.subject = { ...user };
- }
+ let { user } = data;
+ $: if (user) {
+ data.subject = { ...user };
+ }
- let email;
- let message;
- let sent;
- let recaptchaSiteKey = '6LfCd8YkAAAAANmVJgzN3SQY3n3fv1RhiS5PgMYM';
+ let email;
+ let message;
+ let sent;
+ let recaptchaSiteKey = "6LfCd8YkAAAAANmVJgzN3SQY3n3fv1RhiS5PgMYM";
- let submit = (e) => {
- e.preventDefault();
- grecaptcha.ready(() => {
- grecaptcha.execute(recaptchaSiteKey, { action: 'submit' }).then((token) =>
- post('/support', { username: user?.username, email, message, token })
- .then(() => (sent = true))
- .catch(() => fail('problem submitting'))
- );
- });
- };
+ let submit = (e) => {
+ e.preventDefault();
+ grecaptcha.ready(() => {
+ grecaptcha.execute(recaptchaSiteKey, { action: "submit" }).then((token) =>
+ post("/support", { username: user?.username, email, message, token })
+ .then(() => (sent = true))
+ .catch(() => fail("problem submitting"))
+ );
+ });
+ };
- onDestroy(() => {
- const nodeBadge = document.querySelector('.grecaptcha-badge');
- if (nodeBadge) {
- document.body.removeChild(nodeBadge.parentNode);
- }
+ onDestroy(() => {
+ const nodeBadge = document.querySelector(".grecaptcha-badge");
+ if (nodeBadge) {
+ document.body.removeChild(nodeBadge.parentNode);
+ }
- const scriptSelector =
- "script[src='https://www.google.com/recaptcha/api.js?render=" + recaptchaSiteKey + "']";
- const script = document.querySelector(scriptSelector);
- if (script) {
- script.remove();
- }
- });
+ const scriptSelector =
+ "script[src='https://www.google.com/recaptcha/api.js?render=" +
+ recaptchaSiteKey +
+ "']";
+ const script = document.querySelector(scriptSelector);
+ if (script) {
+ script.remove();
+ }
+ });
</script>
<svelte:head>
- <script
- src="https://www.google.com/recaptcha/api.js?render=6LfCd8YkAAAAANmVJgzN3SQY3n3fv1RhiS5PgMYM"
- ></script>
+ <script
+ src="https://www.google.com/recaptcha/api.js?render=6LfCd8YkAAAAANmVJgzN3SQY3n3fv1RhiS5PgMYM"
+ ></script>
</svelte:head>
{#if user}
- <AppHeader {data} />
+ <AppHeader {data} />
{/if}
<div class="container px-4 max-w-lg mx-auto mt-20 space-y-8">
- {#if sent}
- <h1 class="text-center text-3xl md:text-4xl font-semibold">Thank you!</h1>
- <div class="text-center text-lg text-secondary">Someone will be in touch shortly.</div>
+ {#if sent}
+ <h1 class="text-center text-3xl md:text-4xl font-semibold">Thank you!</h1>
+ <div class="text-center text-lg text-secondary">
+ Someone will be in touch shortly.
+ </div>
- <div>
- <a href={user ? `/${user.username}` : '/'}>
- <button
- class="rounded-full border py-2 px-5 font-bold hover:opacity-80 w-full bg-black text-white"
- >Ok</button
- >
- </a>
- </div>
- {:else}
- <h1 class="text-center text-3xl md:text-4xl font-semibold mb-8">
- {$t('user.support.header')}
- </h1>
+ <div>
+ <a href={user ? `/${user.username}` : "/"}>
+ <button
+ class="rounded-full border py-2 px-5 font-bold hover:opacity-80 w-full bg-black text-white"
+ >Ok</button
+ >
+ </a>
+ </div>
+ {:else}
+ <h1 class="text-center text-3xl md:text-4xl font-semibold mb-8">
+ {$t("user.support.header")}
+ </h1>
- <p class="text-secondary text-lg">
- Fill out this form or email us directly at <a
- class="underline"
- > and we'll do our best to get back to you in a timely manner.
- </p>
+ <p class="text-secondary text-lg">
+ Fill out this form or email us directly at <a
+ class="underline"
+ > and we'll do our best to get back to you in a timely manner.
+ </p>
- <form on:submit={submit}>
- <div class="mb-4">
- <label for="account" class="font-semibold">{$t('user.support.accountName')}</label>
- <input
- class="bg-primary"
- type="text"
- name="account"
- required
- value={user?.username || ''}
- />
- </div>
+ <form on:submit={submit}>
+ <div class="mb-4">
+ <label for="account" class="font-semibold"
+ >{$t("user.support.accountName")}</label
+ >
+ <input
+ class="bg-primary"
+ type="text"
+ name="account"
+ required
+ value={user?.username || ""}
+ />
+ </div>
- <div class="mb-4">
- <label for="email" class="font-semibold">{$t('user.support.email')}</label>
- <input class="bg-primary" type="email" name="email" bind:value={email} required />
- </div>
+ <div class="mb-4">
+ <label for="email" class="font-semibold"
+ >{$t("user.support.email")}</label
+ >
+ <input
+ class="bg-primary"
+ type="email"
+ name="email"
+ bind:value={email}
+ required
+ />
+ </div>
- <div class="mb-4">
- <label for="message" class="font-semibold">{$t('user.support.message')}</label>
- <textarea
- rows={5}
- class="bg-primary"
- type="text"
- name="message"
- bind:value={message}
- required
- />
- </div>
+ <div class="mb-4">
+ <label for="message" class="font-semibold"
+ >{$t("user.support.message")}</label
+ >
+ <textarea
+ rows={5}
+ class="bg-primary"
+ type="text"
+ name="message"
+ bind:value={message}
+ required
+ />
+ </div>
- <button
- type="submit"
- disabled={!email || !message}
- class="g-recaptcha {!email || !message
- ? 'opacity-50'
- : 'opacity-100'} bg-black text-white font-bold rounded-xl py-3 w-full mx-auto {email &&
- message
- ? 'hover:opacity-80'
- : ''}">{$t('user.support.send')}</button
- >
- </form>
- {/if}
+ <button
+ type="submit"
+ disabled={!email || !message}
+ class="g-recaptcha {!email || !message
+ ? 'opacity-50'
+ : 'opacity-100'} bg-black text-white font-bold rounded-xl py-3 w-full mx-auto {email &&
+ message
+ ? 'hover:opacity-80'
+ : ''}">{$t("user.support.send")}</button
+ >
+ </form>
+ {/if}
</div>
diff --git a/src/routes/(app)/support/+server.js b/src/routes/(app)/support/+server.js
index 6b1778a8..e9a16cfc 100644
--- a/src/routes/(app)/support/+server.js
+++ b/src/routes/(app)/support/+server.js
@@ -1,9 +1,9 @@
-import { json } from '@sveltejs/kit';
-import { auth, post } from '$lib/utils';
+import { json } from "@sveltejs/kit";
+import { auth, post } from "$lib/utils";
export const prerender = false;
export async function POST({ cookies, request }) {
- let result = await post('/email', await request.json(), auth(cookies));
- return json(result);
+ let result = await post("/email", await request.json(), auth(cookies));
+ return json(result);
}
diff --git a/src/routes/(app)/withdraw/+server.js b/src/routes/(app)/withdraw/+server.js
index 7b1ff4c6..a75a72f8 100644
--- a/src/routes/(app)/withdraw/+server.js
+++ b/src/routes/(app)/withdraw/+server.js
@@ -1,9 +1,10 @@
-import { auth, post } from '$lib/utils';
+import { auth, post } from "$lib/utils";
export async function POST({ cookies, request }) {
- let body = await request.json();
- await post('/payments', body, auth(cookies));
+ let body = await request.json();
+ await post("/payments", body, auth(cookies));
- return new Response(JSON.stringify('ok'), { headers: { 'content-type': 'application/json' } });
+ return new Response(JSON.stringify("ok"), {
+ headers: { "content-type": "application/json" },
+ });
}
-
diff --git a/src/routes/(static)/+layout.server.js b/src/routes/(static)/+layout.server.js
index 2deeebc5..81d1d3dc 100644
--- a/src/routes/(static)/+layout.server.js
+++ b/src/routes/(static)/+layout.server.js
@@ -1,16 +1,16 @@
-import { get, post, auth, protectedRoutes } from '$lib/utils';
+import { get, post, auth, protectedRoutes } from "$lib/utils";
export async function load({ cookies, request, url, params }) {
- let token = cookies.get('token');
- let rate,
- rates = { USD: 1 };
+ let token = cookies.get("token");
+ let rate,
+ rates = { USD: 1 };
- let user;
- if (token) {
- try {
- user = await get('/me', auth(cookies));
- } catch (e) {}
- }
+ let user;
+ if (token) {
+ try {
+ user = await get("/me", auth(cookies));
+ } catch (e) {}
+ }
- return { user };
+ return { user };
}
diff --git a/src/routes/(static)/+layout.svelte b/src/routes/(static)/+layout.svelte
index 3d596dff..29089dff 100644
--- a/src/routes/(static)/+layout.svelte
+++ b/src/routes/(static)/+layout.svelte
@@ -1,13 +1,13 @@
<script>
- import { SvelteToast } from '@zerodevx/svelte-toast';
- import { Footer, LandingHeader } from '$comp';
- export let data;
- $: ({ user } = data);
+ import { SvelteToast } from "@zerodevx/svelte-toast";
+ import { Footer, LandingHeader } from "$comp";
+ export let data;
+ $: ({ user } = data);
</script>
<SvelteToast options={{ reversed: true, intro: { y: 192 } }} />
<LandingHeader {user} />
<main class="container flex px-10 md:px-40 mx-auto min-h-[600px] pb-20">
- <slot />
+ <slot />
</main>
<Footer />
diff --git a/src/routes/(static)/bounties/+page.svelte b/src/routes/(static)/bounties/+page.svelte
index 729cf5dd..b53dc85d 100644
--- a/src/routes/(static)/bounties/+page.svelte
+++ b/src/routes/(static)/bounties/+page.svelte
@@ -1,102 +1,108 @@
<div class="space-y-5">
- <h1 class="text-3xl mb-6 pt-28 md:pt-12">Bounties</h1>
+ <h1 class="text-3xl mb-6 pt-28 md:pt-12">Bounties</h1>
- <p>
- We're offering ⚡️1,000,000 to the first 10 people that help us onboard these businesses in
- Vancouver to take bitcoin payments.
- </p>
+ <p>
+ We're offering ⚡️1,000,000 to the first 10 people that help us onboard
+ these businesses in Vancouver to take bitcoin payments.
+ </p>
- <ul class="text-center text-lg space-y-3">
- <li>Grocery / Produce Store</li>
- <li class="leading-3">
- <span class="line-through">Cafe</span><br />
- <span class="text-xs"
- ><a href="/reverendhodl">ReverendHodl</a> for
- <a href="/laughingbean">Laughing Bean Coffee Co.</a></span
- >
- </li>
- <li>Bakery</li>
- <li class="leading-3">
- <span class="line-through">Butcher Shop</span><br />
- <span class="text-xs"
- ><a href="/danielle">danielle</a> for
- <a href="/butchersguild">All Natural &amp; Exotic Meats</a></span
- >
- </li>
- <li>Laundromat / Dry Cleaner</li>
- <li>Dollar Store / Book Store / Gift Shop</li>
- <li class="leading-3">
- <span class="line-through">Sushi Restaurant</span><br />
- <span class="text-xs"
- ><a href="/ica547">ica547</a> for <a href="/metallmash">VanLove Sushi</a></span
- >
- </li>
- <li>Pizza by the Slice</li>
- <li>Liquor Store / Gas Station</li>
- <li>Bubble Tea / Ice Cream Parlour</li>
- </ul>
+ <ul class="text-center text-lg space-y-3">
+ <li>Grocery / Produce Store</li>
+ <li class="leading-3">
+ <span class="line-through">Cafe</span><br />
+ <span class="text-xs"
+ ><a href="/reverendhodl">ReverendHodl</a> for
+ <a href="/laughingbean">Laughing Bean Coffee Co.</a></span
+ >
+ </li>
+ <li>Bakery</li>
+ <li class="leading-3">
+ <span class="line-through">Butcher Shop</span><br />
+ <span class="text-xs"
+ ><a href="/danielle">danielle</a> for
+ <a href="/butchersguild">All Natural &amp; Exotic Meats</a></span
+ >
+ </li>
+ <li>Laundromat / Dry Cleaner</li>
+ <li>Dollar Store / Book Store / Gift Shop</li>
+ <li class="leading-3">
+ <span class="line-through">Sushi Restaurant</span><br />
+ <span class="text-xs"
+ ><a href="/ica547">ica547</a> for
+ <a href="/metallmash">VanLove Sushi</a></span
+ >
+ </li>
+ <li>Pizza by the Slice</li>
+ <li>Liquor Store / Gas Station</li>
+ <li>Bubble Tea / Ice Cream Parlour</li>
+ </ul>
- <p>Eligible businesses:</p>
+ <p>Eligible businesses:</p>
- <ul class="text-sm ml-4 space-y-2">
- <li>
- Have a physical location in Vancouver, North Vancouver, West Vancouver, Burnaby, New
- Westminster, or Richmond
- </li>
- <li>Take walk-in customers</li>
- <li>Have regular hours and a website with contact info</li>
- <li>Have a 3 star or better rating on Google</li>
- <li>
- Accept bitcoin during their posted hours without needing to make any special arrangements
- </li>
- <li>Use Coinos</li>
- </ul>
+ <ul class="text-sm ml-4 space-y-2">
+ <li>
+ Have a physical location in Vancouver, North Vancouver, West Vancouver,
+ Burnaby, New Westminster, or Richmond
+ </li>
+ <li>Take walk-in customers</li>
+ <li>Have regular hours and a website with contact info</li>
+ <li>Have a 3 star or better rating on Google</li>
+ <li>
+ Accept bitcoin during their posted hours without needing to make any
+ special arrangements
+ </li>
+ <li>Use Coinos</li>
+ </ul>
- <p>Each business category can be claimed only once</p>
- <p>One bounty per person</p>
+ <p>Each business category can be claimed only once</p>
+ <p>One bounty per person</p>
- <p>The businesses can expect free ongoing support and promotion from us and our community.</p>
+ <p>
+ The businesses can expect free ongoing support and promotion from us and our
+ community.
+ </p>
- <p>The prize money will be offered to the person who makes the referral.</p>
+ <p>The prize money will be offered to the person who makes the referral.</p>
- <p>
- <b>⚡️250,000</b> will be awarded once the business has met with us and is ready to take payments
- </p>
- <p>
- <b>⚡️750,000</b> will be awarded once the merchant has accepted bitcoin for 6 months. We'll be
- sending
- <a
- href="https://btcmap.org/leaderboard"
- class="text-black hover:underline"
- target="_blank"
- rel="noreferrer">shadowy supertaggers</a
- > to check up on them periodically.
- </p>
+ <p>
+ <b>⚡️250,000</b> will be awarded once the business has met with us and is ready
+ to take payments
+ </p>
+ <p>
+ <b>⚡️750,000</b> will be awarded once the merchant has accepted bitcoin for
+ 6 months. We'll be sending
+ <a
+ href="https://btcmap.org/leaderboard"
+ class="text-black hover:underline"
+ target="_blank"
+ rel="noreferrer">shadowy supertaggers</a
+ > to check up on them periodically.
+ </p>
- <p>
- Bounties will be awarded solely at our discretion and we reserve the right to refuse any
- applicants without cause
- </p>
+ <p>
+ Bounties will be awarded solely at our discretion and we reserve the right
+ to refuse any applicants without cause
+ </p>
- <h2 class="text-xl">Ready to begin!?</h2>
+ <h2 class="text-xl">Ready to begin!?</h2>
- <p>
- To make a referral, reach out on one of our socials or email us at <a
- href="mailto:[email protected]"
- class="text-black hover:underline">[email protected]</a
- > and we'll set up an appointment with you and the business owner to get started.
- </p>
+ <p>
+ To make a referral, reach out on one of our socials or email us at <a
+ href="mailto:[email protected]"
+ class="text-black hover:underline">[email protected]</a
+ > and we'll set up an appointment with you and the business owner to get started.
+ </p>
- <p>
- We'll update this page as the bounties are claimed so you'll be able to see which ones are still
- available.
- </p>
+ <p>
+ We'll update this page as the bounties are claimed so you'll be able to see
+ which ones are still available.
+ </p>
- <p>Thanks for participating and helping to orange pill Vancouver!</p>
+ <p>Thanks for participating and helping to orange pill Vancouver!</p>
</div>
<style>
- a {
- @apply text-blue-400;
- }
+ a {
+ @apply text-blue-400;
+ }
</style>
diff --git a/src/routes/(static)/docs/+page.server.js b/src/routes/(static)/docs/+page.server.js
index e80365fa..41176b14 100644
--- a/src/routes/(static)/docs/+page.server.js
+++ b/src/routes/(static)/docs/+page.server.js
@@ -1,4 +1,4 @@
export let load = async ({ cookies }) => {
- let token = cookies.get('token');
- return { token };
+ let token = cookies.get("token");
+ return { token };
};
diff --git a/src/routes/(static)/docs/+page.svelte b/src/routes/(static)/docs/+page.svelte
index 63173173..6e08c2e8 100644
--- a/src/routes/(static)/docs/+page.svelte
+++ b/src/routes/(static)/docs/+page.svelte
@@ -1,121 +1,130 @@
<script>
- import { copyNoNewlines as copy } from '$lib/utils';
- import { Code, Icon } from '$comp';
- import { PUBLIC_COINOS_URL } from '$env/static/public';
- export let data;
- let { user, token } = data;
- let api = 'https://coinos.io/api';
+ import { copyNoNewlines as copy } from "$lib/utils";
+ import { Code, Icon } from "$comp";
+ import { PUBLIC_COINOS_URL } from "$env/static/public";
+ export let data;
+ let { user, token } = data;
+ let api = "https://coinos.io/api";
- let tokenSample = token ? `export token="${token}"` : `export token=<your auth token>`;
+ let tokenSample = token
+ ? `export token="${token}"`
+ : `export token=<your auth token>`;
</script>
<div class="space-y-8 lg:text-xl mt-20 pt-20 md:pt-0 w-full max-w-full">
- <h1 class="text-4xl">Documentation</h1>
+ <h1 class="text-4xl">Documentation</h1>
- <p class="text-secondary">
- Coinos has a simple REST API that can be used to register accounts and make payments and
- queries. The following examples show how you can call the API with <a
- href="https://curl.se/"
- class="font-bold">curl</a
- > from your command line.
- </p>
+ <p class="text-secondary">
+ Coinos has a simple REST API that can be used to register accounts and make
+ payments and queries. The following examples show how you can call the API
+ with <a href="https://curl.se/" class="font-bold">curl</a> from your command
+ line.
+ </p>
- <h2 class="text-2xl">API Base URL</h2>
- <div class="bg-black text-white rounded-lg p-4 flex gap-4">
- <div>{api}</div>
- <button class="ml-auto my-auto invert opacity-90" on:click={() => copy(api)}
- ><Icon icon="copy" style="w-10 max-w-none" /></button
- >
- </div>
+ <h2 class="text-2xl">API Base URL</h2>
+ <div class="bg-black text-white rounded-lg p-4 flex gap-4">
+ <div>{api}</div>
+ <button class="ml-auto my-auto invert opacity-90" on:click={() => copy(api)}
+ ><Icon icon="copy" style="w-10 max-w-none" /></button
+ >
+ </div>
- <h2 class="text-2xl">Auth Token</h2>
- <p class="text-secondary">
- {#if user && token}
- This token authorizes you to use the API as <b>{user.username}</b>.
- {:else}
- <a href="/login" class="font-bold">Sign in</a> to view your auth token here, or get one from the
- /login endpoint.
- {/if}
+ <h2 class="text-2xl">Auth Token</h2>
+ <p class="text-secondary">
+ {#if user && token}
+ This token authorizes you to use the API as <b>{user.username}</b>.
+ {:else}
+ <a href="/login" class="font-bold">Sign in</a> to view your auth token here,
+ or get one from the /login endpoint.
+ {/if}
- Save it in a variable called <b>$token</b> to run the examples.
- </p>
+ Save it in a variable called <b>$token</b> to run the examples.
+ </p>
- <div class="bg-black text-white rounded-lg p-4 flex gap-4">
- <div class="w-full break-all">{tokenSample}</div>
- <button class="ml-auto my-auto invert opacity-90" on:click={() => copy(tokenSample)}
- ><Icon icon="copy" style="w-10 max-w-none" /></button
- >
- </div>
+ <div class="bg-black text-white rounded-lg p-4 flex gap-4">
+ <div class="w-full break-all">{tokenSample}</div>
+ <button
+ class="ml-auto my-auto invert opacity-90"
+ on:click={() => copy(tokenSample)}
+ ><Icon icon="copy" style="w-10 max-w-none" /></button
+ >
+ </div>
- <h2 class="text-2xl">POST /register</h2>
- <p class="text-secondary">Register a new user account with a username and password</p>
- <Code sample="register" />
+ <h2 class="text-2xl">POST /register</h2>
+ <p class="text-secondary">
+ Register a new user account with a username and password
+ </p>
+ <Code sample="register" />
- <h2 class="text-2xl">POST /login</h2>
- <p class="text-secondary">Login to an account to get its auth token</p>
- <Code sample="login" />
+ <h2 class="text-2xl">POST /login</h2>
+ <p class="text-secondary">Login to an account to get its auth token</p>
+ <Code sample="login" />
- <h2 class="text-2xl">POST /invoice</h2>
- <p class="text-secondary">Create an invoice.</p>
+ <h2 class="text-2xl">POST /invoice</h2>
+ <p class="text-secondary">Create an invoice.</p>
- <div class="text-secondary">
- <div>Request params</div>
- <div class="grid grid-cols-3 ml-2">
- <div class="font-bold">type</div>
- <div class="col-span-2">bitcoin or lightning</div>
- <div class="font-bold">amount</div>
- <div class="col-span-2">amount in satoshis</div>
- <div class="font-bold">webhook</div>
- <div class="col-span-2">(optional) endpoint to hit when the invoice is paid</div>
- <div class="font-bold">secret</div>
- <div class="col-span-2">a secret for the webhook to check</div>
- </div>
- </div>
+ <div class="text-secondary">
+ <div>Request params</div>
+ <div class="grid grid-cols-3 ml-2">
+ <div class="font-bold">type</div>
+ <div class="col-span-2">bitcoin or lightning</div>
+ <div class="font-bold">amount</div>
+ <div class="col-span-2">amount in satoshis</div>
+ <div class="font-bold">webhook</div>
+ <div class="col-span-2">
+ (optional) endpoint to hit when the invoice is paid
+ </div>
+ <div class="font-bold">secret</div>
+ <div class="col-span-2">a secret for the webhook to check</div>
+ </div>
+ </div>
- <p>Get a lightning invoice to receive funds</p>
- <Code sample="lightningInvoice" />
+ <p>Get a lightning invoice to receive funds</p>
+ <Code sample="lightningInvoice" />
- <p>Get a bitcoin address to receive funds</p>
- <Code sample="bitcoinAddress" />
+ <p>Get a bitcoin address to receive funds</p>
+ <Code sample="bitcoinAddress" />
- <p>Sample response</p>
- <Code sample="invoiceResponse" />
- <p>You can check the <b>received</b> field to see how much has been paid</p>
+ <p>Sample response</p>
+ <Code sample="invoiceResponse" />
+ <p>You can check the <b>received</b> field to see how much has been paid</p>
- <p>Specify a webhook to be called when an invoice is paid</p>
- <Code sample="webhook" />
+ <p>Specify a webhook to be called when an invoice is paid</p>
+ <Code sample="webhook" />
- <h2 class="text-2xl">GET /invoice/:hash</h2>
- <p class="text-secondary">
- Fetch an invoice by passing a bitcoin address or lightning payment hash
- </p>
- <Code sample="fetchInvoice" />
+ <h2 class="text-2xl">GET /invoice/:hash</h2>
+ <p class="text-secondary">
+ Fetch an invoice by passing a bitcoin address or lightning payment hash
+ </p>
+ <Code sample="fetchInvoice" />
- <h2 class="text-2xl">POST /payments</h2>
- <p class="text-secondary">Send a lightning payment</p>
- <Code sample="lightning" />
+ <h2 class="text-2xl">POST /payments</h2>
+ <p class="text-secondary">Send a lightning payment</p>
+ <Code sample="lightning" />
- <p class="text-secondary">Send a bitcoin payment</p>
- <Code sample="bitcoin" />
+ <p class="text-secondary">Send a bitcoin payment</p>
+ <Code sample="bitcoin" />
- <p class="text-secondary">Send an internal payment to another user</p>
- <Code sample="internal" />
+ <p class="text-secondary">Send an internal payment to another user</p>
+ <Code sample="internal" />
- <h2 class="text-2xl">GET /payments</h2>
- <p class="text-secondary">Get all payments sent or received by the current user</p>
+ <h2 class="text-2xl">GET /payments</h2>
+ <p class="text-secondary">
+ Get all payments sent or received by the current user
+ </p>
- <div class="text-secondary">
- <div>Query params</div>
- <div class="grid grid-cols-3 ml-2">
- <div class="font-bold">start</div>
- <div class="col-span-2">only payments after this unix time</div>
- <div class="font-bold">end</div>
- <div class="col-span-2">only payments before this unix time</div>
- <div class="font-bold">limit</div>
- <div class="col-span-2">limit to this integer number of results</div>
- <div class="font-bold">offset</div>
- <div class="col-span-2">start limiting from this integer offset</div>
- </div>
- </div>
- <Code sample="payments" />
+ <div class="text-secondary">
+ <div>Query params</div>
+ <div class="grid grid-cols-3 ml-2">
+ <div class="font-bold">start</div>
+ <div class="col-span-2">only payments after this unix time</div>
+ <div class="font-bold">end</div>
+ <div class="col-span-2">only payments before this unix time</div>
+ <div class="font-bold">limit</div>
+ <div class="col-span-2">limit to this integer number of results</div>
+ <div class="font-bold">offset</div>
+ <div class="col-span-2">start limiting from this integer offset</div>
+ </div>
+ </div>
+ <Code sample="payments" />
</div>
diff --git a/src/routes/+layout.server.js b/src/routes/+layout.server.js
index 60a2f6c6..5273d372 100644
--- a/src/routes/+layout.server.js
+++ b/src/routes/+layout.server.js
@@ -1,17 +1,17 @@
import { auth, get } from "$lib/utils";
-import { loadTranslations, locale } from '$lib/translations';
+import { loadTranslations, locale } from "$lib/translations";
export const load = async ({ cookies, url }) => {
- let user;
- let token = cookies.get('token');
- if (token) {
- try {
- user = await get('/me', auth(cookies));
- } catch (e) {}
- }
+ let user;
+ let token = cookies.get("token");
+ if (token) {
+ try {
+ user = await get("/me", auth(cookies));
+ } catch (e) {}
+ }
- const { host, pathname } = url;
- const defaultLocale = user?.language || 'en'; // get from cookie, user session, ...
- const initLocale = locale.get() || defaultLocale; // set default if no locale already set
- await loadTranslations(initLocale, pathname); // keep this just before the `return`
- return { host, pathname };
+ const { host, pathname } = url;
+ const defaultLocale = user?.language || "en"; // get from cookie, user session, ...
+ const initLocale = locale.get() || defaultLocale; // set default if no locale already set
+ await loadTranslations(initLocale, pathname); // keep this just before the `return`
+ return { host, pathname };
};
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 32d31fa8..182be1cc 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -1,44 +1,47 @@
<script>
- import { PUBLIC_DOMAIN } from '$env/static/public';
- import '../app.css';
- import { loading, t } from '$lib/translations';
+ import { PUBLIC_DOMAIN } from "$env/static/public";
+ import "../app.css";
+ import { loading, t } from "$lib/translations";
- export let data;
- let { pathname } = data;
- let host = PUBLIC_DOMAIN.includes("localhost") ? `http://${PUBLIC_DOMAIN}` : `https://${PUBLIC_DOMAIN}`
+ export let data;
+ let { pathname } = data;
+
+ let host = PUBLIC_DOMAIN.includes("localhost")
+ ? `http://${PUBLIC_DOMAIN}`
+ : `https://${PUBLIC_DOMAIN}`;
</script>
<svelte:head>
- <title>coinos</title>
- <meta property="og:title" content="coinos" />
- <meta name="twitter:title" content="coinos" />
+ <title>coinos</title>
+ <meta property="og:title" content="coinos" />
+ <meta name="twitter:title" content="coinos" />
- <meta property="og:image" content={`${host}/images/logo.png`} />
+ <meta property="og:image" content={`${host}/images/logo.png`} />
- <meta property="og:type" content="website" />
- <meta property="og:description" content="An easy to use bitcoin web wallet" />
- <meta name="description" content="An easy to use bitcoin web wallet" />
+ <meta property="og:type" content="website" />
+ <meta property="og:description" content="An easy to use bitcoin web wallet" />
+ <meta name="description" content="An easy to use bitcoin web wallet" />
- <meta name="keywords" content="coinos easy bitcoin web wallet" />
+ <meta name="keywords" content="coinos easy bitcoin web wallet" />
- <meta name="twitter:image" content={`${host}/images/logo.png`} />
- <meta name="twitter:card" content="summary_large_image" />
+ <meta name="twitter:image" content={`${host}/images/logo.png`} />
+ <meta name="twitter:card" content="summary_large_image" />
- <meta property="og:url" content={host + pathname} />
- <meta name="twitter:site" content="@coinoswallet" />
- <meta name="twitter:creator" content="@coinoswallet" />
+ <meta property="og:url" content={host + pathname} />
+ <meta name="twitter:site" content="@coinoswallet" />
+ <meta name="twitter:creator" content="@coinoswallet" />
</svelte:head>
{#if !$loading}
- <slot />
+ <slot />
{/if}
<style global>
- :root {
- --toastContainerTop: auto;
- --toastContainerRight: auto;
- --toastContainerBottom: 8rem;
- --toastContainerLeft: calc(50vw - 8rem);
- --toastBackground: #292929;
- }
+ :root {
+ --toastContainerTop: auto;
+ --toastContainerRight: auto;
+ --toastContainerBottom: 8rem;
+ --toastContainerLeft: calc(50vw - 8rem);
+ --toastBackground: #292929;
+ }
</style>
diff --git a/src/routes/+page.server.js b/src/routes/+page.server.js
index dbd7fdf7..904e543d 100644
--- a/src/routes/+page.server.js
+++ b/src/routes/+page.server.js
@@ -1,4 +1,4 @@
// export const prerender = true;
export let load = async () => {
- return { faqs: ['cost', 'compatibility', 'safety', 'pos', 'bitcoin'] };
+ return { faqs: ["cost", "compatibility", "safety", "pos", "bitcoin"] };
};
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 59d56bf1..a90eb4ae 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,114 +1,118 @@
<script>
- import {
- LandingHeader,
- LandingHero,
- LandingInfoCard,
- HowItWorksCard,
- FaqCard,
- Image,
- About,
- Footer
- } from '$comp';
+ import {
+ LandingHeader,
+ LandingHero,
+ LandingInfoCard,
+ HowItWorksCard,
+ FaqCard,
+ Image,
+ About,
+ Footer,
+ } from "$comp";
- import { t } from '$lib/translations';
- import { onDestroy, onMount, tick } from 'svelte';
+ import { t } from "$lib/translations";
+ import { onDestroy, onMount, tick } from "svelte";
- export let data;
- let { faqs, locations, user } = data;
- let update = (data) => ({ faqs, locations, user } = data);
- $: update(data);
+ export let data;
+ let { faqs, locations, user } = data;
+ let update = (data) => ({ faqs, locations, user } = data);
+ $: update(data);
- let howItWorks;
- let faq;
- let about;
+ let howItWorks;
+ let faq;
+ let about;
- let log = console.log;
+ let log = console.log;
- const howItWorksSteps = [
- { image: 'hand', stepID: 'step1' },
- { image: 'bolt', stepID: 'step2' },
- { image: 'smile', stepID: 'step3' }
- ];
+ const howItWorksSteps = [
+ { image: "hand", stepID: "step1" },
+ { image: "bolt", stepID: "step2" },
+ { image: "smile", stepID: "step3" },
+ ];
- let loaded;
+ let loaded;
- let observer;
- let isIntersecting = false;
+ let observer;
+ let isIntersecting = false;
- onMount(async () => {
- observer = new IntersectionObserver((entries) => {
- isIntersecting = isIntersecting || entries[0].isIntersecting;
- });
+ onMount(async () => {
+ observer = new IntersectionObserver((entries) => {
+ isIntersecting = isIntersecting || entries[0].isIntersecting;
+ });
- loaded = true;
- await tick();
- observer.observe(document.getElementById('faq'));
- });
+ loaded = true;
+ await tick();
+ observer.observe(document.getElementById("faq"));
+ });
- onDestroy(() => (loaded = false));
+ onDestroy(() => (loaded = false));
</script>
<svelte:head>
- <script src="https://stamen-maps.a.ssl.fastly.net/js/tile.stamen.js"></script>
+ <script src="https://stamen-maps.a.ssl.fastly.net/js/tile.stamen.js"></script>
</svelte:head>
{#if loaded}
- <LandingHeader {howItWorks} {faq} {about} {user} />
+ <LandingHeader {howItWorks} {faq} {about} {user} />
- <main class="space-y-40 py-20 md:py-32 lg:py-36 xl:py-40 px-5 md:px-0">
- <LandingHero />
- <LandingInfoCard
- image="lightning-qr"
- title={$t('landing.info1.title')}
- description={$t('landing.info1.description')}
- />
+ <main class="space-y-40 py-20 md:py-32 lg:py-36 xl:py-40 px-5 md:px-0">
+ <LandingHero />
+ <LandingInfoCard
+ image="lightning-qr"
+ title={$t("landing.info1.title")}
+ description={$t("landing.info1.description")}
+ />
- <LandingInfoCard image="phone-checkout" title={$t('landing.info2.title')} order="reverse">
- {$t('landing.info2.description1')}
- <br /><br />
- {$t('landing.info2.description2')}
- <br /><br />
- {@html $t('landing.info2.description3')}
- </LandingInfoCard>
+ <LandingInfoCard
+ image="phone-checkout"
+ title={$t("landing.info2.title")}
+ order="reverse"
+ >
+ {$t("landing.info2.description1")}
+ <br /><br />
+ {$t("landing.info2.description2")}
+ <br /><br />
+ {@html $t("landing.info2.description3")}
+ </LandingInfoCard>
- <div>
- <LandingInfoCard
- image="customize"
- title={$t('landing.info3.title')}
- description={$t('landing.info3.description')}
- />
- <div bind:this={howItWorks} />
- <div id="about" />
- </div>
+ <div>
+ <LandingInfoCard
+ image="customize"
+ title={$t("landing.info3.title")}
+ description={$t("landing.info3.description")}
+ />
+ <div bind:this={howItWorks} />
+ <div id="about" />
+ </div>
- <div>
- <h3 class="text-5xl font-medium mb-10 text-center">
- {$t('howItWorks.header')}
- </h3>
- <div class="grid lg:grid-cols-3 space-y-10 lg:space-y-0 text-center">
- {#each howItWorksSteps as step}
- <HowItWorksCard image={step.image} stepID={step.stepID} />
- {/each}
- </div>
- <div bind:this={faq} />
- </div>
+ <div>
+ <h3 class="text-5xl font-medium mb-10 text-center">
+ {$t("howItWorks.header")}
+ </h3>
+ <div class="grid lg:grid-cols-3 space-y-10 lg:space-y-0 text-center">
+ {#each howItWorksSteps as step}
+ <HowItWorksCard image={step.image} stepID={step.stepID} />
+ {/each}
+ </div>
+ <div bind:this={faq} />
+ </div>
- <div>
- <div class="space-y-10">
- <h3 class="text-5xl font-medium text-center pt-40 -mt-40" id="faq">
- {$t('faq.header')}
- </h3>
- {#each faqs as f}
- <FaqCard questionID={f} />
- {/each}
- </div>
- <div bind:this={about} />
- </div>
+ <div>
+ <div class="space-y-10">
+ <h3 class="text-5xl font-medium text-center pt-40 -mt-40" id="faq">
+ {$t("faq.header")}
+ </h3>
+ {#each faqs as f}
+ <FaqCard questionID={f} />
+ {/each}
+ </div>
+ <div bind:this={about} />
+ </div>
- {#if isIntersecting}
- <About />
- {/if}
- </main>
+ {#if isIntersecting}
+ <About />
+ {/if}
+ </main>
- <Footer />
+ <Footer />
{/if}
diff --git a/src/routes/about/+server.js b/src/routes/about/+server.js
index f8a37fb5..b6912cb4 100644
--- a/src/routes/about/+server.js
+++ b/src/routes/about/+server.js
@@ -1,5 +1,5 @@
-import { redirect } from '@sveltejs/kit';
+import { redirect } from "@sveltejs/kit";
export async function GET() {
- throw new redirect(307, '/#about');
+ throw new redirect(307, "/#about");
}
diff --git a/src/routes/map/+page.server.js b/src/routes/map/+page.server.js
index 7936e88b..616d28f6 100644
--- a/src/routes/map/+page.server.js
+++ b/src/routes/map/+page.server.js
@@ -1,12 +1,12 @@
-import { get } from '$lib/utils';
+import { get } from "$lib/utils";
// export const prerender = true;
export let load = async () => {
- let data = { locations: [] };
+ let data = { locations: [] };
- try {
- data = await get('/locations');
- } catch (e) {}
+ try {
+ data = await get("/locations");
+ } catch (e) {}
- return data;
+ return data;
};
diff --git a/src/routes/map/+page.svelte b/src/routes/map/+page.svelte
index baf067d7..8c5fc3f8 100644
--- a/src/routes/map/+page.svelte
+++ b/src/routes/map/+page.svelte
@@ -1,18 +1,18 @@
<script>
- import { Icon, LocationsMap } from '$comp';
- export let data;
+ import { Icon, LocationsMap } from "$comp";
+ export let data;
- let { locations } = data;
+ let { locations } = data;
</script>
<div class="pt-4">
- <div class="w-[243px] mx-auto mb-4">
- <a href="/">
- <Icon icon="logo" />
- </a>
- </div>
+ <div class="w-[243px] mx-auto mb-4">
+ <a href="/">
+ <Icon icon="logo" />
+ </a>
+ </div>
- <div class="flex justify-center items-center">
- <LocationsMap {locations} />
- </div>
+ <div class="flex justify-center items-center">
+ <LocationsMap {locations} />
+ </div>
</div>