diff options
| -rw-r--r-- | src/components/Account.svelte | 6 | ||||
| -rw-r--r-- | src/components/Items.svelte | 2 | ||||
| -rw-r--r-- | src/lib/parse.ts | 6 | ||||
| -rw-r--r-- | src/routes/(app)/+layout.server.ts | 4 | ||||
| -rw-r--r-- | src/routes/(app)/account/cash/+page.svelte | 46 | ||||
| -rw-r--r-- | src/routes/(app)/account/mints/+page.svelte | 1 | ||||
| -rw-r--r-- | src/routes/(app)/account/pass/+page.svelte (renamed from src/routes/(app)/account/setup/step2/+page.svelte) | 49 | ||||
| -rw-r--r-- | src/routes/(app)/account/savings/+page.svelte | 31 | ||||
| -rw-r--r-- | src/routes/(app)/account/seed/+page.svelte | 85 | ||||
| -rw-r--r-- | src/routes/(app)/account/self/+page.svelte | 59 | ||||
| -rw-r--r-- | src/routes/(app)/account/setup/+page.svelte | 41 |
11 files changed, 209 insertions, 121 deletions
diff --git a/src/components/Account.svelte b/src/components/Account.svelte index ab24f56a..d52c4f52 100644 --- a/src/components/Account.svelte +++ b/src/components/Account.svelte @@ -9,10 +9,10 @@ $: type = seed ? "Savings" : "Cash"; $: refresh(account); - let refresh = (a) => ({ seed, balance, id } = a) + let refresh = (a) => ({ seed, balance, id } = a); - let setAccount = () => - document.cookie = `account=${id}; path=/; max-age=86400`; + let setAccount = () => + (document.cookie = `account=${id}; path=/; max-age=86400`); </script> <a href={`/payments`} class="block" on:click={setAccount}> diff --git a/src/components/Items.svelte b/src/components/Items.svelte index 47966d8e..2916721d 100644 --- a/src/components/Items.svelte +++ b/src/components/Items.svelte @@ -15,7 +15,7 @@ import DeleteItem from "$comp/DeleteItem.svelte"; import Icon from "$comp/Icon.svelte"; - export let subject, user, items, rate, currency, total; + export let subject, user, items, rate, currency, total; let flipDurationMs = 200; let dropTargetStyle = {}; diff --git a/src/lib/parse.ts b/src/lib/parse.ts index 5bf5ffb0..781c7087 100644 --- a/src/lib/parse.ts +++ b/src/lib/parse.ts @@ -45,9 +45,9 @@ export default async (t, host) => { // bitcoin if (validate(t) || isLiquid(t)) { - let r = `/send/bitcoin/${t}`; - if (amount) r += "/" + amount; - redirect(307, r); + let r = `/send/bitcoin/${t}`; + if (amount) r += "/" + amount; + redirect(307, r); } if (t.startsWith("cashu")) { diff --git a/src/routes/(app)/+layout.server.ts b/src/routes/(app)/+layout.server.ts index e4105fb8..0901cb5d 100644 --- a/src/routes/(app)/+layout.server.ts +++ b/src/routes/(app)/+layout.server.ts @@ -39,12 +39,12 @@ export async function load({ cookies, request, url, params }) { redirect(307, `/${user.username}`); } - if ( protectedRoutes.find((p) => pathname.match(p)) && (!user)) { + if (protectedRoutes.find((p) => pathname.match(p)) && !user) { redirect(307, "/login"); } if (user && !user.pubkey && !pathname.includes("generate")) { - console.log("user missing pubkey", user) + console.log("user missing pubkey", user); redirect(303, `/${user.username}/generate`); } diff --git a/src/routes/(app)/account/cash/+page.svelte b/src/routes/(app)/account/cash/+page.svelte index de0092a4..b0e2e757 100644 --- a/src/routes/(app)/account/cash/+page.svelte +++ b/src/routes/(app)/account/cash/+page.svelte @@ -1,29 +1,37 @@ <script> import { t } from "$lib/translations"; + import Icon from "$comp/Icon.svelte"; </script> <div class="space-y-5"> <h1 class="text-center text-3xl font-semibold">Choose your custodian</h1> - <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5"> - <a href="/account/cash"> - <div class="shadow p-8 rounded-xl flex gap-4"> - <img src="/icons/logo-symbol.svg" class="w-20" /> - <div> - <div class="text-xl">Coinos</div> - <div class="text-secondary"> - https://mint.coinos.io - </div> - </div> - </div> - <div class="shadow p-8 rounded-xl flex gap-4"> - <img src="/images/minibits.png" class="w-20" /> - <div> - <div class="text-xl">Minibits</div> - <div class="text-secondary"> - https://mint.minibits.cash/bitcoin - </div> - </div> + <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-2"> + <div class="space-y-5"> + <a href="/account/cash" class="block"> + <div class="shadow p-8 rounded-xl flex gap-4 hover:bg-primary"> + <img src="/icons/logo-symbol.svg" class="w-20" /> + <div> + <div class="text-xl">Coinos</div> + <div class="text-secondary">https://mint.coinos.io</div> + </div> + </div> + </a> + <a href="/account/cash" class="block"> + <div class="shadow p-8 rounded-xl flex gap-4 hover:bg-primary"> + <img src="/images/minibits.png" class="w-20" /> + <div> + <div class="text-xl">Minibits</div> + <div class="text-secondary">https://mint.minibits.cash/bitcoin</div> + </div> + </div> + </a> </div> + <!-- <a href="/account/mints" class="block"> --> + <!-- <div class="p-4 border rounded-xl flex justify-center gap-2"> --> + <!-- <Icon icon="plus" style="w-8 my-auto" /> --> + <!-- <div class="text-xl my-auto">More</div> --> + <!-- </div> --> + <!-- </a> --> </div> </div> diff --git a/src/routes/(app)/account/mints/+page.svelte b/src/routes/(app)/account/mints/+page.svelte new file mode 100644 index 00000000..1080269e --- /dev/null +++ b/src/routes/(app)/account/mints/+page.svelte @@ -0,0 +1 @@ +mints diff --git a/src/routes/(app)/account/setup/step2/+page.svelte b/src/routes/(app)/account/pass/+page.svelte index 221e545e..b6c7f339 100644 --- a/src/routes/(app)/account/setup/step2/+page.svelte +++ b/src/routes/(app)/account/pass/+page.svelte @@ -1,4 +1,5 @@ <script lang="ts"> + import { onMount } from "svelte"; import { t } from "$lib/translations"; import { mnemonic } from "$lib/store"; import Icon from "$comp/Icon.svelte"; @@ -17,25 +18,37 @@ let { user } = data; - let password; + let confirm, password; + + onMount(() => { + if (!$mnemonic) goto("/account/savings"); + }); let submit = async () => { - let seed = await encrypt(mnemonicToEntropy($mnemonic, wordlist), password); - let master = HDKey.fromMasterSeed( - await mnemonicToSeed($mnemonic), - versions - ); - let child = master.derive("m/84'/0'/0'"); - let pubkey = child.publicExtendedKey; - let fingerprint = child.fingerprint.toString(16).padStart(8, "0"); + if (!password || password !== confirm) { + fail("Passwords don't match"); + return; + } - let receiving = child.deriveChild(0); try { + let seed = await encrypt( + mnemonicToEntropy($mnemonic, wordlist), + password + ); + let master = HDKey.fromMasterSeed( + await mnemonicToSeed($mnemonic, password), + versions + ); + let child = master.derive("m/84'/0'/0'"); + let pubkey = child.publicExtendedKey; + let fingerprint = child.fingerprint.toString(16).padStart(8, "0"); + + let receiving = child.deriveChild(0); await post("/api/accounts", { fingerprint, pubkey, seed }); goto(`/${user.username}`); } catch (e) { console.log(e); - fail("Something went wrong"); + fail(e.message); } $mnemonic = ""; @@ -44,8 +57,7 @@ <div class="space-y-5"> <div> - <h1 class="text-center text-3xl font-semibold">Account setup</h1> - <h2 class="text-center text-2xl text-secondary">Step 2</h2> + <h1 class="text-center text-3xl font-semibold">Account password</h1> </div> <form @@ -53,11 +65,18 @@ class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5" > <p class="text-secondary mb-1"> - Add an additional password to your seed phrase. It should be different - than your login password. We can't reset it if you lose it. + Please add a password to the account. It should be different than your + login password. <b class="text-red-700">We won't be able to reset it.</b> You'll + need it to make withdrawals. </p> <input use:focus type="text" placeholder="Password" bind:value={password} /> + <input + use:focus + type="text" + placeholder="Confirm password" + bind:value={confirm} + /> <button class="bg-black text-white w-full px-5 py-6 text-xl rounded-2xl font-bold" diff --git a/src/routes/(app)/account/savings/+page.svelte b/src/routes/(app)/account/savings/+page.svelte new file mode 100644 index 00000000..8d52f997 --- /dev/null +++ b/src/routes/(app)/account/savings/+page.svelte @@ -0,0 +1,31 @@ +<script> + import { t } from "$lib/translations"; + import Icon from "$comp/Icon.svelte"; +</script> + +<div class="space-y-5"> + <h1 class="text-center text-3xl font-semibold">Wallet setup</h1> + + <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-2"> + <div class="space-y-5"> + <a href="/account/seed" class="block"> + <div class="shadow p-8 rounded-xl flex gap-4 hover:bg-primary"> + <img src="/icons/random.svg" class="w-20" /> + <div> + <div class="text-xl">Create</div> + <div class="text-secondary">Generate a new seed phrase in your browser</div> + </div> + </div> + </a> + <a href="/account/import" class="block"> + <div class="shadow p-8 rounded-xl flex gap-4 hover:bg-primary"> + <img src="/images/keys.png" class="w-20" /> + <div> + <div class="text-xl">Import</div> + <div class="text-secondary">Enter an existing seed phrase or wallet key</div> + </div> + </div> + </a> + </div> + </div> +</div> diff --git a/src/routes/(app)/account/seed/+page.svelte b/src/routes/(app)/account/seed/+page.svelte new file mode 100644 index 00000000..9c19e820 --- /dev/null +++ b/src/routes/(app)/account/seed/+page.svelte @@ -0,0 +1,85 @@ +<script> + import { browser } from "$app/environment"; + import { tick, onMount } from "svelte"; + import { copy } from "$lib/utils"; + import { t } from "$lib/translations"; + import Icon from "$comp/Icon.svelte"; + import { mnemonic } from "$lib/store"; + import { generateMnemonic } from "@scure/bip39"; + import { wordlist } from "@scure/bip39/wordlists/english"; + + export let data; + let { user } = data; + + onMount(async () => { + if (!browser) return; + await generate(); + }); + + let generate = async () => { + $mnemonic = "- - - - - - - - - - - -"; + await tick(); + await new Promise((r) => setTimeout(r, 250)); + $mnemonic = await generateMnemonic(wordlist); + }; +</script> + +<div class="space-y-5"> + <h1 class="text-center text-3xl font-semibold">Seed phrase</h1> + + <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5"> + {#if $mnemonic} + <div + class="text-2xl border p-4 rounded-2xl text-secondary grid grid-rows-6 grid-flow-col gap-2 justify-center gap-x-40" + > + {#each $mnemonic.split(" ") as w, i} + <div> + <span class="select-none text-sm text-black">{i + 1}.</span> + <span>{@html w === "-" ? "—" : w}</span> + </div> + {/each} + </div> + {/if} + + <div> + <button + type="button" + class="flex gap-2 text-center break-all rounded-2xl hover:opacity-80 py-5 px-6 w-full mx-auto justify-center border whitespace-nowrap mb-2" + on:click={generate} + > + <Icon icon="random" style="w-8 my-auto" /> + <div class="my-auto">Regenerate</div> + </button> + + <button + type="button" + class="flex gap-2 text-center break-all rounded-2xl hover:opacity-80 py-5 px-6 w-full mx-auto justify-center border whitespace-nowrap mb-2" + on:click={() => copy($mnemonic)} + > + <Icon icon="copy" style="w-8 my-auto" /> + <div class="my-auto"> + {$t("payments.copy")} + </div> + </button> + </div> + + <div class="flex gap-2"> + <a href={`/account/savings`} class="contents"> + <button + type="button" + class="bg-black text-white w-full px-5 py-6 text-xl rounded-2xl font-bold" + > + Back + </button> + </a> + <a href={`/account/pass`} class="contents"> + <button + type="button" + class="bg-black text-white w-full px-5 py-6 text-xl rounded-2xl font-bold" + > + Next + </button> + </a> + </div> + </div> +</div> diff --git a/src/routes/(app)/account/self/+page.svelte b/src/routes/(app)/account/self/+page.svelte deleted file mode 100644 index 833def87..00000000 --- a/src/routes/(app)/account/self/+page.svelte +++ /dev/null @@ -1,59 +0,0 @@ -<script> - import { copy } from "$lib/utils"; - import { t } from "$lib/translations"; - import Icon from "$comp/Icon.svelte"; - import { mnemonic } from "$lib/store"; - import { generateMnemonic } from "@scure/bip39"; - import { wordlist } from "@scure/bip39/wordlists/english"; - - export let data; - let { user } = data; - - let reveal; - - let toggleSeed = async () => { - try { - $mnemonic = await generateMnemonic(wordlist); - reveal = !reveal; - } catch (e) { - console.log(e); - } - }; -</script> - -<div class="space-y-5"> - <h1 class="text-center text-3xl font-semibold">Account setup</h1> - <h2 class="text-center text-2xl text-secondary">Step 1</h2> - - <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5"> - <p class="text-secondary mb-1"> - Whoever knows these words in the correct order can withdraw funds from the account. We can't - recover them if you lose them. - </p> - - {#if reveal} - <div class="text-2xl border p-4 text-center rounded-2xl"> - {$mnemonic} - </div> - - <div> - <a href={`/account/setup/step2`} class="contents"> - <button - type="button" - class="bg-black text-white w-full px-5 py-6 text-xl rounded-2xl font-bold" - on:click={toggleSeed} - > - I've written down my seed phrase - </button> - </a> - </div> - {:else} - <div class="flex justify-center"> - <button type="button" class="primary" on:click={toggleSeed}> - <Icon icon="warning" style="mr-2 w-6 my-auto" /> - {$t("user.settings.revealSeed")} - </button> - </div> - {/if} - </div> -</div> diff --git a/src/routes/(app)/account/setup/+page.svelte b/src/routes/(app)/account/setup/+page.svelte index f19c4c96..8304c5ed 100644 --- a/src/routes/(app)/account/setup/+page.svelte +++ b/src/routes/(app)/account/setup/+page.svelte @@ -6,26 +6,29 @@ <h1 class="text-center text-3xl font-semibold">Choose account type</h1> <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5"> - <a href="/account/cash"> - <div class="shadow p-8 rounded-xl flex gap-4"> - <img src="/images/cash.png" class="w-20" /> - <div> - <div class="text-xl">Cash</div> - <div class="text-secondary"> - Your balance is held with a custodian. Payments are faster and - cheaper. + <a href="/account/cash" class="block"> + <div class="shadow p-8 rounded-xl flex gap-4 hover:bg-primary"> + <img src="/images/cash.png" class="w-20" /> + <div> + <div class="text-xl">Cash</div> + <div class="text-secondary"> + Your balance is held with a custodian. Payments are fast and + free. + </div> + </div> </div> + </a> + <a href="/account/savings" class="block"> + <div class="shadow p-8 rounded-xl flex gap-4 hover:bg-primary"> + <img src="/images/keys.png" class="w-20" /> + <div> + <div class="text-xl">Savings</div> + <div class="text-secondary"> + You hold the keys to the account and are responsible for + safeguarding them. + </div> + </div> </div> - </div> - <div class="shadow p-8 rounded-xl flex gap-4"> - <img src="/images/keys.png" class="w-20" /> - <div> - <div class="text-xl">Savings</div> - <div class="text-secondary"> - You hold the keys to the account and are responsible for safeguarding - them. - </div> - </div> - </div> + </a> </div> </div> |
