diff options
Diffstat (limited to 'src')
31 files changed, 215 insertions, 378 deletions
diff --git a/src/components/Footer.svelte b/src/components/Footer.svelte index 82282465..a33716d4 100644 --- a/src/components/Footer.svelte +++ b/src/components/Footer.svelte @@ -9,7 +9,7 @@ ]; const companyLinks = [ - { url: '/news', titleID: 'news' }, + { url: '/docs', titleID: 'documentation' }, { url: '/support', titleID: 'support' }, { url: '/jobs', titleID: 'jobs' } ]; diff --git a/src/components/Numpad.svelte b/src/components/Numpad.svelte index 74844b71..d74255e0 100644 --- a/src/components/Numpad.svelte +++ b/src/components/Numpad.svelte @@ -6,7 +6,8 @@ export let amount, currency, - fiat = !amount; + fiat = !amount, + submit = undefined; export let amountFiat = amount ? amount * ($selectedRate / sats) : 0; @@ -88,44 +89,12 @@ } }; - function getCaretPosition(editableDiv) { - var caretPos = 0, - sel, - range; - if (window.getSelection) { - sel = window.getSelection(); - if (sel.rangeCount) { - range = sel.getRangeAt(0); - if (range.commonAncestorContainer.parentNode == editableDiv) { - caretPos = range.endOffset; - } - } - } else if (document.selection && document.selection.createRange) { - range = document.selection.createRange(); - if (range.parentElement() == editableDiv) { - var tempEl = document.createElement('span'); - editableDiv.insertBefore(tempEl, editableDiv.firstChild); - var tempRange = range.duplicate(); - tempRange.moveToElementText(tempEl); - tempRange.setEndPoint('EndToEnd', range); - caretPos = tempRange.text.length; - } - } - return caretPos; - } - - $: html = fiat ? f(amountFiat, currency) : s(amountSats); + $: html = fiat ? amountFiat : s(amountSats); let prev = ''; let input = (e) => { - if (amount > sats) { - if (fiat) amountFiat = (Math.round(sats * ($selectedRate / sats))).toString(); - else amountSats = sats; - return warning($t('user.receive.lessThan1BTCWarning')); - } - - if (prev === '0' && html.length === 2) { + if (prev === '0') { prev = ''; html = html.replace('0', ''); } @@ -133,25 +102,53 @@ let sel = getSelection(); let i = sel.focusOffset; - if (!fiat) amountSats = parseInt(html.replace(/,/g, '')); + let clean = html.substr(0,15) + .replace(/[^0-9.,]+/g, '') + .replace('.', 'F') + .replace(/\./g, '') + .replace('F', '.'); + + if (fiat) clean = clean.replace(',', ''); + if (clean !== html) { + html = clean; + i = html.length; + } + + if (amount > sats && html.length > prev.length) { + html = prev; + warning($t('user.receive.lessThan1BTCWarning')); + i--; + } + + if (fiat) { + amountFiat = html; + } else { + amountSats = parseInt(html.replace(/,/g, '')); + } + if (!amountSats) amountSats = null; setTimeout(() => { - let p = prev.split(',')[0].length; - if (html.length > prev.length && p === 3) i++; - if (html.length < prev.length && p === 1 && i > 1) i--; + if (!fiat) { + let p = prev.split(',')[0].length; + if (html.length > prev.length && p === 3) i++; + if (html.length < prev.length && p === 1 && i > 1) i--; + } let node = e.target.childNodes[0]; let range = document.createRange(); - range.setStart(node, i); - range.setEnd(node, i); + console.log('NODE', node, i); + if (node) { + range.setStart(node, i); + range.setEnd(node, i); - let sel = getSelection(); - sel.removeAllRanges(); - sel.addRange(range); + let sel = getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + } - prev = html; + prev = html.toString(); }, 0); }; @@ -162,47 +159,41 @@ sel.removeAllRanges(); sel.addRange(range); }; + + let keydown = (e) => e.key === 'Enter' && (e.preventDefault() || submit.click()); </script> <div class="flex justify-center items-center mb-3 px-3"> <div class="space-y-5"> <div class="text-center"> <div class="text-5xl md:text-6xl font-semibold tracking-widest flex justify-center"> - {#if fiat} - <div class="my-auto">{symbol}</div> - <div - contenteditable - bind:innerHTML={amountFiat} - on:focus={focus} - on:input={input} - class="outline-none" - /> - {:else} - <div>⚡️</div> - <div - contenteditable - bind:innerHTML={html} - on:focus={focus} - on:input={input} - class="outline-none" - /> - {/if} + <div class="my-auto" class:text-5xl={!fiat}>{fiat ? symbol : '⚡️'}</div> + <div + contenteditable + bind:innerHTML={html} + on:focus={focus} + on:input={input} + on:keydown={keydown} + class="outline-none my-auto" + /> + </div> + <div class="mt-2"> + <span class="text-secondary mr-1">{fiat ? amountSatsConverted : amountFiatConverted}</span> + <button + type="button" + on:click={() => { + if (fiat) { + amountSats = parseInt((amountFiat / ($selectedRate / sats)).toFixed(0)); + } else { + amountFiat = + (amountSats * ($selectedRate / sats)).toFixed(2) > 0.0 + ? (amountSats * ($selectedRate / sats)).toFixed(2) + : 0; + } + fiat = !fiat; + }}><Icon icon="swap" style="inline hover:opacity-80" /></button + > </div> - <span class="text-secondary mr-1">{fiat ? amountSatsConverted : amountFiatConverted}</span> - <button - type="button" - on:click={() => { - if (fiat) { - amountSats = parseInt((amountFiat / ($selectedRate / sats)).toFixed(0)); - } else { - amountFiat = - (amountSats * ($selectedRate / sats)).toFixed(2) > 0.0 - ? (amountSats * ($selectedRate / sats)).toFixed(2) - : 0; - } - fiat = !fiat; - }}><Icon icon="swap" style="inline hover:opacity-80" /></button - > </div> <div class="grid grid-cols-3 gap-2 w-[300px] mx-auto"> diff --git a/src/components/Password.svelte b/src/components/Password.svelte index e21bdad1..95a52cac 100644 --- a/src/components/Password.svelte +++ b/src/components/Password.svelte @@ -1,6 +1,6 @@ <script> import { Icon } from '$comp'; - import { failure, post } from '$lib/utils'; + import { focus, failure, post } from '$lib/utils'; import { password as pw, passwordPrompt } from '$lib/store'; export let user; @@ -35,7 +35,7 @@ class="bg-primary" bind:value={password} autocapitalize="none" - autofocus + use:focus /> {:else} <input @@ -45,7 +45,7 @@ class="bg-primary" bind:value={password} autocapitalize="none" - autofocus + use:focus /> {/if} <button diff --git a/src/components/Pin.svelte b/src/components/Pin.svelte index efc1e81b..c7889aa9 100644 --- a/src/components/Pin.svelte +++ b/src/components/Pin.svelte @@ -1,4 +1,5 @@ <script> + import { focus } from "$lib/utils"; import { pin } from '$lib/store'; let pinCode = ''; @@ -12,7 +13,7 @@ <div class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-20"> <div class="relative top-1/3 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white space-y-5"> <h1 class="text-center text-2xl font-semibold">Please enter your PIN</h1> - <input bind:value={pinCode} autofocus /> + <input bind:value={pinCode} use:focus /> <div class="w-full flex"> <button type="submit" diff --git a/src/components/Qr.svelte b/src/components/Qr.svelte index 31cbe1b3..5fea7cbd 100644 --- a/src/components/Qr.svelte +++ b/src/components/Qr.svelte @@ -10,5 +10,5 @@ on:click={() => screenfull.toggle(qr)} on:keydown={() => screenfull.toggle(qr)} > - <img {src} class="mx-auto" /> +<img {src} class="mx-auto" alt="QR Code" /> </div> diff --git a/src/lib/invoice.js b/src/lib/invoice.js index f0c5aad5..983cde2a 100644 --- a/src/lib/invoice.js +++ b/src/lib/invoice.js @@ -29,9 +29,6 @@ export default async ({ cookies, request, url }) => { throw redirect(307, `/${user.username}/request/${request_id}`); } - if (invoice.prompt) { - throw redirect(307, `/invoice/${hash}/tip`); - } else { - throw redirect(307, `/invoice/${hash}`); - } + if (invoice.prompt) throw redirect(307, `/invoice/${hash}/tip`); + else throw redirect(307, `/invoice/${hash}`); }; diff --git a/src/lib/utils.js b/src/lib/utils.js index e6325e9f..bb6b34fa 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -137,8 +137,9 @@ export const login = async (user, cookies) => { let expires = new Date(); expires.setSeconds(expires.getSeconds() + maxAge); - cookies.set('username', user.username, { path: '/', expires }); - cookies.set('token', token, { path: '/', expires }); + let opts = { path: '/', expires } + cookies.set('username', user.username, opts); + cookies.set('token', token, opts); }; export const auth = (cookies) => ({ authorization: `Bearer ${cookies.get('token')}` }); @@ -160,8 +161,8 @@ export const sat = (s) => { return ( '⚡️' + (parseInt(s) > 0 - ? new Intl.NumberFormat('en-US', { maximumFractionDigits: 1 }).format(s / Math.pow(1000, d)) + - ['', 'K', 'M', 'G', 'T', 'P'][d] + ? new Intl.NumberFormat('en-US', { maximumFractionDigits: 2 }).format(s / Math.pow(1000, d)) + + ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z'][d] : 0 ).toString() ); diff --git a/src/locales/en.json b/src/locales/en.json index b7f4c316..d65ffc45 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -75,7 +75,7 @@ "support": "Support", "telegram": "Telegram", "twitter": "Twitter", - "news": "News" + "documentation": "Documentation" }, "howItWorks": { "header": "How it works", diff --git a/src/routes/(app)/+error.svelte b/src/routes/(app)/+error.svelte deleted file mode 100644 index 8d613909..00000000 --- a/src/routes/(app)/+error.svelte +++ /dev/null @@ -1,42 +0,0 @@ -<script> - import { page } from '$app/stores'; - import { Icon, PageNotFound } from '$comp'; - import { t } from '$lib/translations'; - import { back } from '$lib/utils'; - - export let data; - let { user } = data; - - $: message = $page.error.message; - $: if (message.startsWith('Internal')) message = 'Something went wrong'; -</script> - -{#if $page.error.message.includes('not found') || $page.status === 404} - <PageNotFound /> -{:else} - <div class="container px-4 max-w-lg mx-auto mt-20 space-y-5"> - <div class="w-[243px] mx-auto mb-10"> - <a href="/"> - <Icon icon="logo" /> - </a> - </div> - - <div class="flex justify-center items-center"> - <div class="shadow-xl rounded-3xl p-10 pb-12 space-y-5 w-full mx-5 md:mx-0 md:w-[400px]"> - <h1 class="text-3xl md:text-4xl font-semibold"> - {$t('error.header')} - </h1> - - <p class="text-secondary"> - {message}. If the problem persists, please - <a href="/support" class="underline">let us know.</a> - </p> - - <button - class="rounded-full bg-black text-white py-2 px-5 font-bold hover:opacity-80 mb-2" - on:click={back}>Back</button - > - </div> - </div> - </div> -{/if} diff --git a/src/routes/(app)/+layout.server.js b/src/routes/(app)/+layout.server.js index 0b76c973..1415bdca 100644 --- a/src/routes/(app)/+layout.server.js +++ b/src/routes/(app)/+layout.server.js @@ -15,6 +15,10 @@ export async function load({ cookies, request, url, params }) { } catch (e) {} } + if (user?.needsMigration) { + throw redirect(307, `/migrate`); + } + if (user && ['/', '/login', '/register'].includes(pathname) && request.method === 'GET') { console.log('dashboarding', pathname); throw redirect(307, `/${user.username}/dashboard`); diff --git a/src/routes/(app)/[username]/(profile)/messages/+page.svelte b/src/routes/(app)/[username]/(profile)/messages/+page.svelte index 47228015..00960534 100644 --- a/src/routes/(app)/[username]/(profile)/messages/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/messages/+page.svelte @@ -12,7 +12,6 @@ import { calculateId } from 'nostr'; export let data; - export let form; let { newMessages, subject, user } = data; let input, pane; diff --git a/src/routes/(app)/[username]/payments/[...page]/+page.svelte b/src/routes/(app)/[username]/payments/[...page]/+page.svelte index 620aec84..551238cb 100644 --- a/src/routes/(app)/[username]/payments/[...page]/+page.svelte +++ b/src/routes/(app)/[username]/payments/[...page]/+page.svelte @@ -141,7 +141,7 @@ <a href={`/pot/${p.pot}`}> <div class="text-secondary flex"> <div class="my-auto mr-1"> - <img src="/icons/logo-symbol.svg" class="w-12 border-4 border-transparent" /> + <img src="/icons/logo-symbol.svg" class="w-12 border-4 border-transparent" alt="Pot" /> </div> <div class="my-auto">Pot</div> @@ -162,7 +162,7 @@ <div class="text-3xl">⚡️</div> {:else} <div class="my-auto mr-1"> - <img src="/images/bitcoin.svg" class="w-12 border-4 border-transparent" /> + <img src="/images/bitcoin.svg" class="w-12 border-4 border-transparent" alt="Bitcoin" /> </div> {/if} @@ -201,10 +201,4 @@ .active * { @apply bg-black text-white border-black; } - - select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - } </style> diff --git a/src/routes/(app)/[username]/receive/[...param]/+page.svelte b/src/routes/(app)/[username]/receive/[...param]/+page.svelte index 47c507fa..13c65939 100644 --- a/src/routes/(app)/[username]/receive/[...param]/+page.svelte +++ b/src/routes/(app)/[username]/receive/[...param]/+page.svelte @@ -19,6 +19,7 @@ let { currency, username } = data.subject; let { user } = data; + let submit; </script> <form method="POST" class="flex justify-center items-center mt-24 mb-3 px-3" use:enhance> @@ -34,8 +35,9 @@ <input type="hidden" name="prompt" value={false} /> <div class="space-y-3 w-[300px] mx-auto"> - <Numpad bind:amount bind:currency /> + <Numpad bind:amount bind:currency {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" diff --git a/src/routes/(app)/[username]/settings/[...tab]/+page.server.js b/src/routes/(app)/[username]/settings/[...tab]/+page.server.js index cb642e60..dd16459f 100644 --- a/src/routes/(app)/[username]/settings/[...tab]/+page.server.js +++ b/src/routes/(app)/[username]/settings/[...tab]/+page.server.js @@ -1,4 +1,4 @@ -import { invalid, redirect } from '@sveltejs/kit'; +import { fail, redirect } from '@sveltejs/kit'; import { auth, get, post } from '$lib/utils'; export function load({ params, url }) { @@ -14,7 +14,7 @@ export const actions = { try { ({ user } = await post(`/user`, user, auth(cookies))); } catch (e) { - return invalid(400, { message: e.message }); + return fail(400, { message: e.message }); } return { user, success: true }; diff --git a/src/routes/(app)/invoice/[hash]/+page.svelte b/src/routes/(app)/invoice/[hash]/+page.svelte index 059225ab..090a333f 100644 --- a/src/routes/(app)/invoice/[hash]/+page.svelte +++ b/src/routes/(app)/invoice/[hash]/+page.svelte @@ -151,8 +151,4 @@ .full { @apply p-8; } - - .full-shadow { - box-shadow: inset 0 0 0 1000px rgba(0, 0, 0, 0.25); - } </style> diff --git a/src/routes/(app)/invoice/[hash]/tip/+page.svelte b/src/routes/(app)/invoice/[hash]/tip/+page.svelte index d7df0a69..eaa67170 100644 --- a/src/routes/(app)/invoice/[hash]/tip/+page.svelte +++ b/src/routes/(app)/invoice/[hash]/tip/+page.svelte @@ -2,7 +2,7 @@ import { onMount } from 'svelte'; import { browser } from '$app/environment'; import { invoice as inv, request } from '$lib/store'; - import { back, copy, f, sat, get, reverseFormat, s, sats } from '$lib/utils'; + import { back, copy, focus, f, sat, get, reverseFormat, s, sats } from '$lib/utils'; import { Icon } from '$comp'; import { goto } from '$app/navigation'; import { t } from '$lib/translations'; @@ -190,7 +190,7 @@ on:input={(e) => handleCustomTipAmount(e)} class="pl-10" placeholder="Custom amount" - autofocus + use:focus /> <button type="submit" diff --git a/src/routes/(app)/ln/[lnurl]/+page.server.js b/src/routes/(app)/ln/[lnurl]/+page.server.js index ded2e58c..6f61143f 100644 --- a/src/routes/(app)/ln/[lnurl]/+page.server.js +++ b/src/routes/(app)/ln/[lnurl]/+page.server.js @@ -1,5 +1,5 @@ import { auth, get, post } from '$lib/utils'; -import { error, invalid, redirect } from '@sveltejs/kit'; +import { error, fail, redirect } from '@sveltejs/kit'; export async function load({ params }) { try { @@ -22,7 +22,7 @@ export const actions = { 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 invalid(400, { error }); + if (error) return fail(400, { error }); let { pr } = await fetch(`${callback}?amount=${amount}`).then((r) => r.json()); throw redirect(307, `/send/lightning/${pr}`); diff --git a/src/routes/(app)/login/+page.server.js b/src/routes/(app)/login/+page.server.js index 24146326..27607ff1 100644 --- a/src/routes/(app)/login/+page.server.js +++ b/src/routes/(app)/login/+page.server.js @@ -1,4 +1,4 @@ -import { invalid, redirect } from '@sveltejs/kit'; +import { fail, redirect } from '@sveltejs/kit'; import { login } from '$lib/utils'; export const load = async ({ parent }) => { @@ -17,7 +17,7 @@ export const actions = { try { let r = await login(user, cookies); } catch (e) { - return invalid(400, { error: 'Login failed', message: e.message, ...form }); + return fail(400, { error: 'Login failed', message: e.message, ...form }); } throw redirect(303, loginRedirect || `/${user.username}/dashboard`); diff --git a/src/routes/(app)/payment/[id]/+page.svelte b/src/routes/(app)/payment/[id]/+page.svelte index 5a604d99..9eefda2b 100644 --- a/src/routes/(app)/payment/[id]/+page.svelte +++ b/src/routes/(app)/payment/[id]/+page.svelte @@ -51,7 +51,7 @@ <div class="font-bold">{$t('payments.id')}</div> <div class="flex"> <div> - <a href={`${expl}/tx/${id}`} target="_blank" class="text-blue-600">{id}</a> + <a href={`${expl}/tx/${id}`} target="_blank" rel="noreferrer" class="text-blue-600">{id}</a> </div> <button class="flex font-bold hover:opacity-80 mb-auto" on:click={() => copy(id)} ><Icon icon="copy" style="mr-1" /> diff --git a/src/routes/(app)/pot/[name]/withdraw/+page.server.js b/src/routes/(app)/pot/[name]/withdraw/+page.server.js index 7a486d58..ab56867d 100644 --- a/src/routes/(app)/pot/[name]/withdraw/+page.server.js +++ b/src/routes/(app)/pot/[name]/withdraw/+page.server.js @@ -1,4 +1,4 @@ -import { invalid, redirect } from '@sveltejs/kit'; +import { fail, redirect } from '@sveltejs/kit'; import { auth, post } from '$lib/utils'; export async function load({ parent }) { @@ -13,7 +13,7 @@ export const actions = { try { await post('/take', body, auth(cookies)); } catch (e) { - return invalid(400, { message: e.message }); + return fail(400, { message: e.message }); } throw redirect(307, `/pot/${body.name}`); diff --git a/src/routes/(app)/register/+page.server.js b/src/routes/(app)/register/+page.server.js index 44c1f44b..1d98136f 100644 --- a/src/routes/(app)/register/+page.server.js +++ b/src/routes/(app)/register/+page.server.js @@ -1,4 +1,4 @@ -import { invalid, redirect } from '@sveltejs/kit'; +import { fail, redirect } from '@sveltejs/kit'; import { post, login } from '$lib/utils'; export const load = async ({ parent }) => { @@ -28,7 +28,7 @@ export const actions = { error ||= e.message; } - if (error) return invalid(400, { error }); + if (error) return fail(400, { error }); throw redirect(307, loginRedirect || `/${user.username}/dashboard`); } }; diff --git a/src/routes/(app)/send/[...text]/+page.server.js b/src/routes/(app)/send/[...text]/+page.server.js index 3cc9f73d..8e5e02d7 100644 --- a/src/routes/(app)/send/[...text]/+page.server.js +++ b/src/routes/(app)/send/[...text]/+page.server.js @@ -1,7 +1,7 @@ import { validate } from 'bitcoin-address-validation'; import bip21 from 'bip21'; import { auth, get, post } from '$lib/utils'; -import { invalid, redirect } from '@sveltejs/kit'; +import { redirect } from '@sveltejs/kit'; let parse = async (t, host) => { if (!t) return; @@ -23,9 +23,7 @@ let parse = async (t, host) => { if (t.includes('/pot')) throw redirect(307, t.substring(t.indexOf('/pot'))); - console.log('LNURL?'); if (t.toLowerCase().startsWith('lnurl')) throw redirect(307, `/ln/${t}`); - console.log('NOPE'); if (t.includes(':')) t = t.split(':')[1]; // lightning diff --git a/src/routes/(app)/send/[uuid=uuid]/+page.server.js b/src/routes/(app)/send/[uuid=uuid]/+page.server.js index e9f161c7..7fa613bf 100644 --- a/src/routes/(app)/send/[uuid=uuid]/+page.server.js +++ b/src/routes/(app)/send/[uuid=uuid]/+page.server.js @@ -1,4 +1,4 @@ -import { error, invalid, redirect } from '@sveltejs/kit'; +import { error, fail, redirect } from '@sveltejs/kit'; import { btc as asset, auth, get, post } from '$lib/utils'; export async function load({ params, parent }) { @@ -17,12 +17,12 @@ export const actions = { let { confirmed } = body; if (!confirmed) { - return invalid(403, { amount, confirm: true }); + return fail(400, { amount, confirm: true }); } await post('/payments', body, auth(cookies)); } catch (e) { - return invalid(400, { message: e.message }); + return fail(400, { message: e.message }); } throw redirect(307, '/sent'); 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 901d90a8..59e9326d 100644 --- a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js +++ b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js @@ -1,11 +1,17 @@ -import { invalid, redirect } from '@sveltejs/kit'; +import { fail, redirect } from '@sveltejs/kit'; import { auth, post } from '$lib/utils'; +let seen = {}; export const actions = { default: async ({ cookies, request }) => { try { let feeRate, subtract; - let { address, amount, pin } = Object.fromEntries(await request.formData()); + let { address, amount, pin, ts } = Object.fromEntries(await request.formData()); + + if (seen[ts]) return fail(400, { message: 'Duplicate form submission' }); + seen[ts] = 1; + setTimeout(() => delete seen[ts], 30000); + let { tx } = await post( '/bitcoin/fee', { address, amount, feeRate, subtract }, @@ -13,7 +19,7 @@ export const actions = { ); await post('/bitcoin/send', { pin, tx }, auth(cookies)); } catch (e) { - return invalid(400, { message: e.message }); + return fail(400, { message: e.message }); } 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 2165e896..0da8ea10 100644 --- a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte +++ b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte @@ -11,9 +11,10 @@ let { address, amount } = $page.params; let { currency } = data.user; - let loading; + let loading, submit; + + let toggle = () => (loading = true); - let submit = () => (loading = true); $: update(form); let update = () => { if (form?.message.includes('pin')) $pin = undefined; @@ -36,17 +37,20 @@ <h1 class="text-3xl md:text-4xl font-semibold mb-2">{$t('payments.sendTo')}</h1> <p class="text-lg text-secondary">{address}</p> </div> - <Numpad bind:amount {currency} /> + <Numpad bind:amount {currency} {submit} /> - <form method="POST" use:enhance on:submit={submit}> + <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" /> <div class="flex w-full"> <button + bind:this={submit} 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 /> diff --git a/src/routes/(app)/send/lightning/[payreq]/+page.server.js b/src/routes/(app)/send/lightning/[payreq]/+page.server.js index def7651c..21bc9c71 100644 --- a/src/routes/(app)/send/lightning/[payreq]/+page.server.js +++ b/src/routes/(app)/send/lightning/[payreq]/+page.server.js @@ -1,4 +1,4 @@ -import { invalid, redirect } from '@sveltejs/kit'; +import { fail, redirect } from '@sveltejs/kit'; import { btc as asset, auth, post } from '$lib/utils'; export async function load({ params }) { @@ -18,14 +18,14 @@ export const actions = { console.log('AMOUNT', amount); if (!confirmed) { - return invalid(403, { amount, confirm: true }); + return fail(400, { amount, confirm: true }); } 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 invalid(400, { message: e.message }); + return fail(400, { message: e.message }); } throw redirect(307, '/sent'); diff --git a/src/routes/(app)/send/pot/[name]/[...amount]/+page.server.js b/src/routes/(app)/send/pot/[name]/[...amount]/+page.server.js index eadaa22e..0e1b7f5b 100644 --- a/src/routes/(app)/send/pot/[name]/[...amount]/+page.server.js +++ b/src/routes/(app)/send/pot/[name]/[...amount]/+page.server.js @@ -1,4 +1,4 @@ -import { invalid, redirect } from '@sveltejs/kit'; +import { fail, redirect } from '@sveltejs/kit'; import { auth, post } from '$lib/utils'; export const actions = { @@ -9,7 +9,7 @@ export const actions = { try { p = await post('/payments', body, auth(cookies)); } catch (e) { - return invalid(400, { message: e.message }); + return fail(400, { message: e.message }); } if (p) throw redirect(307, `/pot/${body.name}`); 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 01712df2..46172175 100644 --- a/src/routes/(app)/send/user/[username]/[...amount]/+page.server.js +++ b/src/routes/(app)/send/user/[username]/[...amount]/+page.server.js @@ -1,4 +1,4 @@ -import { error, invalid, redirect } from '@sveltejs/kit'; +import { error, fail, redirect } from '@sveltejs/kit'; import { btc as asset, auth, get, post } from '$lib/utils'; export async function load({ params, parent }) { @@ -17,13 +17,13 @@ export const actions = { amount = parseInt(amount); if (!confirmed) { - return invalid(403, { amount, confirm: true }); + return fail(400, { amount, confirm: true }); } await post('/payments', body, auth(cookies)); } catch (e) { console.log(e); - return invalid(400, { message: e.message }); + return fail(400, { message: e.message }); } throw redirect(307, '/sent'); diff --git a/src/routes/(app)/support/+page.server.js b/src/routes/(app)/support/+page.server.js index 02c5e3b3..5d3bf988 100644 --- a/src/routes/(app)/support/+page.server.js +++ b/src/routes/(app)/support/+page.server.js @@ -1,5 +1,5 @@ import { post } from '$lib/utils'; -import { invalid } from '@sveltejs/kit'; +import { fail } from '@sveltejs/kit'; export const actions = { default: async ({ cookies, request }) => { @@ -8,7 +8,7 @@ export const actions = { try { await post('/email', form); } catch (e) { - return invalid(400, { error: e.message }); + return fail(400, { error: e.message }); } return { success: true }; diff --git a/src/routes/(static)/docs/+page.svelte b/src/routes/(static)/docs/+page.svelte index 0e11decb..c634cd76 100644 --- a/src/routes/(static)/docs/+page.svelte +++ b/src/routes/(static)/docs/+page.svelte @@ -1,76 +1,77 @@ -<h1>API Examples</h1> -<pre><code class="whitespace-pre-wrap" - >{` -api="https://coinos.io/api" -json="Content-Type: application/json" - -username=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) -password="pw" - -echo Create user $username with password $password -curl -s "$api/register" -H "$json" -d '{"user": {"username": "'$username'", "password": "'$password'", "confirm": "'$password'"}}' > /dev/null -echo "" - -echo Login and get an API token -token=$(curl -s "$api/taboggan" -H "$json" -d '{"username": "'$username'", "password": "'$password'"}' | jq -r .token) -echo $token -echo "" - -username="apidemo" -token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFwaWRlbW8iLCJpYXQiOjE2NDAxMzIwNjd9.aBtcrnLjDS_Zixn5_X_N8sqX0KxTDrVySSqh1fa96rk" -auth="Authorization: Bearer $token" - -echo Get account balance in satoshis -curl -s "$api/me" -H "$json" -H "$auth" | jq '.account.balance' -echo "" - -echo Get the current price of bitcoin in USD -curl -s "$api/rates" -H "$json" | jq '.USD' -echo "" - -echo Create a legacy Bitcoin address -address=$(curl -s "$api/address?network=bitcoin&type=legacy" -H "$auth" -H "$json" | jq -r .address) -echo $address -echo "" - -echo Create an invoice to associate the address with the user -curl -s "$api/invoice" -H "$json" -d '{"invoice": {"address": "'$address'", "network": "bitcoin"}, "user": {"username": "'$username'"}}' > /dev/null -echo "" - -msg="testingtesting123" -echo Sign a message with the address: $msg $address -curl -s "$api/signMessage" -H "$json" -H "$auth" -d '{"address": "'$address'", "message": "'$msg'"}' -echo "" - -echo Create a bech32 Bitcoin address -address=$(curl -s "$api/address?network=bitcoin&type=bech32" -H "$auth" -H "$json" | jq -r .address) -echo $address -echo "" - -echo Create a lightning invoice -text=$(curl -s "$api/lightning/invoice" -H "$auth" -H "$json" -d '{"amount": 100}') -echo $text -echo "" - -echo Associate the lightning invoice with the user -curl -s "$api/invoice" -H "$json" -d '{"invoice": {"text": "'$text'", "network": "bitcoin"}, "user": {"username": "'$username'"}}' > /dev/null -echo "" - -echo Get an external invoice -payreq=$(curl -s https://legend.lnbits.com/api/v1/payments -H "$json" -H "x-api-key: 698e5b6744f8442880ba3ad630756c02" -d '{ "out": false, "amount": 50, "memo": "test", "unit": "sat", "lnurl_callback": null }' | jq -r .payment_request) -echo $payreq -echo "" - -echo Send a lightning payment -curl -s "$api/lightning/send" -H "$json" -H "$auth" -d '{"payreq": "'$payreq'"}' | jq -r '{ amount, fee, balance: (.account.balance)}' -echo "" - -echo Send a bitcoin payment -tx=$(curl -s "$api/bitcoin/fee" -H "$json" -H "$auth" -d '{"address": "bc1qqc9azhdjjpdxenrndt8cw4tw9del5pyrjn0wln", "amount": 1200, "feeRate": 1000 }') -hex=$(echo $tx | jq -r .tx.hex) -fee=$(echo $tx | jq -r .tx.fee) - -curl -s "$api/bitcoin/send" -H "$json" -H "$auth" -d '{"address": "bc1qqc9azhdjjpdxenrndt8cw4tw9del5pyrjn0wln", "tx": { "hex": "'$hex'", "fee": "'$fee'" }}' | jq -r '{ amount, fee, balance: (.account.balance)}' -echo "" -`}</code - ></pre> +Coming soon +<!-- <h1>API Examples</h1> --> +<!-- <pre><code class="whitespace-pre-wrap" --> +<!-- >{` --> +<!-- api="https://coinos.io/api" --> +<!-- json="Content-Type: application/json" --> +<!-- --> +<!-- username=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) --> +<!-- password="pw" --> +<!-- --> +<!-- echo Create user $username with password $password --> +<!-- curl -s "$api/register" -H "$json" -d '{"user": {"username": "'$username'", "password": "'$password'", "confirm": "'$password'"}}' > /dev/null --> +<!-- echo "" --> +<!-- --> +<!-- echo Login and get an API token --> +<!-- token=$(curl -s "$api/taboggan" -H "$json" -d '{"username": "'$username'", "password": "'$password'"}' | jq -r .token) --> +<!-- echo $token --> +<!-- echo "" --> +<!-- --> +<!-- username="apidemo" --> +<!-- token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFwaWRlbW8iLCJpYXQiOjE2NDAxMzIwNjd9.aBtcrnLjDS_Zixn5_X_N8sqX0KxTDrVySSqh1fa96rk" --> +<!-- auth="Authorization: Bearer $token" --> +<!-- --> +<!-- echo Get account balance in satoshis --> +<!-- curl -s "$api/me" -H "$json" -H "$auth" | jq '.account.balance' --> +<!-- echo "" --> +<!-- --> +<!-- echo Get the current price of bitcoin in USD --> +<!-- curl -s "$api/rates" -H "$json" | jq '.USD' --> +<!-- echo "" --> +<!-- --> +<!-- echo Create a legacy Bitcoin address --> +<!-- address=$(curl -s "$api/address?network=bitcoin&type=legacy" -H "$auth" -H "$json" | jq -r .address) --> +<!-- echo $address --> +<!-- echo "" --> +<!-- --> +<!-- echo Create an invoice to associate the address with the user --> +<!-- curl -s "$api/invoice" -H "$json" -d '{"invoice": {"address": "'$address'", "network": "bitcoin"}, "user": {"username": "'$username'"}}' > /dev/null --> +<!-- echo "" --> +<!-- --> +<!-- msg="testingtesting123" --> +<!-- echo Sign a message with the address: $msg $address --> +<!-- curl -s "$api/signMessage" -H "$json" -H "$auth" -d '{"address": "'$address'", "message": "'$msg'"}' --> +<!-- echo "" --> +<!-- --> +<!-- echo Create a bech32 Bitcoin address --> +<!-- address=$(curl -s "$api/address?network=bitcoin&type=bech32" -H "$auth" -H "$json" | jq -r .address) --> +<!-- echo $address --> +<!-- echo "" --> +<!-- --> +<!-- echo Create a lightning invoice --> +<!-- text=$(curl -s "$api/lightning/invoice" -H "$auth" -H "$json" -d '{"amount": 100}') --> +<!-- echo $text --> +<!-- echo "" --> +<!-- --> +<!-- echo Associate the lightning invoice with the user --> +<!-- curl -s "$api/invoice" -H "$json" -d '{"invoice": {"text": "'$text'", "network": "bitcoin"}, "user": {"username": "'$username'"}}' > /dev/null --> +<!-- echo "" --> +<!-- --> +<!-- echo Get an external invoice --> +<!-- payreq=$(curl -s https://legend.lnbits.com/api/v1/payments -H "$json" -H "x-api-key: 698e5b6744f8442880ba3ad630756c02" -d '{ "out": false, "amount": 50, "memo": "test", "unit": "sat", "lnurl_callback": null }' | jq -r .payment_request) --> +<!-- echo $payreq --> +<!-- echo "" --> +<!-- --> +<!-- echo Send a lightning payment --> +<!-- curl -s "$api/lightning/send" -H "$json" -H "$auth" -d '{"payreq": "'$payreq'"}' | jq -r '{ amount, fee, balance: (.account.balance)}' --> +<!-- echo "" --> +<!-- --> +<!-- echo Send a bitcoin payment --> +<!-- tx=$(curl -s "$api/bitcoin/fee" -H "$json" -H "$auth" -d '{"address": "bc1qqc9azhdjjpdxenrndt8cw4tw9del5pyrjn0wln", "amount": 1200, "feeRate": 1000 }') --> +<!-- hex=$(echo $tx | jq -r .tx.hex) --> +<!-- fee=$(echo $tx | jq -r .tx.fee) --> +<!-- --> +<!-- curl -s "$api/bitcoin/send" -H "$json" -H "$auth" -d '{"address": "bc1qqc9azhdjjpdxenrndt8cw4tw9del5pyrjn0wln", "tx": { "hex": "'$hex'", "fee": "'$fee'" }}' | jq -r '{ amount, fee, balance: (.account.balance)}' --> +<!-- echo "" --> +<!-- `}</code --> +<!-- ></pre> --> diff --git a/src/routes/(static)/news/+page.svelte b/src/routes/(static)/news/+page.svelte deleted file mode 100644 index 7eb1b7d9..00000000 --- a/src/routes/(static)/news/+page.svelte +++ /dev/null @@ -1,115 +0,0 @@ -<script> - import { Icon, Footer, Check, Quote } from '$comp'; - import { t } from '$lib/translations'; -</script> - -<a href="/"> - <Icon icon="logo" style="mx-auto mt-10" /> -</a> -<div class="w-11/12 md:w-3/4 lg:w-1/2 2xl:w-1/3 max-w-3xl mx-auto my-20 space-y-5"> - <h1 class="text-5xl font-medium">{$t('news.header')}</h1> - - <h2 class="text-2xl font-medium">{$t('news.subheader')}</h2> - - <p class="text-xl font-semibold">Press Release - coinos Redesign - September 23, 2022</p> - - <p class="text-xl text-secondary"> - Today coinos is launching a new and improved interface for their open source web wallet, - allowing businesses to accept bitcoin with no setup or merchant fees. - </p> - - <p class="text-xl text-secondary"> - coinos is a web-based bitcoin payment application that is mobile and tablet friendly with handy - currency conversion and reporting features. bitcoin is the world's leading crypto currency. It - offers a secure and inexpensive way for people all over the globe to make and accept payments. - </p> - - <blockquote - class="p-4 my-4 bg-gray-50 border-l-4 border-gray-300 dark:border-gray-500 dark:bg-gray-800 text-xl text-secondary" - > - <Quote /> coinos has enabled businesses to accept bitcoin since 2012, but now we have made it even - easier with a new simple, friendly and customizable interface for both businesses and customers to - use. - <figcaption class="flex justify-center items-center mt-6 space-x-3"> - <img class="w-6 h-6 rounded-full" src="/images/adam.webp" alt="Adam Soltys" /> - <div class="flex items-center divide-x-2 divide-gray-500 dark:divide-gray-700"> - <cite class="pr-3 font-medium text-gray-900 dark:text-white">Adam Soltys</cite> - <cite class="pl-3 text-sm font-light text-gray-500 dark:text-gray-400">Founder and CTO</cite - > - </div> - </figcaption> - </blockquote> - - <p class="text-xl text-secondary"> - coinos started as an indie bitcoin wallet created by Adam Soltys with the intention of providing - people with a fast, cheap, and easy way to get started with bitcoin. It's free to register an - account and you can sign up with just a username and password. - </p> - - <h3 class="text-xl font-semibold">Why accept bitcoin?</h3> - <blockquote - class="p-4 my-4 bg-gray-50 border-l-4 border-gray-300 dark:border-gray-500 dark:bg-gray-800 text-xl text-secondary" - > - <Quote /> - We have already brought on some local businesses, here in Vancouver, and the business owners are - excited about the opportunity coinos is giving them to attract new customers by being able to accept - bitcoin. With coinos there are no set up fees, no merchant fees and transactions are safe and secure - with payments going directly to the business owner's wallet. There is no middle person. - <figcaption class="flex justify-center items-center mt-6 space-x-3"> - <img class="w-6 h-6 rounded-full" src="/images/cole.webp" alt="Adam Soltys" /> - <div class="flex items-center divide-x-2 divide-gray-500 dark:divide-gray-700"> - <cite class="pr-3 font-medium text-gray-900 dark:text-white">Cole Goodwin</cite> - <cite class="pl-3 text-sm font-light text-gray-500 dark:text-gray-400" - >Bitcoin Professional</cite - > - </div> - </figcaption> - </blockquote> - - <h3 class="text-xl font-semibold">coinos Timeline</h3> - <ul class="text-xl text-secondary"> - <li class="flex items-center space-x-3"> - <Check /> - 2012 Coding started - </li> - <li class="flex items-center space-x-3"> - <Check /> - 2013 Used by merchants in Vancouver to accept on-chain bitcoin - </li> - <li class="flex items-center space-x-3"> - <Check /> - 2018 Lightning network supported - </li> - <li class="flex items-center space-x-3"> - <Check /> - 2020 Incorporated in BC and registered with Fintrac - </li> - <li class="flex items-center space-x-3"> - <Check /> - 2021 Liquid Network and asset issuance supported - </li> - <li class="flex items-center space-x-3"> - <Check /> - 2022 10 year anniversary and UI redesign - </li> - </ul> - - <h3 class="text-xl font-semibold">Contacts</h3> - - <p class="text-xl text-secondary"> - <b>Adam Soltys</b> - Founder & CTO<br /> - <a class="hover:opacity-80" href="mailto:[email protected] ">[email protected]</a> - </p> - - <p class="text-xl text-secondary"> - <b>Cole Goodwin</b> - Bitcoin Professional<br /> - <a class="hover:opacity-80" href="mailto:[email protected]">[email protected]</a> - </p> - - <p class="text-xl text-secondary"> - <b>Shauna Collister</b> - Operations & Marketing<br /> - <a class="hover:opacity-80" href="mailto:[email protected]">[email protected]</a> - </p> -</div> - -<Footer /> |
