diff options
| author | Adam Soltys <[email protected]> | 2023-02-15 19:57:47 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2023-02-15 19:57:47 -0800 |
| commit | 3403bf5c6090109725493b4c7a4fef28266beda8 (patch) | |
| tree | 7ee24e3933bc460be310707df53e823c579e47ff | |
| parent | 979d9dd27a147bae13ba953a7e260412fa024f57 (diff) | |
| download | coinos-ui-3403bf5c6090109725493b4c7a4fef28266beda8.tar.gz coinos-ui-3403bf5c6090109725493b4c7a4fef28266beda8.zip | |
messages on dashboard
| -rw-r--r-- | src/components/Balance.svelte | 2 | ||||
| -rw-r--r-- | src/lib/nostr.js | 3 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+page.server.js | 23 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+page.svelte | 191 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/settings/[...tab]/+page.server.js | 1 | ||||
| -rw-r--r-- | src/routes/(app)/buy/+page.server.js | 19 | ||||
| -rw-r--r-- | src/routes/(app)/buy/+page.svelte | 126 | ||||
| -rw-r--r-- | src/routes/(app)/send/lightning/[payreq]/+page.svelte | 2 |
8 files changed, 306 insertions, 61 deletions
diff --git a/src/components/Balance.svelte b/src/components/Balance.svelte index e95e5dde..5d993c28 100644 --- a/src/components/Balance.svelte +++ b/src/components/Balance.svelte @@ -9,7 +9,7 @@ <div class="space-y-2"> <div class="text-5xl font-bold tabular-nums"> {#if isNaN($animatedRate)} - <div class="text-gray-200">---</div> + <div class="text-gray-200">—</div> {:else} {f((user.balance * $animatedRate) / sats, user.currency)} {/if} diff --git a/src/lib/nostr.js b/src/lib/nostr.js index de19b477..13251856 100644 --- a/src/lib/nostr.js +++ b/src/lib/nostr.js @@ -80,7 +80,7 @@ export let decrypt = async ({ event, user }) => { return Buffer.from(message).toString('utf8'); } catch (e) { - console.log(e); + // console.log(e); } }; @@ -119,6 +119,7 @@ let getPrivateKey = async (user) => { seed = mnemonicToSeedSync(mnemonic); key = bip32.fromSeed(seed); child = key.derivePath("m/44'/1237'/0'/0/0"); + return child.privateKey; }; diff --git a/src/routes/(app)/[username]/(profile)/+page.server.js b/src/routes/(app)/[username]/(profile)/+page.server.js index c68cfecc..d1fa2f41 100644 --- a/src/routes/(app)/[username]/(profile)/+page.server.js +++ b/src/routes/(app)/[username]/(profile)/+page.server.js @@ -1,22 +1,33 @@ import { auth, get } from '$lib/utils'; export async function load({ cookies, params, parent, url }) { - let { subject } = await parent(); + let { user, subject } = await parent(); let { pubkey } = subject; + let { since = 0 } = params; - let events = []; + let messages = []; + + if (user) { + try { + messages = await get(`/${user.pubkey}/${since}/messages`); + } catch (e) { + console.log(`failed to fetch nostr messages`, e); + } + } + + let notes = []; try { - events = await get(`/${pubkey}/notes`); + notes = await get(`/${pubkey}/notes`); } catch (e) { - console.log(`failed to fetch nostr events for ${pubkey}`, e); + console.log(`failed to fetch nostr notes for ${pubkey}`, e); } - events.map((e) => { + notes.map((e) => { e.seen = e.created_at; }); let { invoices, sent, received } = await get('/requests', auth(cookies)); - return { invoices, events, sent, received }; + return { invoices, messages, notes, sent, received }; } diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte index c704c00d..45fe1d84 100644 --- a/src/routes/(app)/[username]/(profile)/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/+page.svelte @@ -1,75 +1,162 @@ <script> - import { Avatar, Balance, Feed } from '$comp'; + import { onMount } from 'svelte'; + import { Icon, Avatar, Balance, Feed } from '$comp'; import { t } from '$lib/translations'; import { f, sat, sats } from '$lib/utils'; + import { sign, send, encrypt, decrypt } from '$lib/nostr'; + import { browser } from '$app/environment'; export let data; - let { events, invoices, sent, received, subject, user } = data; + let { messages, notes, invoices, sent, received, subject, user } = data; + + let keys = new Set(); + let latest = []; + let i = 0; + + onMount(async () => { + if (!browser) return; + while (latest.length < 3 && i < messages.length) { + let event = messages[i]; + let { pubkey: k } = event.user; + if (k !== user.pubkey && !keys.has(k)) { + keys.add(k); + event.content = await decrypt({ event, user }); + if (event.content) latest.push(event); + } + i++; + } + + latest = latest; + }); </script> -<div class="flex justify-center lg:justify-start"> +<div class="space-y-8"> {#if user?.id === subject.id} - <Balance {user} /> - {/if} -</div> + <div> + {#if user.balance > 0 && false} + <h1 class="text-secondary text-xl">Balance</h1> + <div class="flex justify-center lg:justify-start"> + <Balance {user} /> + </div> + {:else} + <div class="space-y-3"> + <h1 class="text-2xl">Welcome</h1> + + <p class="text-secondary"> + Get started by making a deposit or getting someone to pay you in Bitcoin! + </p> -<div> - {#each invoices as { amount, currency, rate, request: { requester, recipient }, hash }} - {@const requested = user.username === requester?.username} - <a href={`/${recipient.username}/invoice/${hash}` + (requested ? '/tip' : '')}> - <div class="border-b p-2 last:border-b-0 hover:bg-gray-100"> - <div class="flex gap-2"> - <div> - {#if requested} - <div class="flex"> - <Avatar user={recipient} size={20} /> - <div class="my-auto text-left"> - <p class="text-secondary">{$t('payments.invoiceFrom')}</p> - <p class="ml-1 text-lg break-words">{recipient.username}</p> + <div class="flex gap-4 justify-center w-full"> + <a href={`/${user.username}/receive`}> + <button class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60"> + <div class="mx-auto flex"> + <Icon icon="numpad" style="my-auto h-6 mr-2" /> + <div class="my-auto mt-1">Receive a Payment</div> </div> + </button> + </a> + + <!-- <a href={`/buy`}> --> + <!-- <button class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-60"> --> + <!-- <div class="mx-auto flex"> --> + <!-- <Icon icon="plus" style="my-auto h-6 mr-2" /> --> + <!-- <div class="my-auto mt-1">Buy Bitcoin</div> --> + <!-- </div> --> + <!-- </button> --> + <!-- </a> --> + </div> + </div> + {/if} + </div> + {/if} + + {#if invoices.length} + <div> + <h1 class="text-secondary text-xl">Invoices</h1> + {#each invoices as { amount, currency, rate, request: { requester, recipient }, hash }} + {@const requested = user.username === requester?.username} + <a href={`/${recipient.username}/invoice/${hash}` + (requested ? '/tip' : '')}> + <div class="border-b p-2 last:border-b-0 hover:bg-gray-100"> + <div class="flex gap-2"> + <div> + {#if requested} + <div class="flex"> + <Avatar user={recipient} size={20} /> + <div class="my-auto text-left"> + <p class="text-secondary">{$t('payments.invoiceFrom')}</p> + <p class="ml-1 text-lg break-words">{recipient.username}</p> + </div> + </div> + {:else} + <div class="flex"> + <Avatar user={requester} size={20} /> + <div class="my-auto text-left"> + <p class="text-secondary">{$t('payments.awaiting')}</p> + <p class="ml-1 text-lg break-words">{requester.username}</p> + </div> + </div> + {/if} </div> - {:else} - <div class="flex"> - <Avatar user={requester} size={20} /> - <div class="my-auto text-left"> - <p class="text-secondary">{$t('payments.awaiting')}</p> - <p class="ml-1 text-lg break-words">{requester.username}</p> + <div class="whitespace-nowrap my-auto ml-auto flex gap-2"> + <div class="font-bold"> + {f(amount * (rate / sats), currency)} </div> + + <div class="text-secondary">{sat(amount)}</div> </div> - {/if} - </div> - <div class="whitespace-nowrap my-auto ml-auto flex gap-2"> - <div class="font-bold"> - {f(amount * (rate / sats), currency)} </div> - - <div class="text-secondary">{sat(amount)}</div> </div> - </div> - </div> - </a> - {/each} + </a> + {/each} + </div> + {/if} + {#if received.length} + <div> + <h1 class="text-secondary text-xl">Requests</h1> - {#each received as { id, invoice, memo, requester: r }} - <a href={`/${user.username}/receive/${id}`}> - <div class="border-b p-2 last:border-b-0 hover:bg-gray-100"> - <div class="flex"> - <div> + {#each received as { id, invoice, memo, requester: r }} + <a href={`/${user.username}/receive/${id}`}> + <div class="border-b p-2 last:border-b-0 hover:bg-gray-100"> <div class="flex"> - <Avatar user={r} size={20} /> - <div class="my-auto text-left"> - <p class="ml-1 text-lg break-words">{r.username}</p> - <p class="ml-1 text-secondary">{memo}</p> + <div> + <div class="flex"> + <Avatar user={r} size={20} /> + <div class="my-auto text-left"> + <p class="ml-1 text-lg break-words">{r.username}</p> + <p class="ml-1 text-secondary">{memo}</p> + </div> + </div> + </div> + <div class="whitespace-nowrap my-auto ml-auto flex gap-2"> + <button class="rounded-full border py-2 px-4 font-bold hover:opacity-80 w-full" + >Invoice</button + > </div> </div> </div> - <div class="whitespace-nowrap my-auto ml-auto flex gap-2"> - <button class="rounded-full border py-2 px-4 font-bold hover:opacity-80 w-full" - >Invoice</button - > + </a> + {/each} + </div> + {/if} + + {#if latest.length} + <div> + <h1 class="text-2xl">Messages</h1> + {#each latest as { content, user }} + <div class="flex"> + <div class="my-auto"> + <Avatar {user} size={12} disabled={true} /> </div> + <div class="my-auto ml-1 text-secondary">{user.username}</div> + {content} </div> - </div> - </a> - {/each} + {/each} + </div> + {/if} </div> + +<style> + h1 { + @apply mb-2; + } +</style> diff --git a/src/routes/(app)/[username]/settings/[...tab]/+page.server.js b/src/routes/(app)/[username]/settings/[...tab]/+page.server.js index dd16459f..94240adc 100644 --- a/src/routes/(app)/[username]/settings/[...tab]/+page.server.js +++ b/src/routes/(app)/[username]/settings/[...tab]/+page.server.js @@ -9,6 +9,7 @@ export function load({ params, url }) { export const actions = { default: async ({ cookies, request }) => { let form = Object.fromEntries(await request.formData()); + form.prompt = form.prompt === 'on' let user = { ...(await get('/me', auth(cookies))), ...form }; try { diff --git a/src/routes/(app)/buy/+page.server.js b/src/routes/(app)/buy/+page.server.js new file mode 100644 index 00000000..e03d6c0a --- /dev/null +++ b/src/routes/(app)/buy/+page.server.js @@ -0,0 +1,19 @@ +import { env } from '$env/dynamic/private'; +import { error, redirect } from '@sveltejs/kit'; +import { post, auth } from '$lib/utils'; +import { createTicket } from '$lib/ticket'; + +export const actions = { + default: async ({ cookies, request }) => { + let form = await request.formData(); + + await post('/buy', { + number: form.get('number').replace(/\s+/g, ''), + month: form.get('expiry').slice(0, 2), + year: form.get('expiry').slice(-2), + cvc: form.get('cvc') + }, auth(cookies)); + + throw redirect(307, `/launch/thanks/${asset}`); + } +}; diff --git a/src/routes/(app)/buy/+page.svelte b/src/routes/(app)/buy/+page.svelte new file mode 100644 index 00000000..34cfc2e5 --- /dev/null +++ b/src/routes/(app)/buy/+page.svelte @@ -0,0 +1,126 @@ +<script> + import { enhance } from '$app/forms'; + import { Icon, Lock, Card, Calendar, Spinner } from '$comp'; + + export let data; + + let { username } = data.user; + let loading; + + let amount = 100; + let n = '4242 4242 4242 4242'; + let d = '10/25'; + let cvc = 444; +</script> + +<div class="container px-4 max-w-xl mx-auto mt-10 space-y-5"> + <div class="w-full flex"> + <a href="/" class="mx-auto"> + <Icon icon="logo" /> + </a> + </div> + + <div class="flex justify-center items-center text-center"> + <div class="shadow-xl rounded-3xl px-10 pt-5 pb-10 space-y-5 w-full mx-5"> + <div class="relative"> + <!-- <p class="absolute right-0 top-1 underline"><Icon icon="settings" /></p> --> + <h1 class="text-2xl md:text-3xl font-semibold">Buy Bitcoin</h1> + </div> + <p class="text-secondary">Purchase up to $500 worth of Bitcoin from us.</p> + + <p class="text-secondary"> + If you want more, try <a href="https://www.kraken.com/" target="_blank" class="text-black" + >Kraken</a + > + or + <a href="https://shakepay.com/" target="_blank" class="text-black">ShakePay</a> or ask + around at our next + <a href="https://www.meetup.com/vancouver-bitcoiners" target="_blank" class="text-black" + >meetup</a + >. + </p> + + <form + method="POST" + class="flex flex-wrap gap-5 w-full p-5 mt-20" + use:enhance={() => { + loading = true; + return ({ update }) => update() && (loading = false); + }} + > + <input type="hidden" name="username" value={username} /> + + <label class="relative w-full flex flex-col"> + <span class="font-bold mb-3">Amount</span> + <input + class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300" + type="text" + name="number" + placeholder="500" + bind:value={amount} + /> + <span class="absolute text-gray-300 bottom-[9px] left-0 translate-x-1/2 w-6">$</span> + </label> + + <label class="relative w-full flex flex-col"> + <span class="font-bold mb-3">Card number</span> + <input + class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300" + type="text" + name="number" + placeholder="0000 0000 0000 0000" + bind:value={n} + /> + <Card /> + </label> + + <label class="relative flex-1 flex flex-col"> + <span class="font-bold mb-3">Expire date</span> + <input + class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300" + type="text" + name="expiry" + placeholder="MM/YY" + bind:value={d} + /> + <Calendar /> + </label> + + <label class="relative flex-1 flex flex-col"> + <span class="font-bold flex items-center gap-3 mb-3"> + CVC/CVV + <span class="relative group"> + <span + class="hidden group-hover:flex justify-center items-center px-2 py-1 text-xs absolute -right-2 transform translate-x-full -translate-y-1/2 w-max top-1/2 bg-black text-white" + > + 3-digit number on the back of your card</span + > + </span> + </span> + <input + class="rounded-md peer pl-12 pr-2 py-2 border-2 border-gray-200 placeholder-gray-300" + type="text" + name="cvc" + placeholder="•••" + bind:value={cvc} + /> + <Lock /> + </label> + + <div class="w-full flex mt-8"> + <button + class="bg-black text-white border rounded-full px-8 py-4 font-bold hover:opacity-80 mx-auto text-xl" + type="submit" + disabled={loading} + > + {#if loading} + <Spinner /> + {:else} + Submit + {/if} + </button> + </div> + </form> + </div> + </div> +</div> diff --git a/src/routes/(app)/send/lightning/[payreq]/+page.svelte b/src/routes/(app)/send/lightning/[payreq]/+page.svelte index 6ae699d2..e9502636 100644 --- a/src/routes/(app)/send/lightning/[payreq]/+page.svelte +++ b/src/routes/(app)/send/lightning/[payreq]/+page.svelte @@ -89,7 +89,7 @@ </div> {/if} {:else} - <form method="POST" action="?/setAmount" class="w-[300px] mx-auto"> + <form method="POST" action="?/setAmount" class="w-[300px] mx-auto" use:enhance> <input type="hidden" value={a} name="amount" /> <Numpad bind:amount={a} {currency} /> <button |
