diff options
| author | Adam Soltys <[email protected]> | 2024-09-23 16:22:10 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2024-09-23 16:22:10 -0700 |
| commit | 52871e09d9218f3f2fb2ce4bd6e97f5c1fc15480 (patch) | |
| tree | 543a0af922d193940846e7a953ed7b027a931e30 /src | |
| parent | 200c5ff1f4e41ed907a1dd4a0337d8ee0a94b8be (diff) | |
| download | coinos-ui-52871e09d9218f3f2fb2ce4bd6e97f5c1fc15480.tar.gz coinos-ui-52871e09d9218f3f2fb2ce4bd6e97f5c1fc15480.zip | |
account import
Diffstat (limited to 'src')
19 files changed, 189 insertions, 123 deletions
diff --git a/src/components/Account.svelte b/src/components/Account.svelte index 47deadcd..4bfdc4ac 100644 --- a/src/components/Account.svelte +++ b/src/components/Account.svelte @@ -1,5 +1,4 @@ <script> - import { account as acc } from "$lib/store"; import Icon from "$comp/Icon.svelte"; import Balance from "$comp/Balance.svelte"; import { t } from "$lib/translations"; @@ -11,8 +10,7 @@ $: refresh(account); let refresh = (a) => ({ seed, balance, id } = a); - let setAccount = () => - (document.cookie = `aid=${id}; path=/; max-age=86400`); + let setAccount = () => (document.cookie = `aid=${id}; path=/; max-age=86400`); </script> <a href={`/payments`} class="block" on:click={setAccount}> @@ -40,7 +38,7 @@ </button> </a> - <a href={`/invoice?account=${id}`} class="contents" on:click={setAccount}> + <a href={`/invoice`} class="contents" on:click={setAccount}> <button class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex bg-primary grow" > diff --git a/src/components/WalletPass.svelte b/src/components/WalletPass.svelte new file mode 100644 index 00000000..7793ecc5 --- /dev/null +++ b/src/components/WalletPass.svelte @@ -0,0 +1,67 @@ +<script> + import { focus } from "$lib/utils"; + import { t } from "$lib/translations"; + import Icon from "$comp/Icon.svelte"; + export let cancel, password, submit; + let revealPassword; +</script> + +<div + class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-20" +> + <div + class="relative top-1/3 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white space-y-5" + > + <h1 class="text-center text-2xl font-semibold"> + {$t("payments.enterWalletPass")} + </h1> + <form on:submit|preventDefault={submit}> + <div class="relative mb-5"> + {#if revealPassword} + <input + name="password" + type="text" + required + class="bg-primary" + bind:value={password} + autocapitalize="none" + use:focus + /> + {:else} + <input + name="password" + type="password" + required + class="bg-primary" + bind:value={password} + autocapitalize="none" + use:focus + /> + {/if} + <button + type="button" + on:click={() => (revealPassword = !revealPassword)} + class="absolute right-5 top-6" + > + <Icon icon={revealPassword ? "eye" : "eye-off"} /> + </button> + </div> + <div class="w-full flex"> + <button + type="button" + class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto" + on:click={cancel} + on:keydown={cancel} + > + <div class="my-auto">Cancel</div> + </button> + <button + type="submit" + class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto" + > + <div class="my-auto">Submit</div> + </button> + </div> + </form> + </div> +</div> diff --git a/src/lib/utils.ts b/src/lib/utils.ts index c20a7d29..0997eeb9 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -287,6 +287,7 @@ export let types = { internal: "internal", fund: "fund", ecash: "ecash", + reconcile: "reconcile", }; export let ease = (t: number): number => diff --git a/src/locales/en.json b/src/locales/en.json index 9752a41d..78ef8de1 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1,6 +1,18 @@ { "accounts": { - "savings": "Savings" + "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 in your browser", + "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", diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte index 03fbc454..2c2ff9a8 100644 --- a/src/routes/(app)/[username]/(profile)/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/+page.svelte @@ -41,13 +41,13 @@ {/each} <div class="flex justify-center gap-2"> - <a href={`/account/setup`} class="contents"> + <a href={`/account/savings`} class="contents"> <button class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex bg-black text-white w-full justify-center" > <div class="mx-auto flex gap-2"> <Icon icon="plus" style="w-8 mx-auto invert" /> - <div class="my-auto text-xl whitespace-nowrap">Add account</div> + <div class="my-auto text-xl whitespace-nowrap">{$t("accounts.addAccount")}</div> </div> </button> </a> diff --git a/src/routes/(app)/account/[id]/+page.svelte b/src/routes/(app)/account/[id]/+page.svelte index df121168..3dcd99b8 100644 --- a/src/routes/(app)/account/[id]/+page.svelte +++ b/src/routes/(app)/account/[id]/+page.svelte @@ -1,13 +1,27 @@ <script> + import WalletPass from "$comp/WalletPass.svelte"; import { goto } from "$app/navigation"; - import { fail, focus, post } from "$lib/utils"; + import { copy, fail, focus, post } from "$lib/utils"; import { enhance } from "$app/forms"; import { t } from "$lib/translations"; import Icon from "$comp/Icon.svelte"; + import { decrypt } from "nostr-tools/nip49"; + import { entropyToMnemonic, mnemonicToSeed } from "@scure/bip39"; + import { wordlist } from "@scure/bip39/wordlists/english"; export let data; let { account, user } = data; - let { id, name } = account; + let { id, name, seed } = account; + let mnemonic, password; + let passwordPrompt; + let cancel = () => (passwordPrompt = false); + let toggle = () => (passwordPrompt = !passwordPrompt); + + let submit = async () => { + toggle(); + let entropy = await decrypt(seed, password); + mnemonic = entropyToMnemonic(entropy, wordlist); + }; let del = async () => { try { @@ -35,15 +49,45 @@ class="rounded-2xl border py-3 font-bold mx-auto bg-black text-white px-4 w-full" >Submit</button > + {#if mnemonic} + <div class="p-8">{mnemonic}</div> + + <button + on:click={() => copy(mnemonic)} + type="button" + class="flex gap-2 rounded-2xl border py-3 font-bold mx-auto px-4 w-full justify-center" + > + <Icon icon="copy" style="w-8 my-auto" /> + <div class="my-auto">Copy</div></button + > + {:else if seed} + <button + on:click={toggle} + type="button" + class="flex gap-2 rounded-2xl border py-3 font-bold mx-auto px-4 w-full justify-center bg-primary" + > + <Icon icon="eye" style="w-8 my-auto" /> + <div class="my-auto">Reveal mnemonic</div></button + > + {/if} + + {#if seed} <button on:click={del} type="button" - class="flex gap-2 rounded-2xl border py-3 font-bold mx-auto px-4 w-full justify-center border-red-600 border-2" + class="flex gap-2 rounded-2xl border py-3 font-bold mx-auto px-4 w-full justify-center bg-primary" > <Icon icon="trash" style="w-8 my-auto" /> - <div class="my-auto">Delete</div></button + <div class="my-auto"><span class="text-red-600">Delete</span> account</div></button > + {/if} </div> + + <div /> </form> </div> </div> + +{#if passwordPrompt} + <WalletPass bind:password bind:cancel bind:submit /> +{/if} diff --git a/src/routes/(app)/account/import/+page.svelte b/src/routes/(app)/account/import/+page.svelte index 866c1b1a..a1d6549b 100644 --- a/src/routes/(app)/account/import/+page.svelte +++ b/src/routes/(app)/account/import/+page.svelte @@ -1,7 +1,7 @@ <script> import { goto } from "$app/navigation"; import { mnemonic } from "$lib/store"; - import { versions } from "$lib/utils"; + import { fail, focus, versions } from "$lib/utils"; import { t } from "$lib/translations"; import { browser } from "$app/environment"; import Icon from "$comp/Icon.svelte"; @@ -16,6 +16,7 @@ }; let submit = () => { + text = text.replace(/,/g, " ").trim(); let valid = validateMnemonic(text, wordlist); if (valid) { $mnemonic = text; @@ -29,28 +30,26 @@ </script> <div class="space-y-5"> - <h1 class="text-center text-3xl font-semibold">Import</h1> + <h1 class="text-center text-3xl font-semibold">{$t("accounts.import")}</h1> <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-2"> - <div class="space-y-5"> - <form on:submit|preventDefault={submit}> - <textarea - use:focus - name="text" - placeholder="Enter a seed phrase" - class="w-full p-4 border rounded-xl h-48 text-xl" - bind:value={text} - autocapitalize="none" - /> - <button - bind:this={el} - type="submit" - class="flex bg-black text-white border rounded-2xl px-6 py-5 w-full font-bold gap-2 justify-center" - > - <Icon icon="send" style="w-8 my-auto invert" /> - <div class="my-auto">{$t("user.send.next")}</div> - </button> - </form> - </div> + <form on:submit|preventDefault={submit} class="space-y-2"> + <textarea + use:focus + name="text" + placeholder={$t("accounts.enterSeed")} + class="w-full p-4 border rounded-xl h-48 text-xl" + bind:value={text} + autocapitalize="none" + /> + <button + bind:this={el} + type="submit" + class="flex bg-black text-white border rounded-2xl px-6 py-5 w-full font-bold gap-2 justify-center" + > + <Icon icon="send" style="w-8 my-auto invert" /> + <div class="my-auto">{$t("user.send.next")}</div> + </button> + </form> </div> </div> diff --git a/src/routes/(app)/account/pass/+page.svelte b/src/routes/(app)/account/pass/+page.svelte index f5e956ff..2ce8b168 100644 --- a/src/routes/(app)/account/pass/+page.svelte +++ b/src/routes/(app)/account/pass/+page.svelte @@ -28,7 +28,7 @@ let submit = async () => { if (!password || password !== confirm) { - fail("Passwords don't match"); + fail($t("accounts.passwordMismatch")); return; } @@ -59,7 +59,7 @@ <div class="space-y-5"> <div> - <h1 class="text-center text-3xl font-semibold">Account password</h1> + <h1 class="text-center text-3xl font-semibold">{$t("accounts.accountPassword")}</h1> </div> <form @@ -67,8 +67,7 @@ class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5" > <p class="text-secondary mb-1"> - If you lose this password you won't be able to use the account. We can't - recover it for you even if you have the seed phrase. + {$t("accounts.passwordWarning")} </p> <div class="relative"> @@ -85,6 +84,7 @@ use:focus name="password" type="password" + placeholder={$t("accounts.password")} required bind:value={password} autocapitalize="none" @@ -113,7 +113,7 @@ {:else} <input type="password" - placeholder="Confirm password" + placeholder={$t("accounts.confirmPassword")} required bind:value={confirm} autocapitalize="none" diff --git a/src/routes/(app)/account/savings/+page.svelte b/src/routes/(app)/account/savings/+page.svelte index 8d52f997..f9c6305c 100644 --- a/src/routes/(app)/account/savings/+page.svelte +++ b/src/routes/(app)/account/savings/+page.svelte @@ -4,7 +4,7 @@ </script> <div class="space-y-5"> - <h1 class="text-center text-3xl font-semibold">Wallet setup</h1> + <h1 class="text-center text-3xl font-semibold">{$t("accounts.accountSetup")}</h1> <div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-2"> <div class="space-y-5"> @@ -12,8 +12,8 @@ <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 class="text-xl">{$t("accounts.create")}</div> + <div class="text-secondary">{$t("accounts.generateSeed")}</div> </div> </div> </a> @@ -21,8 +21,8 @@ <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 class="text-xl">{$t("accounts.import")}</div> + <div class="text-secondary">{$t("accounts.importSeed")}</div> </div> </div> </a> diff --git a/src/routes/(app)/invoice/+page.server.ts b/src/routes/(app)/invoice/+page.server.ts index 7bac3f9b..45389f8c 100644 --- a/src/routes/(app)/invoice/+page.server.ts +++ b/src/routes/(app)/invoice/+page.server.ts @@ -4,6 +4,7 @@ import type { Invoice } from "$lib/types"; export let load = async ({ cookies, parent }) => { let aid = cookies.get("aid"); + console.log("AID", aid) let { subject, user, rates } = await parent(); let invoice: Invoice = { @@ -18,7 +19,7 @@ export let load = async ({ cookies, parent }) => { } catch (e) { console.log(e); error(500, "Failed to generate invoice"); - } + } let { id } = invoice; diff --git a/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts b/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts index 7ccc23b0..cf8143f0 100644 --- a/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts +++ b/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts @@ -37,14 +37,15 @@ export async function load({ cookies, params, parent }) { export const actions = { default: async ({ cookies, request }) => { + let p; try { let body = await fd(request); - await post("/payments", body, auth(cookies)); + p = await post("/payments", body, auth(cookies)); } catch (e: any) { console.log(e); return fail(400, { message: e.message }); } - redirect(307, "/sent"); + redirect(307, `/sent/${p.id}`); }, }; diff --git a/src/routes/(app)/payments/[...page]/+page.svelte b/src/routes/(app)/payments/[...page]/+page.svelte index b7ea7b6e..2ffed537 100644 --- a/src/routes/(app)/payments/[...page]/+page.svelte +++ b/src/routes/(app)/payments/[...page]/+page.svelte @@ -213,6 +213,8 @@ <div class="text-secondary flex"> {#if [types.ecash, types.lightning].includes(p.type)} <div class="text-3xl">⚡️</div> + {:else if p.type === types.reconcile} + <Icon icon="balance" style="w-6 mr-2" /> {:else if p.type === types.bitcoin} <div class="my-auto mr-1"> <img 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 2b8fe37c..e46a9f56 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 @@ -35,14 +35,15 @@ export const actions = { params: { address, amount, feeRate }, request, }) => { + let p; try { let body = await fd(request); - await post("/bitcoin/send", body, auth(cookies)); + p = await post("/bitcoin/send", body, auth(cookies)); } catch (e: any) { console.log("problem sending bitcoin", e); return fail(400, { address, amount, feeRate, message: e.message }); } - redirect(307, "/sent"); + redirect(307, `/sent/${p.id}`); }, }; diff --git a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte index f6c6980d..4da2e04c 100644 --- a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte +++ b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte @@ -30,7 +30,6 @@ export let form; let { encode, decode } = hexUtil; - let revealPassword = false; let passwordPrompt; let password; let cancel = () => (passwordPrompt = false); @@ -206,65 +205,7 @@ </div> {#if passwordPrompt} - <div - class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-20" - > - <div - class="relative top-1/3 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white space-y-5" - > - <h1 class="text-center text-2xl font-semibold"> - {$t("payments.enterWalletPass")} - </h1> - <form on:submit|preventDefault={signTx}> - <div class="relative mb-5"> - {#if revealPassword} - <input - name="password" - type="text" - required - class="bg-primary" - bind:value={password} - autocapitalize="none" - use:focus - /> - {:else} - <input - name="password" - type="password" - required - class="bg-primary" - bind:value={password} - autocapitalize="none" - use:focus - /> - {/if} - <button - type="button" - on:click={() => (revealPassword = !revealPassword)} - class="absolute right-5 top-6" - > - <Icon icon={revealPassword ? "eye" : "eye-off"} /> - </button> - </div> - <div class="w-full flex"> - <button - type="button" - class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto" - on:click={cancel} - on:keydown={cancel} - > - <div class="my-auto">Cancel</div> - </button> - <button - type="submit" - class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto" - > - <div class="my-auto">Submit</div> - </button> - </div> - </form> - </div> - </div> + <WalletPass bind:password bind:cancel /> {/if} <style> diff --git a/src/routes/(app)/send/invoice/[id]/+page.server.ts b/src/routes/(app)/send/invoice/[id]/+page.server.ts index c31bcd7b..6c89912d 100644 --- a/src/routes/(app)/send/invoice/[id]/+page.server.ts +++ b/src/routes/(app)/send/invoice/[id]/+page.server.ts @@ -22,16 +22,17 @@ export async function load({ params: { id }, parent }) { export const actions = { default: async ({ cookies, params: { id }, request }) => { + let p; try { let body = await fd(request); body.hash = id; - await post("/payments", body, auth(cookies)); + p = await post("/payments", body, auth(cookies)); } catch (e: any) { console.log("payment failed", id, e); error(500, e.message); } - redirect(307, "/sent"); + redirect(307, `/sent/${p.id}`); }, }; diff --git a/src/routes/(app)/send/lightning/[payreq]/+page.server.ts b/src/routes/(app)/send/lightning/[payreq]/+page.server.ts index ddec2968..3e87fa53 100644 --- a/src/routes/(app)/send/lightning/[payreq]/+page.server.ts +++ b/src/routes/(app)/send/lightning/[payreq]/+page.server.ts @@ -9,16 +9,17 @@ export const actions = { setAmount: async ({ request }) => fd(request), send: async ({ cookies, request }) => { + let p; try { let body = await fd(request); - await post("/payments", body, auth(cookies)); + p = await post("/payments", body, auth(cookies)); } catch (e: any) { if (e.message.includes("unusable")) e.message = "Failed to route payment, try sending a lower amount"; return fail(400, { message: e.message }); } - redirect(307, "/sent"); + redirect(307, `/sent/${p.id}`); }, }; diff --git a/src/routes/(app)/sent/+page.server.ts b/src/routes/(app)/sent/+page.server.ts deleted file mode 100644 index a7c24fb8..00000000 --- a/src/routes/(app)/sent/+page.server.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { get, auth } from "$lib/utils"; - -export let load = async ({ cookies }) => { - let aid = cookies.get("aid"); - let { payments } = await get(`/payments?aid=${aid}`, auth(cookies)); - return { payments }; -}; diff --git a/src/routes/(app)/sent/[id]/+page.server.ts b/src/routes/(app)/sent/[id]/+page.server.ts new file mode 100644 index 00000000..38d95e4e --- /dev/null +++ b/src/routes/(app)/sent/[id]/+page.server.ts @@ -0,0 +1,7 @@ +import { get, auth } from "$lib/utils"; + +export let load = async ({ cookies, params }) => { + let { id } = params; + let p = await get(`/payments/${id}`, auth(cookies)); + return p; +}; diff --git a/src/routes/(app)/sent/+page.svelte b/src/routes/(app)/sent/[id]/+page.svelte index 7d67ac03..24804974 100644 --- a/src/routes/(app)/sent/+page.svelte +++ b/src/routes/(app)/sent/[id]/+page.svelte @@ -6,11 +6,8 @@ import { t } from "$lib/translations"; export let data; - let { payments, user } = data; + let { amount, rate, tip, user } = data; let { currency, username } = user; - - let payment = payments[0]; - let { amount, rate, tip } = payment; amount = Math.abs(amount); toast.pop(0); |
