From 002cd9134d2e23bec34f838f6d6fefba41f2d530 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 18 Oct 2024 09:08:07 -0700 Subject: biome format --- src/app.html | 9 +- src/components/AppHeader.svelte | 15 +- src/components/Balance.svelte | 5 +- src/components/Event.svelte | 12 +- src/components/Footer.svelte | 5 +- src/components/Invoice.svelte | 2 +- src/components/LocationsMap.svelte | 9 +- src/components/Pinpad.svelte | 3 +- src/lib/handler.ts | 16 +- src/lib/invoice.ts | 48 +- src/lib/nostr.ts | 198 ++++---- src/lib/parse.ts | 148 +++--- src/lib/punks.ts | 128 +++--- src/lib/samples.ts | 30 +- src/lib/socket.ts | 111 ++--- src/lib/store.ts | 54 +-- src/lib/ticket.ts | 44 +- src/lib/tickets.ts | 100 ++--- src/lib/types.ts | 24 +- src/lib/upload.ts | 30 +- src/lib/utils.ts | 500 +++++++++++---------- src/locales/en.json | 72 +-- src/routes/(app)/+layout.svelte | 6 +- .../(app)/[username]/(profile)/+layout.server.ts | 4 +- .../(app)/[username]/(profile)/+layout.svelte | 18 +- .../[username]/(profile)/followers/+page.svelte | 8 +- .../[username]/(profile)/follows/+page.svelte | 6 +- src/routes/(app)/[username]/+layout.server.ts | 2 +- src/routes/(app)/account/[id]/+page.server.ts | 3 +- src/routes/(app)/account/[id]/+page.svelte | 4 +- src/routes/(app)/account/pass/+page.svelte | 4 +- src/routes/(app)/account/savings/+page.svelte | 4 +- src/routes/(app)/account/setup/+page.svelte | 3 +- src/routes/(app)/admin/+page.server.ts | 2 +- .../(app)/ecash/[id]/[...version]/+layout.svelte | 4 +- .../ecash/[id]/[...version]/swap/+page.server.ts | 2 +- .../ecash/[id]/[...version]/swap/+page.svelte | 4 +- src/routes/(app)/invoice/+page.server.ts | 2 +- src/routes/(app)/invoice/+server.ts | 6 +- src/routes/(app)/invoice/[id]/+page.svelte | 2 +- src/routes/(app)/invoice/[id]/tip/+page.server.ts | 7 +- src/routes/(app)/ln/[lnurl]/+page.server.ts | 5 +- src/routes/(app)/messages/+page.server.ts | 6 +- .../(app)/messages/[recipient]/+page.server.ts | 3 +- src/routes/(app)/messages/[recipient]/+page.svelte | 6 +- src/routes/(app)/otpsecret/+server.ts | 2 +- .../pay/[username]/[...amount]/+page.server.ts | 4 +- src/routes/(app)/payments/[...page]/+page.svelte | 2 +- src/routes/(app)/qr/[text]/+page.svelte | 16 +- .../(app)/receive/[...token]/+page.server.ts | 6 +- src/routes/(app)/send/[...text]/+page.server.ts | 2 +- src/routes/(app)/send/[...text]/+page.svelte | 3 +- .../[amount]/[...feeRate]/+page.server.ts | 2 +- .../(app)/send/lightning/[payreq]/+page.svelte | 16 +- src/routes/(app)/sent/[id]/+page.server.ts | 2 +- src/routes/(app)/sent/[id]/+page.svelte | 5 +- src/routes/(app)/settings/[...tab]/+page.svelte | 8 +- src/routes/(app)/settings/[...tab]/_pos.svelte | 44 +- src/routes/(app)/subscription/+server.ts | 11 + src/routes/(static)/+layout.server.ts | 4 +- src/routes/(static)/login/+page.server.ts | 6 +- src/routes/(static)/login/+page.svelte | 23 +- src/routes/(static)/privacy/+page.svelte | 14 +- src/routes/(static)/register/+page.server.ts | 2 +- src/routes/(static)/register/+page.svelte | 2 +- src/routes/(static)/support/+page.svelte | 9 +- src/routes/+layout.server.ts | 68 +-- src/routes/+layout.ts | 12 +- src/routes/+page.server.ts | 4 +- src/routes/map/+page.svelte | 3 +- src/service-worker.js | 4 + 71 files changed, 1020 insertions(+), 928 deletions(-) create mode 100644 src/routes/(app)/subscription/+server.ts (limited to 'src') diff --git a/src/app.html b/src/app.html index ee2fd506..df6e9567 100644 --- a/src/app.html +++ b/src/app.html @@ -1,4 +1,4 @@ - + @@ -7,9 +7,12 @@ name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - + - + %sveltekit.head% diff --git a/src/components/AppHeader.svelte b/src/components/AppHeader.svelte index bedf431e..9dd37d7e 100644 --- a/src/components/AppHeader.svelte +++ b/src/components/AppHeader.svelte @@ -36,9 +36,10 @@ $banner?.id && $banner.id === subject.id ? `url(${$banner.src})` : subject?.banner - ? subject.banner.includes(":") ? `url(${subject.banner})` - : `url(/api/public/${subject.banner}.webp)` - : undefined; + ? subject.banner.includes(":") + ? `url(${subject.banner})` + : `url(/api/public/${subject.banner}.webp)` + : undefined; $: $page && (showMenu = false); @@ -59,14 +60,10 @@ > - + diff --git a/src/components/Event.svelte b/src/components/Event.svelte index d3a6c0af..640b9e68 100644 --- a/src/components/Event.svelte +++ b/src/components/Event.svelte @@ -14,12 +14,12 @@ return diff > d ? Math.round(diff / d) + "d" : diff > h - ? Math.round(diff / h) + "h" - : diff > m - ? Math.round(diff / m) + "m" - : diff > 2 - ? diff + "s" - : "now"; + ? Math.round(diff / h) + "h" + : diff > m + ? Math.round(diff / m) + "m" + : diff > 2 + ? diff + "s" + : "now"; }; let w; diff --git a/src/components/Footer.svelte b/src/components/Footer.svelte index 067e185e..ca711d7f 100644 --- a/src/components/Footer.svelte +++ b/src/components/Footer.svelte @@ -4,7 +4,10 @@ import { t } from "$lib/translations"; const followLinks = [ - { url: "nostr:nprofile1qqst4qyeqenw7zm0fwjsty68h6cnys5jre2xd8ngqpjv5a2j26s78fspz4mhxue69uhhyetvv9ujucm0d9hx7uewd9hsgwx239", titleID: "nostr" }, + { + url: "nostr:nprofile1qqst4qyeqenw7zm0fwjsty68h6cnys5jre2xd8ngqpjv5a2j26s78fspz4mhxue69uhhyetvv9ujucm0d9hx7uewd9hsgwx239", + titleID: "nostr", + }, { url: "https://t.me/coinoswallet", titleID: "telegram" }, { url: "https://linktr.ee/coinos.io", titleID: "linktree" }, ]; diff --git a/src/components/Invoice.svelte b/src/components/Invoice.svelte index 563a2fee..3298dfe5 100644 --- a/src/components/Invoice.svelte +++ b/src/components/Invoice.svelte @@ -19,7 +19,7 @@ {f( ($invoice.amount * $invoice.rate) / sats, - $invoice.currency + $invoice.currency, )} {sat($invoice.amount)} diff --git a/src/components/LocationsMap.svelte b/src/components/LocationsMap.svelte index 6c213525..4c3532b3 100644 --- a/src/components/LocationsMap.svelte +++ b/src/components/LocationsMap.svelte @@ -51,13 +51,18 @@ $: list = search ? markers.filter((m) => - m.tags.name.toLowerCase().includes(search.toLowerCase()) + m.tags.name.toLowerCase().includes(search.toLowerCase()), ) : inview; let scroll = () => { const el = document.querySelector(".selected"); - if (el) el.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "nearest" }); + if (el) + el.scrollIntoView({ + behavior: "smooth", + block: "nearest", + inline: "nearest", + }); }; let selected; diff --git a/src/components/Pinpad.svelte b/src/components/Pinpad.svelte index 2bb2bcf9..585a7942 100644 --- a/src/components/Pinpad.svelte +++ b/src/components/Pinpad.svelte @@ -81,7 +81,8 @@ handleInput(value)} + >{value} {/if} {/each} diff --git a/src/lib/handler.ts b/src/lib/handler.ts index 559761f0..baca442d 100644 --- a/src/lib/handler.ts +++ b/src/lib/handler.ts @@ -2,12 +2,12 @@ import { goto } from "$app/navigation"; import { applyAction } from "$app/forms"; export default (toggle) => () => { - toggle(); - return async ({ result }) => { - if (result.type === "redirect") { - goto(result.location); - } else { - await applyAction(result); - } - }; + toggle(); + return async ({ result }) => { + if (result.type === "redirect") { + goto(result.location); + } else { + await applyAction(result); + } + }; }; diff --git a/src/lib/invoice.ts b/src/lib/invoice.ts index fa2f7786..fec845f9 100644 --- a/src/lib/invoice.ts +++ b/src/lib/invoice.ts @@ -2,34 +2,34 @@ import { auth, get, post } from "$lib/utils"; import { redirect } from "@sveltejs/kit"; export default async ({ cookies, request }) => { - let aid = cookies.get("aid"); - let form = await request.formData(); + let aid = cookies.get("aid"); + let form = await request.formData(); - let rates = await get("/rates"); - let amount = parseInt(form.get("amount")); + let rates = await get("/rates"); + let amount = parseInt(form.get("amount")); - let invoice = { - aid, - amount, - items: JSON.parse(form.get("items")), - memo: form.get("memo"), - memoPrompt: form.get("memoPrompt"), - tip: parseInt(form.get("tip")) || 0, - type: form.get("type"), - prompt: form.get("prompt") === "true", - rate: parseFloat(form.get("rate")) || rates[form.get("currency")], - id: undefined, - }; + let invoice = { + aid, + amount, + items: JSON.parse(form.get("items")), + memo: form.get("memo"), + memoPrompt: form.get("memoPrompt"), + tip: parseInt(form.get("tip")) || 0, + type: form.get("type"), + prompt: form.get("prompt") === "true", + rate: parseFloat(form.get("rate")) || rates[form.get("currency")], + id: undefined, + }; - let user = { username: form.get("username"), currency: form.get("currency") }; + let user = { username: form.get("username"), currency: form.get("currency") }; - invoice = await post("/invoice", { invoice, user }, auth(cookies)); - let { id } = invoice; + invoice = await post("/invoice", { invoice, user }, auth(cookies)); + let { id } = invoice; - if (invoice.prompt && invoice.tip === null) - redirect(307, `/invoice/${id}/tip`); + if (invoice.prompt && invoice.tip === null) + redirect(307, `/invoice/${id}/tip`); - if (invoice.memoPrompt && !invoice.memo) { - redirect(307, `/invoice/${id}/memo`); - } else redirect(307, `/invoice/${id}`); + if (invoice.memoPrompt && !invoice.memo) { + redirect(307, `/invoice/${id}/memo`); + } else redirect(307, `/invoice/${id}`); }; diff --git a/src/lib/nostr.ts b/src/lib/nostr.ts index a9926ba1..a00a28f9 100644 --- a/src/lib/nostr.ts +++ b/src/lib/nostr.ts @@ -5,16 +5,16 @@ import { Buffer } from "buffer"; import { wait, post, stretch } from "$lib/utils"; import { decrypted, password as pw, passwordPrompt, pin } from "$lib/store"; import { - getPublicKey, - finalizeEvent, - nip04, - nip19, - type EventTemplate, + getPublicKey, + finalizeEvent, + nip04, + nip19, + type EventTemplate, } from "nostr-tools"; import { - encrypt as nip49encrypt, - decrypt as nip49decrypt, + encrypt as nip49encrypt, + decrypt as nip49decrypt, } from "nostr-tools/nip49"; import { mnemonicToEntropy, entropyToMnemonic } from "@scure/bip39"; @@ -24,154 +24,154 @@ import { wordlist } from "@scure/bip39/wordlists/english"; import { generateSeedWords, privateKeyFromSeedWords } from "nostr-tools/nip06"; type User = { - [key: string]: any; + [key: string]: any; }; type EncryptParams = { - message: string; - recipient: string; - user: User; + message: string; + recipient: string; + user: User; }; const { encode, decode, toWords, fromWords } = bech32m; export let generate = async (user: any) => { - let p = get(pin); - if (p && p.length !== 6) return; + let p = get(pin); + if (p && p.length !== 6) return; - let salt = crypto.getRandomValues(new Uint8Array(16)); - let mnemonic = generateSeedWords(); - let sk = privateKeyFromSeedWords(mnemonic); + let salt = crypto.getRandomValues(new Uint8Array(16)); + let mnemonic = generateSeedWords(); + let sk = privateKeyFromSeedWords(mnemonic); - let bytes = new Uint8Array( - await crypto.subtle.encrypt( - { name: "AES-GCM", iv: new Uint8Array(16) }, - await stretch(await getPassword(), salt), - mnemonicToEntropy(mnemonic, wordlist) - ) - ); + let bytes = new Uint8Array( + await crypto.subtle.encrypt( + { name: "AES-GCM", iv: new Uint8Array(16) }, + await stretch(await getPassword(), salt), + mnemonicToEntropy(mnemonic, wordlist), + ), + ); - user.pubkey = getPublicKey(hexToUint8Array(sk)); - user.cipher = encode("en", toWords(bytes), 180); - user.salt = Buffer.from(salt).toString("hex"); + user.pubkey = getPublicKey(hexToUint8Array(sk)); + user.cipher = encode("en", toWords(bytes), 180); + user.salt = Buffer.from(salt).toString("hex"); }; export let encrypt = async ({ message, recipient, user }: EncryptParams) => { - let sk = await getPrivateKey(user); - return nip04.encrypt(sk, recipient, message); + let sk = await getPrivateKey(user); + return nip04.encrypt(sk, recipient, message); }; export let decrypt = async ({ event, user }) => { - let cache = get(decrypted); - try { - let { content, pubkey, id } = event; - if (cache[id]) return cache[id]; - if (pubkey === user.pubkey) pubkey = event.tags[0][1]; + let cache = get(decrypted); + try { + let { content, pubkey, id } = event; + if (cache[id]) return cache[id]; + if (pubkey === user.pubkey) pubkey = event.tags[0][1]; - let message = await nip04.decrypt( - await getPrivateKey(user), - pubkey, - content - ); + let message = await nip04.decrypt( + await getPrivateKey(user), + pubkey, + content, + ); - cache[id] = Buffer.from(message).toString("utf8"); - decrypted.set(cache); + cache[id] = Buffer.from(message).toString("utf8"); + decrypted.set(cache); - return cache[id]; - } catch (e) { - // console.log(e); - } + return cache[id]; + } catch (e) { + // console.log(e); + } }; export let getPrivateKey = async (user: User): Promise => { - let k; - if (browser) { - k = localStorage.getItem("nsec"); - if (k) { - return nip19.decode(k).data as Uint8Array; - } - } + let k; + if (browser) { + k = localStorage.getItem("nsec"); + if (k) { + return nip19.decode(k).data as Uint8Array; + } + } - let { nsec } = user; + let { nsec } = user; - if (nsec) { - k = nip49decrypt(nsec, await getPassword()); - } else k = hexToUint8Array(privateKeyFromSeedWords(await getMnemonic(user))); + if (nsec) { + k = nip49decrypt(nsec, await getPassword()); + } else k = hexToUint8Array(privateKeyFromSeedWords(await getMnemonic(user))); - localStorage.setItem("nsec", nip19.nsecEncode(k)); - return k; + localStorage.setItem("nsec", nip19.nsecEncode(k)); + return k; }; export let getMnemonic = async (user: User) => { - let { cipher, salt } = user; - let entropy; + let { cipher, salt } = user; + let entropy; - entropy = new Uint8Array( - await crypto.subtle.decrypt( - { name: "AES-GCM", iv: new Uint8Array(16) }, - await stretch(await getPassword(), Buffer.from(salt, "hex")), - Uint8Array.from(fromWords(decode(cipher, 180).words)) - ) - ); + entropy = new Uint8Array( + await crypto.subtle.decrypt( + { name: "AES-GCM", iv: new Uint8Array(16) }, + await stretch(await getPassword(), Buffer.from(salt, "hex")), + Uint8Array.from(fromWords(decode(cipher, 180).words)), + ), + ); - return entropyToMnemonic(entropy, wordlist); + return entropyToMnemonic(entropy, wordlist); }; let decodeNsec = (nsec: string): Uint8Array => { - let { type, data } = nip19.decode(nsec); - if (type === "nsec" && data instanceof Uint8Array) return data; - else throw new Error("invalid nsec"); + let { type, data } = nip19.decode(nsec); + if (type === "nsec" && data instanceof Uint8Array) return data; + else throw new Error("invalid nsec"); }; export let getNsec = async (user: User) => { - return nip19.nsecEncode(await getPrivateKey(user)); + return nip19.nsecEncode(await getPrivateKey(user)); }; export let setNsec = async (user: User, nsec: string) => { - user.pubkey = getPublicKey(decodeNsec(nsec)); - user.nsec = await encryptNsec(nsec); + user.pubkey = getPublicKey(decodeNsec(nsec)); + user.nsec = await encryptNsec(nsec); }; export let encryptNsec = async (nsec: string) => { - let d = decodeNsec(nsec); - return nip49encrypt(d, await getPassword()); + let d = decodeNsec(nsec); + return nip49encrypt(d, await getPassword()); }; type SignParams = { - event: EventTemplate; - user: User; + event: EventTemplate; + user: User; }; export let sign = async ({ event, user }: SignParams) => { - event = finalizeEvent(event, await getPrivateKey(user)); + event = finalizeEvent(event, await getPrivateKey(user)); }; export let send = (event: EventTemplate) => { - return post("/events", { event }); + return post("/events", { event }); }; let getPassword = async (): Promise => { - if (!get(pw)) passwordPrompt.set(true); - await wait(() => !!get(pw)); - return get(pw) || ""; + if (!get(pw)) passwordPrompt.set(true); + await wait(() => !!get(pw)); + return get(pw) || ""; }; export let reEncryptEntropy = async (user: User, newPassword: string) => { - let { cipher, salt } = user; - - let entropy = await crypto.subtle.decrypt( - { name: "AES-GCM", iv: new Uint8Array(16) }, - await stretch(await getPassword(), hexToUint8Array(salt)), - Uint8Array.from(fromWords(decode(cipher, 180).words)) - ); - - let bytes = new Uint8Array( - await crypto.subtle.encrypt( - { name: "AES-GCM", iv: new Uint8Array(16) }, - await stretch(newPassword, hexToUint8Array(salt)), - entropy - ) - ); - - return encode("en", toWords(bytes), 180); + let { cipher, salt } = user; + + let entropy = await crypto.subtle.decrypt( + { name: "AES-GCM", iv: new Uint8Array(16) }, + await stretch(await getPassword(), hexToUint8Array(salt)), + Uint8Array.from(fromWords(decode(cipher, 180).words)), + ); + + let bytes = new Uint8Array( + await crypto.subtle.encrypt( + { name: "AES-GCM", iv: new Uint8Array(16) }, + await stretch(newPassword, hexToUint8Array(salt)), + entropy, + ), + ); + + return encode("en", toWords(bytes), 180); }; diff --git a/src/lib/parse.ts b/src/lib/parse.ts index 0ab3e809..fcce7e1e 100644 --- a/src/lib/parse.ts +++ b/src/lib/parse.ts @@ -1,80 +1,80 @@ +import { PUBLIC_DOMAIN } from "$env/static/public"; import { get, isLiquid, post } from "$lib/utils"; +import { redirect } from "@sveltejs/kit"; import bip21 from "bip21"; import { validate } from "bitcoin-address-validation"; -import { PUBLIC_DOMAIN } from "$env/static/public"; -import { redirect } from "@sveltejs/kit"; export default async (t, host) => { - if (!t) return; - - if (t.startsWith("http")) redirect(307, t); - if (t.startsWith(host)) redirect(307, `http://${t}`); - - let amount, user; - - t = t.trim(); - t.toLowerCase().startsWith("bitcoin:") && - ({ - address: t, - options: { amount }, - } = bip21.decode(t)); - - if (t.startsWith("lightning:")) t = t.replace("lightning:", ""); - 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.includes("/fund")) redirect(307, t.substring(t.indexOf("/fund"))); - - // lightning - if (t.toLowerCase().startsWith("lightning:")) - t = t.toLowerCase().replace("lightning:", ""); - if (t.toLowerCase().startsWith("lnurl")) redirect(307, `/ln/${t}`); - if (t.includes(":")) t = t.split(":")[1]; - - if (t.toLowerCase().startsWith("ln")) { - try { - await get(`/invoice/${t}`); - } catch (e) { - redirect(307, `/send/lightning/${t}`); - } - } - - // bitcoin - if (validate(t) || isLiquid(t)) { - let r = `/send/bitcoin/${t}`; - if (amount) r += "/" + amount; - redirect(307, r); - } - - if (t.startsWith("cashu")) { - let { id } = await post("/cash", { token: t }); - redirect(307, `/ecash/${id}`); - } - - // user - try { - user = await get(`/users/${t.split("/")[0]}`); - if (user.anon) user = null; - } catch (e) {} - - if (user) redirect(307, `/pay/${t}`); - - let fund; - try { - fund = await get(`/fund/${t}`); - } catch (e) {} - - if (fund) redirect(307, `/send/fund/${t}`); - - // invoice - let invoice; - try { - invoice = await get(`/invoice/${t}`); - } catch (e) {} - - if (invoice) redirect(307, `/send/invoice/${invoice.id}`); + if (!t) return; + + if (t.startsWith("http")) redirect(307, t); + if (t.startsWith(host)) redirect(307, `http://${t}`); + + let amount, user; + + t = t.trim(); + t.toLowerCase().startsWith("bitcoin:") && + ({ + address: t, + options: { amount }, + } = bip21.decode(t)); + + if (t.startsWith("lightning:")) t = t.replace("lightning:", ""); + 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.includes("/fund")) redirect(307, t.substring(t.indexOf("/fund"))); + + // lightning + if (t.toLowerCase().startsWith("lightning:")) + t = t.toLowerCase().replace("lightning:", ""); + if (t.toLowerCase().startsWith("lnurl")) redirect(307, `/ln/${t}`); + if (t.includes(":")) t = t.split(":")[1]; + + if (t.toLowerCase().startsWith("ln")) { + try { + await get(`/invoice/${t}`); + } catch (e) { + redirect(307, `/send/lightning/${t}`); + } + } + + // bitcoin + if (validate(t) || isLiquid(t)) { + let r = `/send/bitcoin/${t}`; + if (amount) r += "/" + amount; + redirect(307, r); + } + + if (t.startsWith("cashu")) { + const { id } = await post("/cash", { token: t }); + redirect(307, `/ecash/${id}`); + } + + // user + try { + user = await get(`/users/${t.split("/")[0]}`); + if (user.anon) user = null; + } catch (e) {} + + if (user) redirect(307, `/pay/${t}`); + + let fund; + try { + fund = await get(`/fund/${t}`); + } catch (e) {} + + if (fund) redirect(307, `/send/fund/${t}`); + + // invoice + let invoice; + try { + invoice = await get(`/invoice/${t}`); + } catch (e) {} + + if (invoice) redirect(307, `/send/invoice/${invoice.id}`); }; diff --git a/src/lib/punks.ts b/src/lib/punks.ts index e5551062..8b7b53df 100644 --- a/src/lib/punks.ts +++ b/src/lib/punks.ts @@ -1,66 +1,66 @@ export default [ - "6e1f1626a9308abd0b7732fecbc7d49366ced46dead63ef3603fb0f4ec7aa53d", - "96ea95735b61c48c6f465db8ca0246a62539f34c3a977be6338770d92461da89", - "8c18c036b257d7b9c5a970a786efa04c320f3cf5afa73fa310a6603744d1ee3a", - "a7a6187e54e83acdb893d456c8b4ad6877e4738ea9e0d47c9b2070bf2c98a35f", - "836d72150b6f3033b6220d15ac9d49457d81041a700d12898bd3b8e737b4d265", - "e3d1c40538a8aed282a430647e3ff16ff94b64b8ed0a7cf427e8b0e2780a9b2a", - "2155224ef35cfb4bbdb06d982b42bb9b93d97b2bc691c22c804e1759a0a04882", - "5f6549ad9dd15dd57c204b1a9b60c68c376e77d7d114cfa13219e6a1a71f901f", - "ba8dd5a7070aee7102d4f8bc83ac497091e2e473a0f448a1c513569167dd0791", - "4f07e1da3685a858970a9f39300564971e97257f5d37b55a6d9882014aecc421", - "01aa9348244c4b65de3df758d49c98076e17a109aef04bc211abc77cf42dc51d", - "080574eb9e0613926e0e5d9b667c132e12965a1b1d47218a0b290a2d55c55d2f", - "08ce4840184f63a779bcd1f91bbcb56a2e8cba75c3ba2dbd290a559a57e4238e", - "27caa38c14a85ad64b4c4469a34e61a3965a1cbc4cad9a85707da374301c830d", - "eccc90741370ba4acc70bc65f4ccc38fddb1327d977c366156018974bc52fe4a", - "3759946d9e875ac2fc5c51ed850686193bb1e0716219e42efd52f3965993b275", - "db5fe28b8ddb9ae5e4f5e0e757ee60bc938bffbdfdd38de807939e212d3c5066", - "3f8f861e663accee51e68b987705ce647a7446b7f118abc94c632e158acbb44f", - "43bab95a047112dca6705506f916a97252363a20da4a4c0a5ab7b676e11083fc", - "d3018e45c66dd485697f738ad191af98267e6255d562496dd06b1a662e0c7a88", - "0ec2e8d07e053d686140857493ece653cde52764a60aa1d25651d81811d44a3d", - "4595bb092682af7177799464364c047fbfa7ede767336734949bce5d085fce15", - "6524936709e0719f9e28bf1caac91f86f323d1a79f10ab65a03fa9ea2e6b3270", - "8ba4736edc68da8e5b21c23af4de1f9131f47778c2a5b2a970a799223a9380b3", - "2aaba839e8546a8cb6d04a81ff5be83ac4e700ca4491607a16ebd60dc4995750", - "5f88f7f579ce50d7bcf60d6e9556f77acf361ba54caf3eb61dc4f2ca9c94c6a2", - "d1ca262272613d7386fecce7f1ad268188940b59ea83fdd1f8d7ab85771e3f43", - "5211890e4128d8d730802cc75ee105867e8878581d5e72ffc98a0d60027a5398", - "c1dececcbd7b623bfaa726392512e6ea110db93a0649dd01bf9e559318514f7c", - "43ce93c9835e983403a01329918d184832c5516e1b0ea4e0878b4c4def042777", - "c84724a398bda8d20436fb0663d059c196628fa71192bb720fa5adf6137bed9b", - "f307620a90a36045fa550316d61be4755c1f6c7f3fc81c95fc4d590ac568b2c4", - "78ae0118856a1f17c13f292628fcf3dccc150c7278578caaceb9f35ca8bc9f55", - "9d9a9a40c490f05d7dbafde8bf60d68aab7dcb79a7976eef62d1448a4549816b", - "f6017bec50563dbbb24b979214ae225f96b01dea5b090b174038ff2842a1c6b1", - "6dbec748ea87e959390b0df429c005fccffe9e672864f555c14c7a407b940870", - "11701e76233395ebdba8d1d50334d1a0cc3a158f3452260c9a7b9a067b78d753", - "7354f3829dd05c838aa16d66b00316f7994797927a4d3b594c8af04a2b4ae86a", - "14d254333c6240fbdacbbbe261f7ae15be2e83bffcf084f4f59f459bae0a4571", - "7849ac0f31056265efb0110038f86e61effe17b7db836377d88433b73312e65a", - "9018be885177c675baf9b36978428d841ca30a9185e11de1a31238a08e3ec1af", - "777c5715de7423597fea6c03c496ea46414f0508cd96e119261f002965beb805", - "b742b7509a97c2b346ea762f0510e8475b30dc988417f0f146ae00a800c6dd56", - "7af3029a826e6dfba23ad502901a3632ef4ee047bd45d618b1f106717b0af7ea", - "82390020b2bca461204cc49b20ee6fec1b93dad476726f377785d04040a4d6c3", - "6bc2990699be7f610c9f6a49bc7a19b034040727bc3de63fee89a1573fe00673", - "50c7c2e8e8c7f4aabffb195187a1ac1105f5712859f02d491d794ba2d8498e08", - "5d1d3769cd141603ce907df5dca159e389ca4d75338eddbe1b7db1c0766cbfd6", - "4d8ad7a761d068b7ef9b058fb4ba924b276e7a45a3ad47a79b767e08e821e038", - "d5b5b2167602f7277cdac76cfda8bdad0befcb8ff12df495dfabf40c91d1120c", - "6f2618272bb0164247e9b0f40b19f020c1c9253510dc658a3cc078d079ef01bf", - "fd502d5007bfc62a91eb7a34c1ae23b9de4a20cf5e1bbd86dadc421d7083099c", - "914813583b901044970b9c7d6dcdfde4344339325f5988d0dd681d377f1243fb", - "85cb7cf0245613d08b3027f879f58a6db645bf0db107242beb856b340ee49ee8", - "d6dbb18d5df5e92c8c74ee59aa86d19dff25a5d0f9d030e84285a686f86804d7", - "65309f82cd9f9926a578d09e1ad13909f68a06d02b853760731a4225f7aecef4", - "433519d13f2b64fe82a90cd054797d4779507fea93c1054bd51c35ba846a0f7f", - "62a55d4d6e4b6f992a1c1840f041585c957affcdbf875202cf9e1db9def05aeb", - "e19eb1eeabdeffa04ccd792eb09776690cf2121887b26a7c5d20d66e3e8fd0fb", - "a80951fc593035abb2dda045a2076d29a30f5cc470e0a20db39c3ec588ed61d8", - "4f1b39906714266a7a1936bfa8e08c5d891285dd2b188b4bcd4afb0107671004", - "f46310b2e60727af071d09e759814f52f933688a0ff0995c7da373b381e70005", - "2cc94821f56d21918ba4f2770d97f280e06fd99f4199167e2f4ddc780f404c8a", - "698a2ca7a09e88fa6594801da037f4dbce6ae841d1d33736aebcf8f6fe104e01", + "6e1f1626a9308abd0b7732fecbc7d49366ced46dead63ef3603fb0f4ec7aa53d", + "96ea95735b61c48c6f465db8ca0246a62539f34c3a977be6338770d92461da89", + "8c18c036b257d7b9c5a970a786efa04c320f3cf5afa73fa310a6603744d1ee3a", + "a7a6187e54e83acdb893d456c8b4ad6877e4738ea9e0d47c9b2070bf2c98a35f", + "836d72150b6f3033b6220d15ac9d49457d81041a700d12898bd3b8e737b4d265", + "e3d1c40538a8aed282a430647e3ff16ff94b64b8ed0a7cf427e8b0e2780a9b2a", + "2155224ef35cfb4bbdb06d982b42bb9b93d97b2bc691c22c804e1759a0a04882", + "5f6549ad9dd15dd57c204b1a9b60c68c376e77d7d114cfa13219e6a1a71f901f", + "ba8dd5a7070aee7102d4f8bc83ac497091e2e473a0f448a1c513569167dd0791", + "4f07e1da3685a858970a9f39300564971e97257f5d37b55a6d9882014aecc421", + "01aa9348244c4b65de3df758d49c98076e17a109aef04bc211abc77cf42dc51d", + "080574eb9e0613926e0e5d9b667c132e12965a1b1d47218a0b290a2d55c55d2f", + "08ce4840184f63a779bcd1f91bbcb56a2e8cba75c3ba2dbd290a559a57e4238e", + "27caa38c14a85ad64b4c4469a34e61a3965a1cbc4cad9a85707da374301c830d", + "eccc90741370ba4acc70bc65f4ccc38fddb1327d977c366156018974bc52fe4a", + "3759946d9e875ac2fc5c51ed850686193bb1e0716219e42efd52f3965993b275", + "db5fe28b8ddb9ae5e4f5e0e757ee60bc938bffbdfdd38de807939e212d3c5066", + "3f8f861e663accee51e68b987705ce647a7446b7f118abc94c632e158acbb44f", + "43bab95a047112dca6705506f916a97252363a20da4a4c0a5ab7b676e11083fc", + "d3018e45c66dd485697f738ad191af98267e6255d562496dd06b1a662e0c7a88", + "0ec2e8d07e053d686140857493ece653cde52764a60aa1d25651d81811d44a3d", + "4595bb092682af7177799464364c047fbfa7ede767336734949bce5d085fce15", + "6524936709e0719f9e28bf1caac91f86f323d1a79f10ab65a03fa9ea2e6b3270", + "8ba4736edc68da8e5b21c23af4de1f9131f47778c2a5b2a970a799223a9380b3", + "2aaba839e8546a8cb6d04a81ff5be83ac4e700ca4491607a16ebd60dc4995750", + "5f88f7f579ce50d7bcf60d6e9556f77acf361ba54caf3eb61dc4f2ca9c94c6a2", + "d1ca262272613d7386fecce7f1ad268188940b59ea83fdd1f8d7ab85771e3f43", + "5211890e4128d8d730802cc75ee105867e8878581d5e72ffc98a0d60027a5398", + "c1dececcbd7b623bfaa726392512e6ea110db93a0649dd01bf9e559318514f7c", + "43ce93c9835e983403a01329918d184832c5516e1b0ea4e0878b4c4def042777", + "c84724a398bda8d20436fb0663d059c196628fa71192bb720fa5adf6137bed9b", + "f307620a90a36045fa550316d61be4755c1f6c7f3fc81c95fc4d590ac568b2c4", + "78ae0118856a1f17c13f292628fcf3dccc150c7278578caaceb9f35ca8bc9f55", + "9d9a9a40c490f05d7dbafde8bf60d68aab7dcb79a7976eef62d1448a4549816b", + "f6017bec50563dbbb24b979214ae225f96b01dea5b090b174038ff2842a1c6b1", + "6dbec748ea87e959390b0df429c005fccffe9e672864f555c14c7a407b940870", + "11701e76233395ebdba8d1d50334d1a0cc3a158f3452260c9a7b9a067b78d753", + "7354f3829dd05c838aa16d66b00316f7994797927a4d3b594c8af04a2b4ae86a", + "14d254333c6240fbdacbbbe261f7ae15be2e83bffcf084f4f59f459bae0a4571", + "7849ac0f31056265efb0110038f86e61effe17b7db836377d88433b73312e65a", + "9018be885177c675baf9b36978428d841ca30a9185e11de1a31238a08e3ec1af", + "777c5715de7423597fea6c03c496ea46414f0508cd96e119261f002965beb805", + "b742b7509a97c2b346ea762f0510e8475b30dc988417f0f146ae00a800c6dd56", + "7af3029a826e6dfba23ad502901a3632ef4ee047bd45d618b1f106717b0af7ea", + "82390020b2bca461204cc49b20ee6fec1b93dad476726f377785d04040a4d6c3", + "6bc2990699be7f610c9f6a49bc7a19b034040727bc3de63fee89a1573fe00673", + "50c7c2e8e8c7f4aabffb195187a1ac1105f5712859f02d491d794ba2d8498e08", + "5d1d3769cd141603ce907df5dca159e389ca4d75338eddbe1b7db1c0766cbfd6", + "4d8ad7a761d068b7ef9b058fb4ba924b276e7a45a3ad47a79b767e08e821e038", + "d5b5b2167602f7277cdac76cfda8bdad0befcb8ff12df495dfabf40c91d1120c", + "6f2618272bb0164247e9b0f40b19f020c1c9253510dc658a3cc078d079ef01bf", + "fd502d5007bfc62a91eb7a34c1ae23b9de4a20cf5e1bbd86dadc421d7083099c", + "914813583b901044970b9c7d6dcdfde4344339325f5988d0dd681d377f1243fb", + "85cb7cf0245613d08b3027f879f58a6db645bf0db107242beb856b340ee49ee8", + "d6dbb18d5df5e92c8c74ee59aa86d19dff25a5d0f9d030e84285a686f86804d7", + "65309f82cd9f9926a578d09e1ad13909f68a06d02b853760731a4225f7aecef4", + "433519d13f2b64fe82a90cd054797d4779507fea93c1054bd51c35ba846a0f7f", + "62a55d4d6e4b6f992a1c1840f041585c957affcdbf875202cf9e1db9def05aeb", + "e19eb1eeabdeffa04ccd792eb09776690cf2121887b26a7c5d20d66e3e8fd0fb", + "a80951fc593035abb2dda045a2076d29a30f5cc470e0a20db39c3ec588ed61d8", + "4f1b39906714266a7a1936bfa8e08c5d891285dd2b188b4bcd4afb0107671004", + "f46310b2e60727af071d09e759814f52f933688a0ff0995c7da373b381e70005", + "2cc94821f56d21918ba4f2770d97f280e06fd99f4199167e2f4ddc780f404c8a", + "698a2ca7a09e88fa6594801da037f4dbce6ae841d1d33736aebcf8f6fe104e01", ]; diff --git a/src/lib/samples.ts b/src/lib/samples.ts index 17e4f4c8..1cd74409 100644 --- a/src/lib/samples.ts +++ b/src/lib/samples.ts @@ -1,48 +1,48 @@ import { PUBLIC_DOMAIN, PUBLIC_COINOS_URL } from "$env/static/public"; let api = PUBLIC_DOMAIN.includes("localhost") - ? `${PUBLIC_COINOS_URL}` - : `https://${PUBLIC_DOMAIN}/api`; + ? `${PUBLIC_COINOS_URL}` + : `https://${PUBLIC_DOMAIN}/api`; export default { - register: `curl "${api}/register" -H "content-type: application/json" -d '{ + register: `curl "${api}/register" -H "content-type: application/json" -d '{ "user": { "username": "demo", "password": "hunter2" } }'`, - login: `curl "${api}/login" -H "content-type: application/json" -d '{ + login: `curl "${api}/login" -H "content-type: application/json" -d '{ "username": "demo", "password": "hunter2" }'`, - payments: `curl "${api}/payments" + payments: `curl "${api}/payments" -H "content-type: application/json" -H "Authorization: Bearer $token" `, - lightning: `curl "${api}/payments" + lightning: `curl "${api}/payments" -H "content-type: application/json" -H "Authorization: Bearer $token" -d '{ "payreq": "lnbc1pj94d8fsp5n77k340ps4m9jn7kp8he8yynpddvurv6mcsrrqpnq5l2jxdxzlwqpp5m96pqhc5nrlk8cqsu9ufdxxa43sarp8vwf9egvm2pg9nl0zu9r8qdq2vdhkjmn0wvxqztgcqpjrzjqwhmav82kntsppmkp8jp4vg4h9nns78tsy8mg7ve4lq5txrkp0h56zlarvqqdtcqqsqqqqqqqqqqp6cq9q9qyysgqzxlypywzphyujm3ga5j5csfcmqvlnae0fgnvymkaaw94eeg7py5rqzysyjkr3ev2snq63qpsc69vf54adkd0szvmvwt5cuadjnuy95sq4xfa40" }'`, - bitcoin: `curl "${api}/bitcoin/send" + bitcoin: `curl "${api}/bitcoin/send" -H "content-type: application/json" -H "Authorization: Bearer $token" -d '{ "amount": 5000, "address": "bc1q3unh97w4rmelflrm2hvdwz37d8kray3vn4d5ca" }'`, - internal: `curl "${api}/send" + internal: `curl "${api}/send" -H "content-type: application/json" -H "Authorization: Bearer $token" -d '{ "amount": 5000, "username": "alice" }'`, - rates: `curl "${api}/rates" + rates: `curl "${api}/rates" -H "content-type: application/json" }'`, - lightningInvoice: `curl "${api}/invoice" + lightningInvoice: `curl "${api}/invoice" -H "content-type: application/json" -H "Authorization: Bearer $token" -d '{ @@ -51,7 +51,7 @@ export default { "type": "lightning" } }'`, - bitcoinAddress: `curl "${api}/invoice" + bitcoinAddress: `curl "${api}/invoice" -H "content-type: application/json" -H "Authorization: Bearer $token" -d '{ @@ -60,7 +60,7 @@ export default { "type": "bitcoin" } }'`, - webhook: `curl "${api}/invoice" + webhook: `curl "${api}/invoice" -H "content-type: application/json" -H "Authorization: Bearer $token" -d '{ @@ -70,9 +70,9 @@ export default { "secret": "webhooksecret" } }'`, - fetchInvoice: `curl "${api}/invoice/bc1qmhfk9stzffhd9umzmld92vff7zg3mdlh7rvvaj" + fetchInvoice: `curl "${api}/invoice/bc1qmhfk9stzffhd9umzmld92vff7zg3mdlh7rvvaj" -H "content-type: application/json"'`, - invoiceResponse: `{ + invoiceResponse: `{ "amount":3141 "tip":0 "type":"bitcoin" @@ -85,5 +85,5 @@ export default { "received":0 "created":1677537428134 }`, - socketAuth: `echo '{"type":"login","data":{"username":"user","password":"password"}}' | websocat -n https://coinos.io/ws`, + socketAuth: `echo '{"type":"login","data":{"username":"user","password":"password"}}' | websocat -n https://coinos.io/ws`, }; diff --git a/src/lib/socket.ts b/src/lib/socket.ts index 26ab543c..bbedd3ed 100644 --- a/src/lib/socket.ts +++ b/src/lib/socket.ts @@ -11,44 +11,44 @@ let token; export const auth = () => token && send("login", token); export const send = async (type, data) => { - try { - await wait(() => socket && socket.readyState === 1, 100, 10); - } catch (e: any) { - if (e.message === "timeout") reconnectToWebsocket(); - } - - await wait(() => socket.readyState === 1, 100, 10); - socket.send(JSON.stringify({ type, data })); + try { + await wait(() => socket && socket.readyState === 1, 100, 10); + } catch (e: any) { + if (e.message === "timeout") reconnectToWebsocket(); + } + + await wait(() => socket.readyState === 1, 100, 10); + socket.send(JSON.stringify({ type, data })); }; export const messages = (data) => ({ - id() { - last.set(Date.now()); - }, - invoice() { - invoice.set(data); - }, - event() { - event.set(data); - }, - - request() { - request.set(data); - invalidate("app:invoice"); - }, - - async payment() { - let { amount, confirmed } = data; - invalidate("app:user"); - invalidate("app:invoice"); - invalidate("app:payments"); - - if (amount > 0) { - success(`${confirmed ? "Received" : "Detected"} ${sat(amount)}!`); - } - }, - - connected: auth, + id() { + last.set(Date.now()); + }, + invoice() { + invoice.set(data); + }, + event() { + event.set(data); + }, + + request() { + request.set(data); + invalidate("app:invoice"); + }, + + async payment() { + let { amount, confirmed } = data; + invalidate("app:user"); + invalidate("app:invoice"); + invalidate("app:payments"); + + if (amount > 0) { + success(`${confirmed ? "Received" : "Detected"} ${sat(amount)}!`); + } + }, + + connected: auth, }); const initialReconnectDelay = 1000; @@ -57,41 +57,44 @@ const maxReconnectDelay = 16000; let currentReconnectDelay = initialReconnectDelay; export function connect(t) { - token = t; + token = t; - if (socket) return auth(); + if (socket) return auth(); - socket = new WebSocket(PUBLIC_SOCKET); - socket.addEventListener("open", onWebsocketOpen); - socket.addEventListener("close", onWebsocketClose); - socket.addEventListener("message", onWebsocketMessage); + socket = new WebSocket(PUBLIC_SOCKET); + socket.addEventListener("open", onWebsocketOpen); + socket.addEventListener("close", onWebsocketClose); + socket.addEventListener("message", onWebsocketMessage); } export function close() { - if (socket) socket.close(); + if (socket) socket.close(); } async function onWebsocketMessage(msg) { - let { type, data } = JSON.parse(msg.data); - if (get(navigating)) await sleep(2000); + let { type, data } = JSON.parse(msg.data); + if (get(navigating)) await sleep(2000); - messages(data)[type] && messages(data)[type](); + messages(data)[type] && messages(data)[type](); } function onWebsocketOpen() { - currentReconnectDelay = initialReconnectDelay; + currentReconnectDelay = initialReconnectDelay; } function onWebsocketClose() { - socket = null; - setTimeout(() => { - reconnectToWebsocket(); - }, currentReconnectDelay + Math.floor(Math.random() * 3000)); + socket = null; + setTimeout( + () => { + reconnectToWebsocket(); + }, + currentReconnectDelay + Math.floor(Math.random() * 3000), + ); } function reconnectToWebsocket() { - if (currentReconnectDelay < maxReconnectDelay) { - currentReconnectDelay *= 2; - } - connect(token); + if (currentReconnectDelay < maxReconnectDelay) { + currentReconnectDelay *= 2; + } + connect(token); } diff --git a/src/lib/store.ts b/src/lib/store.ts index 42b41127..1183f049 100644 --- a/src/lib/store.ts +++ b/src/lib/store.ts @@ -2,40 +2,40 @@ import { writable } from "svelte/store"; import { browser } from "$app/environment"; let persistSession = (key, defaultValue = undefined) => { - let s = writable( - browser && - sessionStorage.getItem(key) && - sessionStorage.getItem(key) !== "undefined" - ? JSON.parse(sessionStorage.getItem(key) || "") - : defaultValue - ); + let s = writable( + browser && + sessionStorage.getItem(key) && + sessionStorage.getItem(key) !== "undefined" + ? JSON.parse(sessionStorage.getItem(key) || "") + : defaultValue, + ); - s.subscribe((v) => { - browser && sessionStorage.setItem(key, JSON.stringify(v)); - }); + s.subscribe((v) => { + browser && sessionStorage.setItem(key, JSON.stringify(v)); + }); - return s; + return s; }; let persistLocal = (key, defaultValue: any = undefined) => { - let s = writable( - browser && - localStorage.getItem(key) && - localStorage.getItem(key) !== "undefined" - ? JSON.parse(localStorage.getItem(key) || "") - : defaultValue - ); + let s = writable( + browser && + localStorage.getItem(key) && + localStorage.getItem(key) !== "undefined" + ? JSON.parse(localStorage.getItem(key) || "") + : defaultValue, + ); - s.subscribe((v) => { - try { - browser && localStorage.setItem(key, JSON.stringify(v)); - } catch (e) { - console.log("problem setting key", v); - console.log(e); - } - }); + s.subscribe((v) => { + try { + browser && localStorage.setItem(key, JSON.stringify(v)); + } catch (e) { + console.log("problem setting key", v); + console.log(e); + } + }); - return s; + return s; }; export let account = writable(); diff --git a/src/lib/ticket.ts b/src/lib/ticket.ts index 73ef7716..557b278f 100644 --- a/src/lib/ticket.ts +++ b/src/lib/ticket.ts @@ -3,29 +3,29 @@ import { env } from "$env/dynamic/private"; import tickets from "$lib/tickets"; export const createTicket = async (cookies, username) => { - let { ticket } = await get("/ticket"); + let { ticket } = await get("/ticket"); - let { address } = await post( - `/${username}/invoice`, - { invoice: { network: "liquid" }, user: { username } }, - auth(cookies) - ); + let { address } = await post( + `/${username}/invoice`, + { invoice: { network: "liquid" }, user: { username } }, + auth(cookies), + ); - let { asset } = await post( - "/assets", - { - address, - name: `Launch Party Ticket ${ticket}`, - ticker: "LPT", - precision: 0, - asset_amount: "1", - token_amount: 0, - domain: "coinos.io", - filename: tickets[ticket], - }, - { authorization: `Bearer ${env.LAUNCH}` } - ); + let { asset } = await post( + "/assets", + { + address, + name: `Launch Party Ticket ${ticket}`, + ticker: "LPT", + precision: 0, + asset_amount: "1", + token_amount: 0, + domain: "coinos.io", + filename: tickets[ticket], + }, + { authorization: `Bearer ${env.LAUNCH}` }, + ); - await post("/ticket", { asset }, { authorization: `Bearer ${env.LAUNCH}` }); - return asset; + await post("/ticket", { asset }, { authorization: `Bearer ${env.LAUNCH}` }); + return asset; }; diff --git a/src/lib/tickets.ts b/src/lib/tickets.ts index 1f8cbdac..f37fad78 100644 --- a/src/lib/tickets.ts +++ b/src/lib/tickets.ts @@ -1,52 +1,52 @@ export default [ - "QmPbFkiXNuaQyfwD4W9GcXP7qWaNd6wFd9KCnJir5mes1J", - "QmPVjvbaMxSJRpDxMLYTdUWQbknej1vYLckBQ2pAgxZgJN", - "QmVasycSFzCYhyXrcvTE7mN3d6W78RFnrhzg5cRF6Dp6Zq", - "QmTmnhjpTBjZdeHwXgT9Dftc3uWMW73qsZpZ5ECrTyiKKZ", - "QmWDBiyVPyFkzUrEdvy7DSJe1rBQpene1m6quKAtyjBP6a", - "QmcxxzQZwgG7SE9QbHNvoTiCi6Dhs9rrz9roqvxm2JQA88", - "Qmd9ThEpXLLHimEzRUWEFrAGLSw5PAHzn2SmwfCnjNLwWt", - "Qmc7pWw1yxc2ARBWitDMYiyPkWK61NsXXU8oYAQwNAvcx1", - "QmX9Hgw1rMFUgBBuq8QfZZDzLjN5xCED19fwFdDeDDqwmr", - "QmconCAyNyaagqKvvfKCbG7Eu8c6vCtkv2ivvxMoFN9YwA", - "QmWbFmvViy5gx4JdD5sw5YJETet4ZQcXtYgijYnrsnSZ3a", - "QmP6QiiJwoqkduZMqdd5e5htHZ4aYHCsvEu7WEEUknKYa5", - "QmefHk8hvBUHsuKugntok5u75Q8i886cnPwsj4LZZB6uWg", - "QmdrSDK5ByYHxc88hfDu8hQgK5XYFpXSfPorUXdyTbE437", - "QmZ6ZycjRzYpsjnLm1Lfgk1fXXDdhcYRkyfmqYJZVGwADy", - "QmSkBxS5yLPjB2VPs89oQRZANsoyodK6s8s52E8GKXr2zt", - "QmSn8TCPwq3UZ1Z4eyko76CvQo2H2Hyef1NGboDQN22m5i", - "QmP6CTH5zrKMzZwtjy7VaK9NXiq4Gw53kmuRjUbJjeovoe", - "QmdwT4yvz9Xf8g3t6irKRoh3Wa7MwRQrcUJckQxyQe1Bc5", - "QmcwuHH3bD5JExqjDvKqRQ3V5xEVGqs94qPNVDzaCqJa5a", - "QmRfVduHwn8sXwSoyfgZpxSi8L7EjP4LMp6fmijo9VApDp", - "Qmc4TGj1Ahbg9HJ9WDXDm2bXsNkT4zRDp8Wdw9wqqdEwuH", - "QmY8uQiHMWA8ztf2wAurNFW7eiTHQEUiLTQ2vGceMwVqz3", - "QmXiXbaD12F4mkbUSQcKhWshkPU4oi29cUawZGnPk86VVU", - "QmZaiAGjvBZBw4suz33s9JQNzY2Kshean2mQQPCa6gKMQe", - "Qme8bDSdzX7tanPYf672xXW8vPdkLkeSLqLzHJqidRXsYJ", - "QmRJirjuUKgh2AMyBF4jNquaXHFPBtDJf3gKFC614hX7qu", - "QmdRoSKSgabcrpekNBPB6T47RBtJnCETYE8ry2a3P37MUe", - "QmXoqArruHSghrUGW35vVQHPknBG5Cj939iWWHHXU3B616", - "QmYCxtDuoHLMAFhNpifUsLU9ptAeFKwyxSiSEJhDQLMZtN", - "QmQZQBw8wgzUsrmVDEejdePK23AEJbk9aUracvxLPyHz2x", - "QmSsbu7U3i62AQKuYqfisRo5E6nqdMZfHRKWYJgouaXDxD", - "QmaP4adVQuWyqtN4QCXeYXj4nr19Je4EqsaTG1zM9ywqFS", - "QmPPsvgfXsiN3wNqdAwHAhQVpw6SZWNpry9Na6R2Xb7zXY", - "QmR7z6wQmcfZUvRsVK83GRRboyvwZEDQujLwumMY33nAit", - "QmQXgkgorDLPUyQpFbW8aLUW6jioQjUKamtXw5UuNCttDg", - "QmcwYBeXt1BPfdSrtSrXFpWctRZ7RDHvmKTttC5hPPTSzG", - "QmTUexZnw3xPy3YVdUsqJALwTvy2qFax5b3QpJVMvTCJQb", - "QmNLkYXFUvctpfeeDTZxruZE2f834EUnYMM6JJUeTcUB4J", - "QmT7544UaTPgKFYMCpPE4unXphNGX71BLZBPKLkxAijSmj", - "QmTpVR4dqX1QV4fae5pZ1ACtRcANHwEWCLwpMXFmwE95Sm", - "Qmdfyps5fW8d8wstXMfpvpei2DTmaADBWgmxLT7e7uan4P", - "QmUSK8byipYoG3H97QpmPrHw9vYrvHygyhkiM81XmaKyxS", - "QmQUGRvp96LTw5i8Ly9f5LoMvUpeQHCLGNqC88KV74wEHK", - "Qma693TFLh9Z1MeBFzhPHi6Yk4tmTcZY2jogwJ8cPkU2B4", - "QmNu8FyMpmkFdbCXGRVG8mKJtMvvCoLP8LNzFsxBQwxQeS", - "QmWZHE87PKujL1MGLuFgP3D12o5GQxYbvbeTRgr12f1XMu", - "QmcZJn8gPGd3poS9yuiNJh6iP4cn9uy5fnypky71r4sSSw", - "QmTwrAF4E9ucpdnrUZFj6EfrQamGv7Qyn7ceSx1DUQYceC", - "QmexssxU5iTXypgNqrmrQwMDJnTNxyZUxor5wDLnwb5TZp", + "QmPbFkiXNuaQyfwD4W9GcXP7qWaNd6wFd9KCnJir5mes1J", + "QmPVjvbaMxSJRpDxMLYTdUWQbknej1vYLckBQ2pAgxZgJN", + "QmVasycSFzCYhyXrcvTE7mN3d6W78RFnrhzg5cRF6Dp6Zq", + "QmTmnhjpTBjZdeHwXgT9Dftc3uWMW73qsZpZ5ECrTyiKKZ", + "QmWDBiyVPyFkzUrEdvy7DSJe1rBQpene1m6quKAtyjBP6a", + "QmcxxzQZwgG7SE9QbHNvoTiCi6Dhs9rrz9roqvxm2JQA88", + "Qmd9ThEpXLLHimEzRUWEFrAGLSw5PAHzn2SmwfCnjNLwWt", + "Qmc7pWw1yxc2ARBWitDMYiyPkWK61NsXXU8oYAQwNAvcx1", + "QmX9Hgw1rMFUgBBuq8QfZZDzLjN5xCED19fwFdDeDDqwmr", + "QmconCAyNyaagqKvvfKCbG7Eu8c6vCtkv2ivvxMoFN9YwA", + "QmWbFmvViy5gx4JdD5sw5YJETet4ZQcXtYgijYnrsnSZ3a", + "QmP6QiiJwoqkduZMqdd5e5htHZ4aYHCsvEu7WEEUknKYa5", + "QmefHk8hvBUHsuKugntok5u75Q8i886cnPwsj4LZZB6uWg", + "QmdrSDK5ByYHxc88hfDu8hQgK5XYFpXSfPorUXdyTbE437", + "QmZ6ZycjRzYpsjnLm1Lfgk1fXXDdhcYRkyfmqYJZVGwADy", + "QmSkBxS5yLPjB2VPs89oQRZANsoyodK6s8s52E8GKXr2zt", + "QmSn8TCPwq3UZ1Z4eyko76CvQo2H2Hyef1NGboDQN22m5i", + "QmP6CTH5zrKMzZwtjy7VaK9NXiq4Gw53kmuRjUbJjeovoe", + "QmdwT4yvz9Xf8g3t6irKRoh3Wa7MwRQrcUJckQxyQe1Bc5", + "QmcwuHH3bD5JExqjDvKqRQ3V5xEVGqs94qPNVDzaCqJa5a", + "QmRfVduHwn8sXwSoyfgZpxSi8L7EjP4LMp6fmijo9VApDp", + "Qmc4TGj1Ahbg9HJ9WDXDm2bXsNkT4zRDp8Wdw9wqqdEwuH", + "QmY8uQiHMWA8ztf2wAurNFW7eiTHQEUiLTQ2vGceMwVqz3", + "QmXiXbaD12F4mkbUSQcKhWshkPU4oi29cUawZGnPk86VVU", + "QmZaiAGjvBZBw4suz33s9JQNzY2Kshean2mQQPCa6gKMQe", + "Qme8bDSdzX7tanPYf672xXW8vPdkLkeSLqLzHJqidRXsYJ", + "QmRJirjuUKgh2AMyBF4jNquaXHFPBtDJf3gKFC614hX7qu", + "QmdRoSKSgabcrpekNBPB6T47RBtJnCETYE8ry2a3P37MUe", + "QmXoqArruHSghrUGW35vVQHPknBG5Cj939iWWHHXU3B616", + "QmYCxtDuoHLMAFhNpifUsLU9ptAeFKwyxSiSEJhDQLMZtN", + "QmQZQBw8wgzUsrmVDEejdePK23AEJbk9aUracvxLPyHz2x", + "QmSsbu7U3i62AQKuYqfisRo5E6nqdMZfHRKWYJgouaXDxD", + "QmaP4adVQuWyqtN4QCXeYXj4nr19Je4EqsaTG1zM9ywqFS", + "QmPPsvgfXsiN3wNqdAwHAhQVpw6SZWNpry9Na6R2Xb7zXY", + "QmR7z6wQmcfZUvRsVK83GRRboyvwZEDQujLwumMY33nAit", + "QmQXgkgorDLPUyQpFbW8aLUW6jioQjUKamtXw5UuNCttDg", + "QmcwYBeXt1BPfdSrtSrXFpWctRZ7RDHvmKTttC5hPPTSzG", + "QmTUexZnw3xPy3YVdUsqJALwTvy2qFax5b3QpJVMvTCJQb", + "QmNLkYXFUvctpfeeDTZxruZE2f834EUnYMM6JJUeTcUB4J", + "QmT7544UaTPgKFYMCpPE4unXphNGX71BLZBPKLkxAijSmj", + "QmTpVR4dqX1QV4fae5pZ1ACtRcANHwEWCLwpMXFmwE95Sm", + "Qmdfyps5fW8d8wstXMfpvpei2DTmaADBWgmxLT7e7uan4P", + "QmUSK8byipYoG3H97QpmPrHw9vYrvHygyhkiM81XmaKyxS", + "QmQUGRvp96LTw5i8Ly9f5LoMvUpeQHCLGNqC88KV74wEHK", + "Qma693TFLh9Z1MeBFzhPHi6Yk4tmTcZY2jogwJ8cPkU2B4", + "QmNu8FyMpmkFdbCXGRVG8mKJtMvvCoLP8LNzFsxBQwxQeS", + "QmWZHE87PKujL1MGLuFgP3D12o5GQxYbvbeTRgr12f1XMu", + "QmcZJn8gPGd3poS9yuiNJh6iP4cn9uy5fnypky71r4sSSw", + "QmTwrAF4E9ucpdnrUZFj6EfrQamGv7Qyn7ceSx1DUQYceC", + "QmexssxU5iTXypgNqrmrQwMDJnTNxyZUxor5wDLnwb5TZp", ]; diff --git a/src/lib/types.ts b/src/lib/types.ts index 827f96aa..19277ec1 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,23 +1,23 @@ export interface Message { - author: User; - created_at: number; - recipient: User; + author: User; + created_at: number; + recipient: User; } export interface Note { - created_at: number; - seen: number; + created_at: number; + seen: number; } export interface User { - id: string; + id: string; } export interface Invoice { - aid: string; - id?: string; - memoPrompt?: boolean; - memo?: string; - rate: number; - type?: string; + aid: string; + id?: string; + memoPrompt?: boolean; + memo?: string; + rate: number; + type?: string; } diff --git a/src/lib/upload.ts b/src/lib/upload.ts index 31c235c6..6bc5eaf3 100644 --- a/src/lib/upload.ts +++ b/src/lib/upload.ts @@ -1,18 +1,18 @@ export const upload = async (file, type, progress) => { - let url = `/api/upload/${type}`; - let formData = new FormData(); - formData.append("file", file); + let url = `/api/upload/${type}`; + let formData = new FormData(); + formData.append("file", file); - return new Promise((resolve, reject) => { - let ajax = new XMLHttpRequest(); - ajax.addEventListener("load", () => { - if (ajax.readyState === ajax.DONE) { - if (ajax.status === 200) resolve(ajax.response); - else reject(new Error(`Upload failed: ${ajax.response}`)); - } - }); - ajax.upload.addEventListener("progress", progress, false); - ajax.open("POST", url); - ajax.send(formData); - }); + return new Promise((resolve, reject) => { + let ajax = new XMLHttpRequest(); + ajax.addEventListener("load", () => { + if (ajax.readyState === ajax.DONE) { + if (ajax.status === 200) resolve(ajax.response); + else reject(new Error(`Upload failed: ${ajax.response}`)); + } + }); + ajax.upload.addEventListener("progress", progress, false); + ajax.open("POST", url); + ajax.send(formData); + }); }; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 2ecd332b..96d07703 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -6,355 +6,359 @@ import { page } from "$app/stores"; import { get as getStore } from "svelte/store"; export function scroll(section: any) { - if (getStore(page).url.pathname !== "/") goto("/"); - setTimeout(() => section.scrollIntoView({ behavior: "smooth" }), 500); + if (getStore(page).url.pathname !== "/") goto("/"); + setTimeout(() => section.scrollIntoView({ behavior: "smooth" }), 500); } let base = browser ? "" : PUBLIC_COINOS_URL; export let punk = (k: string) => - Math.floor((parseInt(k.slice(-2), 16) / 256) * 64) + 1 + ".webp"; + Math.floor((parseInt(k.slice(-2), 16) / 256) * 64) + 1 + ".webp"; export let g = (url: string, fetch: any, headers: object) => - fetch(base + url, { headers }) - .then((r: Response) => r.text()) - .then((body: string) => { - try { - return JSON.parse(body); - } catch (e) { - throw new Error(body); - } - }); + fetch(base + url, { headers }) + .then((r: Response) => r.text()) + .then((body: string) => { + try { + return JSON.parse(body); + } catch (e) { + throw new Error(body); + } + }); export let get = (url: string, headers = {}) => { - return fetch(base + url, { headers }) - .then(async (r) => { - if (r.ok) return r.text(); - throw new Error(await r.text()); - }) - .then((body) => { - try { - return JSON.parse(body); - } catch (e) { - return body; - } - }); + return fetch(base + url, { headers }) + .then(async (r) => { + if (r.ok) return r.text(); + throw new Error(await r.text()); + }) + .then((body) => { + try { + return JSON.parse(body); + } catch (e) { + return body; + } + }); }; export let post = async ( - url: string, - body: object, - headers: HeadersInit | undefined = undefined + url: string, + body: object, + headers: HeadersInit | undefined = undefined, ) => { - headers = { - "content-type": "application/json", - accept: "application/json", - ...headers, - }; - - let response = await fetch(base + url, { - method: "POST", - body: JSON.stringify(body), - headers, - }) - .then(async (r) => { - if (r.ok) return r.text(); - let text = await r.text(); - - let message; - try { - ({ message } = JSON.parse(text)); - } catch (e) {} - - if (message) throw new Error(message); - throw new Error(text); - }) - .then((body) => { - let json; - try { - json = JSON.parse(body); - } catch (e) { - throw new Error(body); - } - - if (json.error instanceof Error) throw json.error; - if (json instanceof Error) throw body; - if (json.name === "Error") throw new Error(json.message); - - return json; - }); - - return response; + headers = { + "content-type": "application/json", + accept: "application/json", + ...headers, + }; + + let response = await fetch(base + url, { + method: "POST", + body: JSON.stringify(body), + headers, + }) + .then(async (r) => { + if (r.ok) return r.text(); + let text = await r.text(); + + let message; + try { + ({ message } = JSON.parse(text)); + } catch (e) {} + + if (message) throw new Error(message); + throw new Error(text); + }) + .then((body) => { + let json; + try { + json = JSON.parse(body); + } catch (e) { + throw new Error(body); + } + + if (json.error instanceof Error) throw json.error; + if (json instanceof Error) throw body; + if (json.name === "Error") throw new Error(json.message); + + return json; + }); + + return response; }; export let copy = (text: string) => { - navigator.clipboard.writeText(text); - success("Copied!"); + navigator.clipboard.writeText(text); + success("Copied!"); }; export let copyNoNewlines = (text: string) => { - let stripped = text.replace(/\n/g, " ").replace(/\s+/g, " "); - navigator.clipboard.writeText(stripped); - success("Copied!"); + let stripped = text.replace(/\n/g, " ").replace(/\s+/g, " "); + navigator.clipboard.writeText(stripped); + success("Copied!"); }; export function reverseFormat(val: string, locale: string): number { - let parts = new Intl.NumberFormat(locale).formatToParts(1111.1); - let group = parts.find((part) => part.type === "group")!.value; - let decimal = parts.find((part) => part.type === "decimal")!.value; - let reversedVal = val.replace(new RegExp("\\" + group, "g"), ""); - reversedVal = reversedVal.replace(new RegExp("\\" + decimal, "g"), "."); - return Number.isNaN(reversedVal) ? 0 : +reversedVal; + let parts = new Intl.NumberFormat(locale).formatToParts(1111.1); + let group = parts.find((part) => part.type === "group")!.value; + let decimal = parts.find((part) => part.type === "decimal")!.value; + let reversedVal = val.replace(new RegExp("\\" + group, "g"), ""); + reversedVal = reversedVal.replace(new RegExp("\\" + decimal, "g"), "."); + return Number.isNaN(reversedVal) ? 0 : +reversedVal; } export let protectedRoutes: RegExp[] = [/customers/, /settings/, /payments/]; let recent: string[] = []; export let success = (m: string, clear: boolean = true) => { - if (recent.includes(m)) return; - recent.push(m); - setTimeout(() => (recent = []), 5000); - if (clear) toast.pop(); - toast.push(m, { - theme: { - "--toastBarBackground": "#F5F7FA", - }, - }); + if (recent.includes(m)) return; + recent.push(m); + setTimeout(() => (recent = []), 5000); + if (clear) toast.pop(); + toast.push(m, { + theme: { + "--toastBarBackground": "#F5F7FA", + }, + }); }; export let warning = (m: string, clear: boolean = true) => { - if (clear) toast.pop(); - toast.push(m, { - theme: { - "--toastBarBackground": "#FFCE22", - }, - }); + if (clear) toast.pop(); + toast.push(m, { + theme: { + "--toastBarBackground": "#FFCE22", + }, + }); }; export let fail = (m: string, clear: boolean = true) => { - if (clear) toast.pop(); - toast.push(m, { - theme: { - "--toastBarBackground": "#E93535", - }, - }); + if (clear) toast.pop(); + toast.push(m, { + theme: { + "--toastBarBackground": "#E93535", + }, + }); }; export let info = (m: string) => { - toast.pop(); - toast.push(m, { - theme: { - "--toastBarBackground": "white", - }, - }); + toast.pop(); + toast.push(m, { + theme: { + "--toastBarBackground": "white", + }, + }); }; export let login = async ( - user: { username: string; password: string }, - cookies: any, - ip: string + user: { username: string; password: string }, + cookies: any, + ip: string, ) => { - let maxAge = 380 * 24 * 60 * 60; - - let res = await fetch(base + "/login", { - method: "POST", - body: JSON.stringify(user), - headers: { - "content-type": "application/json", - accept: "application/json", - "cf-connecting-ip": ip, - }, - }); - if (res.status === 401) { - let text = await res.text(); - if (text.startsWith("2fa")) throw new Error("2fa"); - } - - let { user: u, token } = await res.json(); - if (!token) throw new Error("Login failed"); - - let expires = new Date(); - expires.setSeconds(expires.getSeconds() + maxAge); - - let opts = { path: "/", expires }; - if (u.language) cookies.set("lang", u.language, opts); - cookies.set("username", user.username, opts); - cookies.set("token", token, opts); + let maxAge = 380 * 24 * 60 * 60; + + let res = await fetch(base + "/login", { + method: "POST", + body: JSON.stringify(user), + headers: { + "content-type": "application/json", + accept: "application/json", + "cf-connecting-ip": ip, + }, + }); + if (res.status === 401) { + let text = await res.text(); + if (text.startsWith("2fa")) throw new Error("2fa"); + } + + let { user: u, token } = await res.json(); + if (!token) throw new Error("Login failed"); + + let expires = new Date(); + expires.setSeconds(expires.getSeconds() + maxAge); + + let opts = { path: "/", expires }; + if (u.language) cookies.set("lang", u.language, opts); + cookies.set("username", user.username, opts); + cookies.set("token", token, opts); }; export let auth = (cookies: any) => ({ - authorization: `Bearer ${cookies.get("token")}`, + authorization: `Bearer ${cookies.get("token")}`, }); export let btc = (fiat: number, rate: number): number => - Math.round((fiat * sats) / rate); + Math.round((fiat * sats) / rate); export let fiat = (amount: number, rate: number): number => - (amount * rate) / sats; + (amount * rate) / sats; export let fd = async (req: Request): Promise => { - let obj: Record = Object.fromEntries(await req.formData()); - for (let k in obj) - (obj[k] === "undefined" && (obj[k] = undefined)) || - (obj[k] === "false" && (obj[k] = false)); - return obj; + let obj: Record = Object.fromEntries(await req.formData()); + for (let k in obj) + (obj[k] === "undefined" && (obj[k] = undefined)) || + (obj[k] === "false" && (obj[k] = false)); + return obj; }; export let f = (s: number, currency: string): string => { - try { - return new Intl.NumberFormat("en-US", { - style: "currency", - currency, - }) - .format(s) - .replace("CA", ""); - } catch (e) { - return ""; - } + try { + return new Intl.NumberFormat("en-US", { + style: "currency", + currency, + }) + .format(s) + .replace("CA", ""); + } catch (e) { + return ""; + } }; export let s = (s: number): string => - new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(s); + new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(s); export let sat = (s: number): string => "⚡️" + si(Math.abs(s)).toString(); export let si = (s: number) => { - if (s < 1000) return s.toString(); // Return the original number if it's less than 1000 + if (s < 1000) return s.toString(); // Return the original number if it's less than 1000 - const units = ["", "K", "M", "G", "T", "P", "E", "Z"]; - const d = Math.floor(Math.log10(s) / 3); // Calculate the unit index - const scaled = s / Math.pow(1000, d); // Scale the number to the correct unit + const units = ["", "K", "M", "G", "T", "P", "E", "Z"]; + const d = Math.floor(Math.log10(s) / 3); // Calculate the unit index + const scaled = s / Math.pow(1000, d); // Scale the number to the correct unit - return new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format(scaled) + units[d]; + return ( + new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format( + scaled, + ) + units[d] + ); }; export let sats = 100000000; export let back = (): any => - browser && (history.length ? history.go(-1) : goto("/")); + browser && (history.length ? history.go(-1) : goto("/")); export let focus = (el: HTMLElement): any => - browser && screen.width > 1280 && setTimeout(() => el.focus(), 1); + browser && screen.width > 1280 && setTimeout(() => el.focus(), 1); export let select = (el: HTMLInputElement): any => - browser && screen.width > 1280 && setTimeout(() => el.select(), 1); + browser && screen.width > 1280 && setTimeout(() => el.select(), 1); export let sleep = (n: number): Promise => - new Promise((r) => setTimeout(r, n)); + new Promise((r) => setTimeout(r, n)); export let wait = async ( - f: () => boolean | Promise, - n: number = 100, - s: number = 300 + f: () => boolean | Promise, + n: number = 100, + s: number = 300, ): Promise => { - let i = 0; - while (!(await f()) && i < s) { - await sleep(n); - i++; - } - if (i >= s) throw new Error("timeout"); - return f(); + let i = 0; + while (!(await f()) && i < s) { + await sleep(n); + i++; + } + if (i >= s) throw new Error("timeout"); + return f(); }; export let stretch = async (password: string, salt: Uint8Array) => - crypto.subtle.deriveKey( - { - name: "PBKDF2", - salt, - iterations: 100000, - hash: "SHA-256", - }, - await crypto.subtle.importKey( - "raw", - new TextEncoder().encode(password), - { name: "PBKDF2" }, - false, - ["deriveBits", "deriveKey"] - ), - { name: "AES-GCM", length: 256 }, - true, - ["encrypt", "decrypt"] - ); + crypto.subtle.deriveKey( + { + name: "PBKDF2", + salt, + iterations: 100000, + hash: "SHA-256", + }, + await crypto.subtle.importKey( + "raw", + new TextEncoder().encode(password), + { name: "PBKDF2" }, + false, + ["deriveBits", "deriveKey"], + ), + { name: "AES-GCM", length: 256 }, + true, + ["encrypt", "decrypt"], + ); export let types = { - bitcoin: "bitcoin", - liquid: "liquid", - lightning: "lightning", - internal: "internal", - fund: "fund", - ecash: "ecash", - reconcile: "reconcile", + bitcoin: "bitcoin", + liquid: "liquid", + lightning: "lightning", + internal: "internal", + fund: "fund", + ecash: "ecash", + reconcile: "reconcile", }; export let ease = (t: number): number => - t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t; + t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t; export function shuffleArray(array: T[]): void { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } } export let closest = (a: number[], n: number): number => - a.reduce((prev, curr) => - Math.abs(curr - n) < Math.abs(prev - n) ? curr : prev - ); + a.reduce((prev, curr) => + Math.abs(curr - n) < Math.abs(prev - n) ? curr : prev, + ); export let isLiquid = (text: string): boolean => - text.startsWith("Az") || - text.startsWith("lq1") || - text.startsWith("VJL") || - text.startsWith("VT") || - text.startsWith("XR") || - text.startsWith("XC") || - ((text.startsWith("H") || text.startsWith("G") || text.startsWith("Q")) && - text.length === 34) || - (text.startsWith("ert1q") && text.length === 43) || - (text.startsWith("ex1q") && text.length === 42) || - text.startsWith("el1qq") || - text.startsWith("lq1qq"); + text.startsWith("Az") || + text.startsWith("lq1") || + text.startsWith("VJL") || + text.startsWith("VT") || + text.startsWith("XR") || + text.startsWith("XC") || + ((text.startsWith("H") || text.startsWith("G") || text.startsWith("Q")) && + text.length === 34) || + (text.startsWith("ert1q") && text.length === 43) || + (text.startsWith("ex1q") && text.length === 42) || + text.startsWith("el1qq") || + text.startsWith("lq1qq"); export function getCookie(name: string): string | null { - let nameEQ = name + "="; - let ca = document.cookie.split(";"); - for (let i = 0; i < ca.length; i++) { - let c = ca[i]; - while (c.charAt(0) === " ") c = c.substring(1); - if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); - } - return null; + let nameEQ = name + "="; + let ca = document.cookie.split(";"); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) === " ") c = c.substring(1); + if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); + } + return null; } export function setCookie(name: string, value: string, seconds: number): void { - const now = new Date(); - now.setTime(now.getTime() + seconds * 1000); - const expires = "expires=" + now.toUTCString(); - document.cookie = name + "=" + value + ";" + expires + ";path=/"; + const now = new Date(); + now.setTime(now.getTime() + seconds * 1000); + const expires = "expires=" + now.toUTCString(); + document.cookie = name + "=" + value + ";" + expires + ";path=/"; } export let network = { - bitcoin: { - bech32: "bc", - pubKeyHash: 0x00, - scriptHash: 0x05, - wif: 0x80, - }, - regtest: { - bech32: "bcrt", - pubKeyHash: 0x6f, - scriptHash: 0xc4, - wif: 0xef, - }, + bitcoin: { + bech32: "bc", + pubKeyHash: 0x00, + scriptHash: 0x05, + wif: 0x80, + }, + regtest: { + bech32: "bcrt", + pubKeyHash: 0x6f, + scriptHash: 0xc4, + wif: 0xef, + }, }[PUBLIC_COINOS_NETWORK]; export let versions = { - bitcoin: { - private: 0x0488ade4, - public: 0x0488b21e, - }, - regtest: { - private: 0x04358394, - public: 0x043587cf, - }, + bitcoin: { + private: 0x0488ade4, + public: 0x0488b21e, + }, + regtest: { + private: 0x04358394, + public: 0x043587cf, + }, }[PUBLIC_COINOS_NETWORK]; diff --git a/src/locales/en.json b/src/locales/en.json index 3bc196e6..369533ad 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1,30 +1,30 @@ { - "accounts": { - "showQR": "Show QR", - "revealNwc": "Reveal NWC", - "finish": "Finish", - "back": "Back", - "next": "Next", - "seedPhrase": "Seed phrase", - "save": "Save", - "copy": "Copy", - "revealMnemonic": "Reveal seed phrase", - "regenerate": "Regenerate", - "deleteAccount": "Delete account", - "addAccount": "Add account", - "create": "Create", - "import": "Import", - "accountSetup": "Account setup", - "enterSeed": "Enter a seed phrase", - "passwordMismatch": "Passwords don't match", - "generateSeed": "Generate a new seed phrase", - "accountPassword": "Account password", - "importSeed": "Enter an existing seed phrase", - "passwordWarning": "If you lose this password you won't be able to withdraw funds from the account or restore your seed phrase. We can't recover it for you.", - "savings": "Savings", - "password": "Password", - "confirmPassword": "Confirm password" - }, + "accounts": { + "showQR": "Show QR", + "revealNwc": "Reveal NWC", + "finish": "Finish", + "back": "Back", + "next": "Next", + "seedPhrase": "Seed phrase", + "save": "Save", + "copy": "Copy", + "revealMnemonic": "Reveal seed phrase", + "regenerate": "Regenerate", + "deleteAccount": "Delete account", + "addAccount": "Add account", + "create": "Create", + "import": "Import", + "accountSetup": "Account setup", + "enterSeed": "Enter a seed phrase", + "passwordMismatch": "Passwords don't match", + "generateSeed": "Generate a new seed phrase", + "accountPassword": "Account password", + "importSeed": "Enter an existing seed phrase", + "passwordWarning": "If you lose this password you won't be able to withdraw funds from the account or restore your seed phrase. We can't recover it for you.", + "savings": "Savings", + "password": "Password", + "confirmPassword": "Confirm password" + }, "404": { "404": "404", "pageNotFound": "Page not found." @@ -45,7 +45,7 @@ } }, "locations": { - "viewMap": "View map", + "viewMap": "View map", "header": "Locations", "description": "Over 500 businesses worldwide are accepting bitcoin with Coinos. We hope that yours will be next!" } @@ -185,7 +185,7 @@ "subheader": "Latest Updates" }, "user": { - "url": "Public URL", + "url": "Public URL", "install": "Install app", "moreDetails": "Profile details", "noMessages": "You have not sent or received any messages", @@ -213,7 +213,7 @@ "this": "This", "lightningAddress": "Lightning address", "dashboard": { - "setup": "Setup self-custody", + "setup": "Setup self-custody", "payNow": "Pay now", "checkout": "Checkout", "go": "Go", @@ -255,7 +255,9 @@ "thresholdDesc": "When your balance exceeds this amount, a withdrawal will be triggered", "autoWithdraw": "Autowithdrawal", "autoWithdrawDescription": "Enable withdrawals to an external address when your balance reaches a given threshold", - "notifications": "Payment notifications", + "notifications": "Email notifications", + "pushNotifications": "Push notifications", + "pushNotificationsDesc": "Get push notifications to your phone's status bar", "notificationsDesc": "Receive an email whenever a payment lands in your account", "seedPhrase": "Seed phrase", "nostrKeys": "Nostr key", @@ -327,11 +329,11 @@ } }, "payments": { - "ok": "Ok", - "cancel": "Cancel", - "failedToRoute": "Failed to route payment, recipient may not be reachable. Try sending a smaller amount or increasing the max fee reserve.", - "message": "Message", - "enterWalletPass": "Wallet password", + "ok": "Ok", + "cancel": "Cancel", + "failedToRoute": "Failed to route payment, recipient may not be reachable. Try sending a smaller amount or increasing the max fee reserve.", + "message": "Message", + "enterWalletPass": "Wallet password", "legacyVersion": "Legacy version", "newVersion": "New version", "income": "Income", diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index db14b046..d8519a1f 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -19,7 +19,7 @@ import Password from "$comp/Password.svelte"; import { getCookie, warning, protectedRoutes } from "$lib/utils"; import { t, locale, loading } from "$lib/translations"; - import { goto, onNavigate } from "$app/navigation"; + import { goto, afterNavigate, preloadData } from "$app/navigation"; export let data; @@ -30,6 +30,10 @@ ({ rate, user, subject, token, rates } = data); }; + afterNavigate(() => { + preloadData(`/${user.username}/receive`); + }); + onMount(async () => { if (browser) { checkSocket(); diff --git a/src/routes/(app)/[username]/(profile)/+layout.server.ts b/src/routes/(app)/[username]/(profile)/+layout.server.ts index 73a24397..4592b81e 100644 --- a/src/routes/(app)/[username]/(profile)/+layout.server.ts +++ b/src/routes/(app)/[username]/(profile)/+layout.server.ts @@ -8,7 +8,9 @@ export async function load({ depends, parent }) { let { follows, followers } = await get(`/${subject.pubkey}/count`); let followList = new Promise((r) => r([])); if (user) - followList = get(`/${user.pubkey}/follows?pubkeysOnly=true`).catch(() => []); + followList = get(`/${user.pubkey}/follows?pubkeysOnly=true`).catch( + () => [], + ); return { follows, followers, followList }; } diff --git a/src/routes/(app)/[username]/(profile)/+layout.svelte b/src/routes/(app)/[username]/(profile)/+layout.svelte index d48fa279..5ad30f46 100644 --- a/src/routes/(app)/[username]/(profile)/+layout.svelte +++ b/src/routes/(app)/[username]/(profile)/+layout.svelte @@ -54,7 +54,7 @@ let follow = async () => { list = [...list, subject.pubkey]; let tags = await get( - `/api/${user.pubkey}/follows?pubkeysOnly=true&nocache=true` + `/api/${user.pubkey}/follows?pubkeysOnly=true&nocache=true`, ); tags.push(["p", subject.pubkey]); await update(tags); @@ -64,15 +64,15 @@ let unfollow = async () => { list.splice( list.findIndex((t) => t[1] === subject.pubkey), - 1 + 1, ); list = list; let tags = await get( - `/api/${user.pubkey}/follows?pubkeysOnly=true&nocache=true` + `/api/${user.pubkey}/follows?pubkeysOnly=true&nocache=true`, ); tags.splice( tags.findIndex((t) => t[1] === subject.pubkey), - 1 + 1, ); await update(tags); }; @@ -151,13 +151,17 @@
- {si(follows)} {$t("user.following")} {si(followers)} {$t("user.followers")} @@ -199,7 +203,7 @@ > diff --git a/src/routes/(app)/[username]/(profile)/followers/+page.svelte b/src/routes/(app)/[username]/(profile)/followers/+page.svelte index 19e4a9aa..c0080990 100644 --- a/src/routes/(app)/[username]/(profile)/followers/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/followers/+page.svelte @@ -14,11 +14,15 @@ {#await followers} -
Loading followers...
+
Loading followers...
{:then followers} {#if followers.length} -
+
- +
diff --git a/src/routes/(app)/[username]/+layout.server.ts b/src/routes/(app)/[username]/+layout.server.ts index 1b95bdd0..c7a5c44b 100644 --- a/src/routes/(app)/[username]/+layout.server.ts +++ b/src/routes/(app)/[username]/+layout.server.ts @@ -9,7 +9,7 @@ export let load = async ({ }) => { depends("app:user"); - try { + try { let subject = await get(`/users/${username}`); let { user } = await parent(); diff --git a/src/routes/(app)/account/[id]/+page.server.ts b/src/routes/(app)/account/[id]/+page.server.ts index 35b5517d..e521f15b 100644 --- a/src/routes/(app)/account/[id]/+page.server.ts +++ b/src/routes/(app)/account/[id]/+page.server.ts @@ -1,11 +1,10 @@ import { error, redirect } from "@sveltejs/kit"; import { auth, get, post, fd } from "$lib/utils"; - export let load = async ({ cookies, params }) => { let account = await get(`/account/${params.id}`, auth(cookies)); return { account }; -} +}; export const actions = { default: async ({ cookies, params, request }) => { diff --git a/src/routes/(app)/account/[id]/+page.svelte b/src/routes/(app)/account/[id]/+page.svelte index 3f7bb900..63fd7a42 100644 --- a/src/routes/(app)/account/[id]/+page.svelte +++ b/src/routes/(app)/account/[id]/+page.svelte @@ -37,10 +37,10 @@ } }; -let nwc; + let nwc; let revealNwc = () => (nwc = `nostr+walletconnect://${pk}?relay=${encodeURIComponent( - relay + relay, )}&secret=${user.nwc}`); diff --git a/src/routes/(app)/account/pass/+page.svelte b/src/routes/(app)/account/pass/+page.svelte index c063d647..5becf337 100644 --- a/src/routes/(app)/account/pass/+page.svelte +++ b/src/routes/(app)/account/pass/+page.svelte @@ -39,12 +39,12 @@ try { let seed = await encrypt( mnemonicToEntropy($mnemonic, wordlist), - password + password, ); let master = HDKey.fromMasterSeed( await mnemonicToSeed($mnemonic, password), - versions + versions, ); let child = master.derive("m/84'/0'/0'"); diff --git a/src/routes/(app)/account/savings/+page.svelte b/src/routes/(app)/account/savings/+page.svelte index f9c6305c..8cc24aa2 100644 --- a/src/routes/(app)/account/savings/+page.svelte +++ b/src/routes/(app)/account/savings/+page.svelte @@ -4,7 +4,9 @@
-

{$t("accounts.accountSetup")}

+

+ {$t("accounts.accountSetup")} +

diff --git a/src/routes/(app)/account/setup/+page.svelte b/src/routes/(app)/account/setup/+page.svelte index f5a5ac09..24b6e248 100644 --- a/src/routes/(app)/account/setup/+page.svelte +++ b/src/routes/(app)/account/setup/+page.svelte @@ -12,8 +12,7 @@
Cash
- Your balance is held with a custodian. Payments are fast and - free. + Your balance is held with a custodian. Payments are fast and free.
diff --git a/src/routes/(app)/admin/+page.server.ts b/src/routes/(app)/admin/+page.server.ts index 96a1cd78..40c56c77 100644 --- a/src/routes/(app)/admin/+page.server.ts +++ b/src/routes/(app)/admin/+page.server.ts @@ -6,7 +6,7 @@ export async function load({ cookies, parent }) { if (!user) redirect(307, "/login"); let users = (await get("/users", auth(cookies))).sort( - (a, b) => b.balance - a.balance + (a, b) => b.balance - a.balance, ); return { users }; } diff --git a/src/routes/(app)/ecash/[id]/[...version]/+layout.svelte b/src/routes/(app)/ecash/[id]/[...version]/+layout.svelte index 89de4b58..f9c80329 100644 --- a/src/routes/(app)/ecash/[id]/[...version]/+layout.svelte +++ b/src/routes/(app)/ecash/[id]/[...version]/+layout.svelte @@ -34,10 +34,10 @@ {/if}
- {$t("payments.from")} {mint} + {$t("payments.from")} + {mint}
- {#if spent > 0}
diff --git a/src/routes/(app)/ecash/[id]/[...version]/swap/+page.server.ts b/src/routes/(app)/ecash/[id]/[...version]/swap/+page.server.ts index c8806bf0..14e4767d 100644 --- a/src/routes/(app)/ecash/[id]/[...version]/swap/+page.server.ts +++ b/src/routes/(app)/ecash/[id]/[...version]/swap/+page.server.ts @@ -1,3 +1,3 @@ - export async function load() { +export async function load() { return { test: 123 }; } diff --git a/src/routes/(app)/ecash/[id]/[...version]/swap/+page.svelte b/src/routes/(app)/ecash/[id]/[...version]/swap/+page.svelte index 3df9504d..ee22cea2 100644 --- a/src/routes/(app)/ecash/[id]/[...version]/swap/+page.svelte +++ b/src/routes/(app)/ecash/[id]/[...version]/swap/+page.svelte @@ -2,4 +2,6 @@ export let data; -
Support for swapping in cash from other mints is coming soon
+
+ Support for swapping in cash from other mints is coming soon +
diff --git a/src/routes/(app)/invoice/+page.server.ts b/src/routes/(app)/invoice/+page.server.ts index fa644eb9..5f3c38d3 100644 --- a/src/routes/(app)/invoice/+page.server.ts +++ b/src/routes/(app)/invoice/+page.server.ts @@ -5,7 +5,7 @@ import type { Invoice } from "$lib/types"; export let load = async ({ cookies, parent }) => { let aid = cookies.get("aid"); let { subject, user, rates } = await parent(); - if (aid === user.id) redirect(307, `/${user.username}/receive`) + if (aid === user.id) redirect(307, `/${user.username}/receive`); let invoice: Invoice = { aid, diff --git a/src/routes/(app)/invoice/+server.ts b/src/routes/(app)/invoice/+server.ts index 5e3d0300..0b5eb04e 100644 --- a/src/routes/(app)/invoice/+server.ts +++ b/src/routes/(app)/invoice/+server.ts @@ -1,7 +1,7 @@ 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))); +export const POST = async ({ cookies: c, request: r }) => { + const j = await r.json(); + return json(await post("/invoice", j, auth(c))); }; diff --git a/src/routes/(app)/invoice/[id]/+page.svelte b/src/routes/(app)/invoice/[id]/+page.svelte index 5495e118..1a32b5b4 100644 --- a/src/routes/(app)/invoice/[id]/+page.svelte +++ b/src/routes/(app)/invoice/[id]/+page.svelte @@ -52,7 +52,7 @@ user: { username, currency }, } = invoice); - if (browser && !subbed[id]) + if (browser && !subbed[id]) send("subscribe", invoice) .then(() => (subbed[id] = true)) .catch((e) => { diff --git a/src/routes/(app)/invoice/[id]/tip/+page.server.ts b/src/routes/(app)/invoice/[id]/tip/+page.server.ts index 97f148d1..f5a1b220 100644 --- a/src/routes/(app)/invoice/[id]/tip/+page.server.ts +++ b/src/routes/(app)/invoice/[id]/tip/+page.server.ts @@ -3,12 +3,11 @@ import invoice from "$lib/invoice"; import { redirect } from "@sveltejs/kit"; export async function load({ parent }) { - let rates = await get("/rates"); + let rates = await get("/rates"); let { invoice } = await parent(); - if (!invoice.amount) - redirect(307, `/invoice/${invoice.id}`); + if (!invoice.amount) redirect(307, `/invoice/${invoice.id}`); - return { rates} + return { rates }; } export const actions = { diff --git a/src/routes/(app)/ln/[lnurl]/+page.server.ts b/src/routes/(app)/ln/[lnurl]/+page.server.ts index cae220bf..4ac55e44 100644 --- a/src/routes/(app)/ln/[lnurl]/+page.server.ts +++ b/src/routes/(app)/ln/[lnurl]/+page.server.ts @@ -24,9 +24,8 @@ export const actions = { pay: async ({ fetch, request }) => { let error; - let { callback, amount, minSendable, maxSendable, comment } = await fd( - request - ); + let { callback, amount, minSendable, maxSendable, comment } = + await fd(request); minSendable = Math.round(minSendable / 1000); maxSendable = Math.round(maxSendable / 1000); diff --git a/src/routes/(app)/messages/+page.server.ts b/src/routes/(app)/messages/+page.server.ts index 94659621..a7195b18 100644 --- a/src/routes/(app)/messages/+page.server.ts +++ b/src/routes/(app)/messages/+page.server.ts @@ -10,7 +10,7 @@ interface Note { seen: number; } -export async function load({ params, parent, }) { +export async function load({ params, parent }) { let { user } = await parent(); if (user.username !== params.username) redirect(307, `/${params.username}`); let { since = 0 } = params; @@ -22,7 +22,9 @@ export async function load({ params, parent, }) { let { pubkey } = user; try { messages = await get(`/${pubkey}/${since}/messages`); - messages = messages.sort((a: Message, b: Message) => b.created_at - a.created_at); + messages = messages.sort( + (a: Message, b: Message) => b.created_at - a.created_at, + ); } catch (e) { console.log(`failed to fetch nostr messages`, e); } diff --git a/src/routes/(app)/messages/[recipient]/+page.server.ts b/src/routes/(app)/messages/[recipient]/+page.server.ts index 6be9f6ec..0ff2885e 100644 --- a/src/routes/(app)/messages/[recipient]/+page.server.ts +++ b/src/routes/(app)/messages/[recipient]/+page.server.ts @@ -13,7 +13,8 @@ export async function load({ params, parent }) { messages = messages .filter( - (m: Message) => m.recipient?.id === recipient.id || m.author?.id === recipient.id + (m: Message) => + m.recipient?.id === recipient.id || m.author?.id === recipient.id, ) .sort((a, b) => a.created_at - b.created_at); } catch (e) { diff --git a/src/routes/(app)/messages/[recipient]/+page.svelte b/src/routes/(app)/messages/[recipient]/+page.svelte index 98638389..9b9f6f47 100644 --- a/src/routes/(app)/messages/[recipient]/+page.svelte +++ b/src/routes/(app)/messages/[recipient]/+page.svelte @@ -17,7 +17,7 @@ function fadeScale( node, - { delay = 0, duration = 200, easing = (x) => x, baseScale = 0 } + { delay = 0, duration = 200, easing = (x) => x, baseScale = 0 }, ) { const o = +getComputedStyle(node).opacity; const m = getComputedStyle(node).transform.match(/scale\(([0-9.]+)\)/); @@ -43,8 +43,8 @@ let initialize = async (p) => { await Promise.all( messages.map( - async (event) => (event.message = await decrypt({ event, user })) - ) + async (event) => (event.message = await decrypt({ event, user })), + ), ); messages = messages; diff --git a/src/routes/(app)/otpsecret/+server.ts b/src/routes/(app)/otpsecret/+server.ts index 2539ba27..9102dd51 100644 --- a/src/routes/(app)/otpsecret/+server.ts +++ b/src/routes/(app)/otpsecret/+server.ts @@ -8,7 +8,7 @@ export async function POST({ cookies, request }) { 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 }) }) + JSON.stringify({ secret, uri, qr: Qr.drawImg(uri, { size: 600 }) }), ); } catch (e: any) { if (e.message.includes("pin")) throw e; diff --git a/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts b/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts index 93100ff0..61809cf7 100644 --- a/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts +++ b/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts @@ -5,7 +5,7 @@ export async function load({ cookies, params, parent }) { let { username } = params; let { rates, user } = await parent(); - if ((user?.username === username)) error(500, "Cannot send to self"); + if (user?.username === username) error(500, "Cannot send to self"); let subject = await get(`/users/${username}`); let [amount, currency] = params.amount.split("/"); @@ -28,7 +28,7 @@ export async function load({ cookies, params, parent }) { }, user: { username }, }, - auth(cookies) + auth(cookies), ); redirect(307, `/invoice/${id}`); diff --git a/src/routes/(app)/payments/[...page]/+page.svelte b/src/routes/(app)/payments/[...page]/+page.svelte index be2ba3bb..e781409f 100644 --- a/src/routes/(app)/payments/[...page]/+page.svelte +++ b/src/routes/(app)/payments/[...page]/+page.svelte @@ -46,7 +46,7 @@ $: selection = start ? presets.findIndex( - (p) => Math.abs(differenceInDays(new Date(start * 1000), p.start)) < 1 + (p) => Math.abs(differenceInDays(new Date(start * 1000), p.start)) < 1, ) : 0; diff --git a/src/routes/(app)/qr/[text]/+page.svelte b/src/routes/(app)/qr/[text]/+page.svelte index 5469c084..e073b66e 100644 --- a/src/routes/(app)/qr/[text]/+page.svelte +++ b/src/routes/(app)/qr/[text]/+page.svelte @@ -10,13 +10,13 @@
diff --git a/src/routes/(app)/receive/[...token]/+page.server.ts b/src/routes/(app)/receive/[...token]/+page.server.ts index 011ffb00..65888ff4 100644 --- a/src/routes/(app)/receive/[...token]/+page.server.ts +++ b/src/routes/(app)/receive/[...token]/+page.server.ts @@ -1,4 +1,4 @@ -import { fd} from "$lib/utils"; +import { fd } from "$lib/utils"; import { fail, redirect } from "@sveltejs/kit"; import parse from "$lib/parse"; @@ -11,7 +11,7 @@ export async function load({ params, parent, url }) { export const actions = { default: async ({ request, url }) => { let { text } = await fd(request); - await parse(text, url.host ); - return fail(400, { error: "default" }); + await parse(text, url.host); + return fail(400, { error: "default" }); }, }; diff --git a/src/routes/(app)/send/[...text]/+page.server.ts b/src/routes/(app)/send/[...text]/+page.server.ts index 950a2c98..906d6563 100644 --- a/src/routes/(app)/send/[...text]/+page.server.ts +++ b/src/routes/(app)/send/[...text]/+page.server.ts @@ -13,7 +13,7 @@ export async function load({ cookies, params, url }) { export const actions = { default: async ({ request, url }) => { let { text } = await fd(request); - await parse(text, url.host); + await parse(text, url.host); return fail(400, { error: "default" }); }, }; diff --git a/src/routes/(app)/send/[...text]/+page.svelte b/src/routes/(app)/send/[...text]/+page.svelte index 2fbd8d59..d27df639 100644 --- a/src/routes/(app)/send/[...text]/+page.svelte +++ b/src/routes/(app)/send/[...text]/+page.svelte @@ -18,7 +18,8 @@ let el, text, pasted, w; - let keypress = (e) => e.key === "Enter" ? (e.preventDefault() || el.click()) : (pasted = false); + let keypress = (e) => + e.key === "Enter" ? e.preventDefault() || el.click() : (pasted = false); let paste = async () => { text = await navigator.clipboard.readText(); diff --git a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.server.ts b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.server.ts index e46a9f56..77fd8a4d 100644 --- a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.server.ts +++ b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.server.ts @@ -8,7 +8,7 @@ export async function load({ params: { address, amount, feeRate }, cookies }) { let { fee, fees, inputs, ourfee, hex } = await post( "/bitcoin/fee", { address, amount, feeRate, aid }, - auth(cookies) + auth(cookies), ); let account = await get(`/account/${aid}`, auth(cookies)); diff --git a/src/routes/(app)/send/lightning/[payreq]/+page.svelte b/src/routes/(app)/send/lightning/[payreq]/+page.svelte index 01f41d34..132874a0 100644 --- a/src/routes/(app)/send/lightning/[payreq]/+page.svelte +++ b/src/routes/(app)/send/lightning/[payreq]/+page.svelte @@ -36,14 +36,14 @@ amount < 100 ? amount * 5 : amount < 1000 - ? amount - : amount < 10000 - ? amount * 0.5 - : amount < 100000 - ? amount * 0.1 - : amount < 1000000 - ? amount * 0.05 - : amount * 0.01 + ? amount + : amount < 10000 + ? amount * 0.5 + : amount < 100000 + ? amount * 0.1 + : amount < 1000000 + ? amount * 0.05 + : amount * 0.01, ); let loading; diff --git a/src/routes/(app)/sent/[id]/+page.server.ts b/src/routes/(app)/sent/[id]/+page.server.ts index 38d95e4e..80b1d428 100644 --- a/src/routes/(app)/sent/[id]/+page.server.ts +++ b/src/routes/(app)/sent/[id]/+page.server.ts @@ -2,6 +2,6 @@ import { get, auth } from "$lib/utils"; export let load = async ({ cookies, params }) => { let { id } = params; - let p = await get(`/payments/${id}`, auth(cookies)); + let p = await get(`/payments/${id}`, auth(cookies)); return p; }; diff --git a/src/routes/(app)/sent/[id]/+page.svelte b/src/routes/(app)/sent/[id]/+page.svelte index ce2c8d6e..a71a5360 100644 --- a/src/routes/(app)/sent/[id]/+page.svelte +++ b/src/routes/(app)/sent/[id]/+page.svelte @@ -14,10 +14,7 @@
-
+

{$t("payments.sent")}!

diff --git a/src/routes/(app)/settings/[...tab]/+page.svelte b/src/routes/(app)/settings/[...tab]/+page.svelte index f59806d1..7ef1653d 100644 --- a/src/routes/(app)/settings/[...tab]/+page.svelte +++ b/src/routes/(app)/settings/[...tab]/+page.svelte @@ -87,7 +87,7 @@ try { data.set( "cipher", - await reEncryptEntropy(user, data.get("password")) + await reEncryptEntropy(user, data.get("password")), ); } catch (e) { console.log("Failed to encrypt keys with new password"); @@ -98,7 +98,7 @@ if ($avatar) { try { let { hash } = JSON.parse( - await upload($avatar.file, $avatar.type, $avatar.progress, token) + await upload($avatar.file, $avatar.type, $avatar.progress, token), ); data.set("profile", hash); @@ -115,7 +115,7 @@ if ($banner) { try { let { hash } = JSON.parse( - await upload($banner.file, $banner.type, $banner.progress, token) + await upload($banner.file, $banner.type, $banner.progress, token), ); data.set("banner", hash); @@ -161,7 +161,7 @@ await post( "/api/request", { id: user.id, email: user.email }, - auth(cookies) + auth(cookies), ); warning($t("user.settings.verifying"), false); diff --git a/src/routes/(app)/settings/[...tab]/_pos.svelte b/src/routes/(app)/settings/[...tab]/_pos.svelte index dcfcbe44..7ccaa69f 100644 --- a/src/routes/(app)/settings/[...tab]/_pos.svelte +++ b/src/routes/(app)/settings/[...tab]/_pos.svelte @@ -13,7 +13,7 @@ let { currency } = user; let rate = rates[currency]; - let fiats = Object.keys(rates).sort((a,b) => a.localeCompare(b)); + let fiats = Object.keys(rates).sort((a, b) => a.localeCompare(b)); $: user.language = $locale; let keypress = (e) => e.key === "Enter" && (e.preventDefault() || el.click()); @@ -38,6 +38,36 @@ if (!user.threshold) user.threshold = 1000000; if (!user.reserve) user.reserve = 100000; let reserveEl, thresholdEl; + + let subscription; + $: updateNotifications(user.push); + let updateNotifications = async (push) => { + if (push && navigator.serviceWorker) { + const VAPID_PUBLIC_KEY = + "BEfgMDTeAn1F8ZQUGC6v5iKTD86zYXaa3QVr1E-ylKXC5c24JplcfSAN9PDlXAsVtUd3cU6j4-LsX_TKQzz_yDQ"; + + const swRegistration = await navigator.serviceWorker.getRegistration(); + const pushManager = swRegistration.pushManager; + if (!isPushManagerActive(pushManager)) { + console.log("not active"); + return; + } + subscription = await pushManager.subscribe({ + userVisibleOnly: true, + applicationServerKey: VAPID_PUBLIC_KEY, + }); + } + }; + + function isPushManagerActive(pushManager) { + if (pushManager) return true; + + if (!window.navigator.standalone) { + console.log("not standalone"); + } + + return false; + }
@@ -72,6 +102,18 @@

+
+
+ {$t("user.settings.pushNotifications")} + +
+

+ {$t("user.settings.pushNotificationsDesc")} +

+
+ +{JSON.stringify(subscription)} +
{$t("user.settings.tipPrompt")} diff --git a/src/routes/(app)/subscription/+server.ts b/src/routes/(app)/subscription/+server.ts new file mode 100644 index 00000000..8ae3f965 --- /dev/null +++ b/src/routes/(app)/subscription/+server.ts @@ -0,0 +1,11 @@ +import { auth, post } from "$lib/utils"; +import { json } from "@sveltejs/kit"; + +export async function POST({ cookies, request }) { + const result = await post( + "/subscription", + await request.json(), + auth(cookies), + ); + return json(result); +} diff --git a/src/routes/(static)/+layout.server.ts b/src/routes/(static)/+layout.server.ts index c26f8d96..56b8efa1 100644 --- a/src/routes/(static)/+layout.server.ts +++ b/src/routes/(static)/+layout.server.ts @@ -1,9 +1,9 @@ -import { get, auth} from "$lib/utils"; +import { get, auth } from "$lib/utils"; export async function load({ cookies }) { let token = cookies.get("token"); - let user; + let user; if (token) { try { user = await get("/me", auth(cookies)); diff --git a/src/routes/(static)/login/+page.server.ts b/src/routes/(static)/login/+page.server.ts index fd7fec8c..6ba7e033 100644 --- a/src/routes/(static)/login/+page.server.ts +++ b/src/routes/(static)/login/+page.server.ts @@ -15,11 +15,7 @@ export const actions = { if (loginRedirect === "undefined") loginRedirect = undefined; try { - await login( - user, - cookies, - request.headers.get("cf-connecting-ip") - ); + await login(user, cookies, request.headers.get("cf-connecting-ip")); } catch (e: any) { return fail(400, { error: "Login failed", message: e.message, ...form }); } diff --git a/src/routes/(static)/login/+page.svelte b/src/routes/(static)/login/+page.svelte index df7db5e6..efaa84b8 100644 --- a/src/routes/(static)/login/+page.svelte +++ b/src/routes/(static)/login/+page.svelte @@ -76,7 +76,9 @@ {/if} -
+ diff --git a/src/routes/(static)/privacy/+page.svelte b/src/routes/(static)/privacy/+page.svelte index bf8ba78e..7b21a5be 100644 --- a/src/routes/(static)/privacy/+page.svelte +++ b/src/routes/(static)/privacy/+page.svelte @@ -2,11 +2,15 @@
Privacy Policy
- The only personal information we require to create an account is a username and password. Some users may provide an email address for notification or password reset purposes. We will not share your email with anyone or use it for anything other than the purposes stated. -
- -
+ The only personal information we require to create an account is a username + and password. Some users may provide an email address for notification or + password reset purposes. We will not share your email with anyone or use it + for anything other than the purposes stated. +
- We keep records of payment history associated to accounts and may be obligated to share our records with financial authorities. You may request that your account be deleted by visiting https://coinos.io/removal +
+ We keep records of payment history associated to accounts and may be + obligated to share our records with financial authorities. You may request + that your account be deleted by visiting https://coinos.io/removal
diff --git a/src/routes/(static)/register/+page.server.ts b/src/routes/(static)/register/+page.server.ts index 731530c4..0b965507 100644 --- a/src/routes/(static)/register/+page.server.ts +++ b/src/routes/(static)/register/+page.server.ts @@ -21,7 +21,7 @@ export const actions = { let error; if (loginRedirect === "undefined") loginRedirect = undefined; - + try { await post("/register", { user }, { "cf-connecting-ip": ip }); } catch (e: any) { diff --git a/src/routes/(static)/register/+page.svelte b/src/routes/(static)/register/+page.svelte index 3491bd5d..1749bd16 100644 --- a/src/routes/(static)/register/+page.svelte +++ b/src/routes/(static)/register/+page.svelte @@ -98,7 +98,7 @@ if ($avatar) { try { let { hash } = JSON.parse( - await upload($avatar.file, $avatar.type, $avatar.progress, token) + await upload($avatar.file, $avatar.type, $avatar.progress, token), ); data.set("profile", hash); diff --git a/src/routes/(static)/support/+page.svelte b/src/routes/(static)/support/+page.svelte index 6b32124e..062f552b 100644 --- a/src/routes/(static)/support/+page.svelte +++ b/src/routes/(static)/support/+page.svelte @@ -24,7 +24,7 @@ grecaptcha.execute(recaptchaSiteKey, { action: "submit" }).then((token) => post("/support", { username: user?.username, email, message, token }) .then(() => (sent = true)) - .catch(() => fail("problem submitting")) + .catch(() => fail("problem submitting")), ); }); }; @@ -97,12 +97,7 @@ - +
diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index c21e8525..605c0134 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -1,46 +1,46 @@ import { auth, get } from "$lib/utils"; import { - locales, - loadTranslations, - translations, - defaultLocale, + locales, + loadTranslations, + translations, + defaultLocale, } from "$lib/translations/index"; export let load = async ({ cookies, request, url }) => { - let { host, pathname } = url; - let user; - let token = cookies.get("token"); - if (token) { - try { - user = await get("/me", auth(cookies)); - } catch (e) {} - } + let { host, pathname } = url; + let user; + let token = cookies.get("token"); + if (token) { + try { + user = await get("/me", auth(cookies)); + } catch (e) {} + } - // Try to get the locale from cookie - let locale = user?.language || (cookies.get("lang") || "").toLowerCase(); + // Try to get the locale from cookie + let locale = user?.language || (cookies.get("lang") || "").toLowerCase(); - // Get user preferred locale - if (!locale) { - locale = `${`${request.headers.get("accept-language")}`.match( - /[a-zA-Z]+?(?=-|_|,|;)/ - )}`.toLowerCase(); - } + // Get user preferred locale + if (!locale) { + locale = `${`${request.headers.get("accept-language")}`.match( + /[a-zA-Z]+?(?=-|_|,|;)/, + )}`.toLowerCase(); + } - // Get defined locales - let supportedLocales = locales.get().map((l) => l.toLowerCase()); + // Get defined locales + let supportedLocales = locales.get().map((l) => l.toLowerCase()); - // Use default locale if current locale is not supported - if (!supportedLocales.includes(locale)) { - locale = defaultLocale; - } + // Use default locale if current locale is not supported + if (!supportedLocales.includes(locale)) { + locale = defaultLocale; + } - await loadTranslations(locale, pathname); // keep this just before the `return` + await loadTranslations(locale, pathname); // keep this just before the `return` - return { - host, - pathname, - i18n: { locale, route: pathname }, - user, - translations: translations.get(), - }; + return { + host, + pathname, + i18n: { locale, route: pathname }, + user, + translations: translations.get(), + }; }; diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index d9f713b4..20892e44 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -2,13 +2,13 @@ import { addTranslations, setLocale, setRoute } from "$lib/translations/index"; /** @type {import('@sveltejs/kit').Load} */ export const load = async ({ data }) => { - const { i18n, translations } = data; - const { locale, route } = i18n; + const { i18n, translations } = data; + const { locale, route } = i18n; - addTranslations(translations); + addTranslations(translations); - await setRoute(route); - await setLocale(locale); + await setRoute(route); + await setLocale(locale); - return i18n; + return i18n; }; diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 20c3a05b..5679ea61 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -1,3 +1,3 @@ -export let load = async () => { - return { faqs: ["cost", "compatibility", "safety", "pos", "bitcoin"] }; +export const load = async () => { + return { faqs: ["cost", "compatibility", "safety", "pos", "bitcoin"] }; }; diff --git a/src/routes/map/+page.svelte b/src/routes/map/+page.svelte index c307826e..2addca54 100644 --- a/src/routes/map/+page.svelte +++ b/src/routes/map/+page.svelte @@ -7,6 +7,5 @@
- +
- diff --git a/src/service-worker.js b/src/service-worker.js index d477ed81..fee7eee0 100644 --- a/src/service-worker.js +++ b/src/service-worker.js @@ -3,3 +3,7 @@ import { build, files, version } from "$service-worker"; self.addEventListener("install", (event) => {}); self.addEventListener("activate", (event) => {}); self.addEventListener("fetch", (event) => {}); +self.addEventListener("push", (event) => { + const pushData = event.data.json(); + self.registration.showNotification(pushData.title, pushData); +}); -- cgit v1.2.3