diff options
| author | Adam Soltys <[email protected]> | 2024-02-09 22:29:14 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2024-02-09 22:29:14 -0800 |
| commit | e58799c21b44f5d08f3de622d3df526b337b3d0a (patch) | |
| tree | c016992b51da1ded3526830285e68ffaf2fa9d9e | |
| parent | e51acb47d5b82f75126277239baaab52dbce3b72 (diff) | |
| download | coinos-ui-e58799c21b44f5d08f3de622d3df526b337b3d0a.tar.gz coinos-ui-e58799c21b44f5d08f3de622d3df526b337b3d0a.zip | |
separate messages page
| -rwxr-xr-x | bun.lockb | bin | 120406 -> 120406 bytes | |||
| -rw-r--r-- | src/components/AppHeader.svelte | 1 | ||||
| -rw-r--r-- | src/locales/en.json | 1 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+page.server.js | 35 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+page.svelte | 184 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/messages/+page.server.js | 38 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/messages/+page.svelte | 226 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/messages/[recipient]/+page.server.js | 25 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/messages/[recipient]/+page.svelte | 191 | ||||
| -rw-r--r-- | src/routes/(app)/send/[...text]/+page.svelte | 46 | ||||
| -rw-r--r-- | vite.config.js | 1 |
11 files changed, 330 insertions, 418 deletions
| Binary files differ diff --git a/src/components/AppHeader.svelte b/src/components/AppHeader.svelte index 57892b18..a2103cf5 100644 --- a/src/components/AppHeader.svelte +++ b/src/components/AppHeader.svelte @@ -23,6 +23,7 @@ }, { key: "nav.support", icon: "support", href: `/support` }, { key: "nav.map", icon: "map", href: `/${user.username}/map` }, + { key: "nav.messages", icon: "message", href: `/${user.username}/messages` }, { key: "nav.merch", icon: "shop", href: `https://coinosmerch.com` }, { key: "nav.signOut", icon: "logout", href: `/logout` }, ]; diff --git a/src/locales/en.json b/src/locales/en.json index 84f58ced..c4a272b6 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -154,6 +154,7 @@ "subheader": "Latest Updates" }, "user": { + "noMessages": "You have not sent or received any messages yet", "profilePage": "Profile page", "nostrPubkey": "Nostr pubkey", "requestPayment": "Request Payment", diff --git a/src/routes/(app)/[username]/(profile)/+page.server.js b/src/routes/(app)/[username]/(profile)/+page.server.js deleted file mode 100644 index 3110ec40..00000000 --- a/src/routes/(app)/[username]/(profile)/+page.server.js +++ /dev/null @@ -1,35 +0,0 @@ -import { auth, get } from "$lib/utils"; - -export async function load({ cookies, params, parent, url }) { - let { user, subject } = await parent(); - let { pubkey } = subject; - let { since = 0 } = params; - - let messages, invoices, sent, received, notes; - messages = invoices = sent = received = notes = []; - - if (pubkey) { - if (user) { - try { - messages = await get(`/${user.pubkey}/${since}/messages`); - messages = messages.sort((a, b) => b.created_at - a.created_at); - } catch (e) { - console.log(`failed to fetch nostr messages`, e); - } - - ({ invoices, sent, received } = await get("/requests", auth(cookies))); - } - - try { - notes = await get(`/${pubkey}/notes`); - } catch (e) { - console.log(`failed to fetch nostr notes for ${pubkey}`, e); - } - - notes.map((e) => { - e.seen = e.created_at; - }); - } - - 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 9dd968b6..420cc5fa 100644 --- a/src/routes/(app)/[username]/(profile)/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/+page.svelte @@ -1,70 +1,13 @@ <script> import { onMount } from "svelte"; import Icon from "$comp/Icon.svelte"; - import Avatar from "$comp/Avatar.svelte"; import Balance from "$comp/Balance.svelte"; - import Feed from "$comp/Feed.svelte"; import { t } from "$lib/translations"; - import { ease, f, sat, sats } from "$lib/utils"; - import { sign, send, encrypt, decrypt } from "$lib/nostr"; - import { event as e, password } from "$lib/store"; - import { browser } from "$app/environment"; export let data; - let { messages, notes, invoices, sent, received, subject, user, rates } = - data; + let { subject, user, rates } = data; $: refresh(data); - let refresh = (d) => - ({ messages, notes, invoices, sent, received, subject, user, rates } = d); - - let keys = new Set(); - let latest = []; - let ready; - - e.subscribe(async (event) => { - if (!(browser && event && ready)) return; - if ( - event.recipient?.id === user.id && - !~latest.findIndex((m) => m.id === event.id) - ) { - event.content = await decrypt({ event, user }); - - let i = latest.findIndex((m) => m.pubkey === event.pubkey); - let popped; - if (~i) popped = latest.splice(i, 1); - else popped = latest.pop(); - - latest.unshift(event); - latest = latest; - } - }); - - $: initialize($password); - let initialize = async (p) => { - let i = 0; - ready = false; - if (!(browser && user)) return; - while (i < messages.length) { - let event = messages[i]; - i++; - - if (!(event.author && event.recipient)) continue; - - let k = - event.author.id === user.id - ? event.recipient?.pubkey - : event.author.pubkey; - - if (!keys.has(k)) { - keys.add(k); - event.content = await decrypt({ event, user }); - if (event.content) latest.push(event); - } - } - - latest = latest; - ready = true; - }; + let refresh = (d) => ({ subject, user, rates } = d); </script> <div class="space-y-5"> @@ -82,10 +25,10 @@ </div> {/if} - <div class="flex flex-wrap sm:flex-nowrap gap-2 justify-center w-full"> + <div class="flex flex-wrap sm:flex-nowrap gap-3 justify-center w-full text-xl"> <a href={`/send`} class="w-full"> <button - class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-full" + class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex w-full bg-primary" > <div class="mx-auto flex"> <Icon icon="send" style="my-auto h-6 mr-2" /> @@ -96,7 +39,7 @@ <a href={`/${user.username}/receive`} class="w-full"> <button - class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-full" + class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex w-full bg-primary" > <div class="mx-auto flex"> <Icon icon="numpad" style="my-auto h-6 mr-2" /> @@ -104,124 +47,7 @@ </div> </button> </a> - - {#if user.eligible} - <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" /> --> - - <img - src="/images/bitcoin.svg" - class="w-8 border-4 border-transparent mr-2" - alt="Bitcoin" - /> - <div class="my-auto mt-1">Buy Bitcoin</div> - </div> - </button> - </a> - {/if} </div> </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> - <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} - </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> - <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> - </a> - {/each} - </div> - {/if} - - {#if latest.length && user?.id === subject.id} - <div class="relative"> - {#each latest as { content, pubkey, author, recipient }} - {@const user = author.id === user.id ? recipient : author} - <a href={`/${user.username}/messages`}> - <div class="flex hover:bg-gray-100 p-4 rounded-2xl"> - <div class="my-auto"> - <Avatar {user} size={"20"} disabled={true} /> - </div> - <div class="my-auto truncate"> - <div class="my-auto ml-1 text-lg font-bold">{user.username}</div> - <div class="my-auto ml-1 text-secondary text-lg truncate"> - {content} - </div> - </div> - </div> - </a> - {/each} - </div> - {/if} </div> diff --git a/src/routes/(app)/[username]/messages/+page.server.js b/src/routes/(app)/[username]/messages/+page.server.js index f0148be7..eeb883ee 100644 --- a/src/routes/(app)/[username]/messages/+page.server.js +++ b/src/routes/(app)/[username]/messages/+page.server.js @@ -1,25 +1,33 @@ -import { get } from "$lib/utils"; +import { auth, get } from "$lib/utils"; -export async function load({ params, parent, url }) { +export async function load({ cookies, params, parent, url }) { let { user, subject } = await parent(); + let { pubkey } = subject; let { since = 0 } = params; - let messages = []; + let messages = [], + notes = []; - try { - messages = await get(`/${user.pubkey}/${since}/messages`); + if (pubkey) { + if (user) { + try { + messages = await get(`/${user.pubkey}/${since}/messages`); + messages = messages.sort((a, b) => b.created_at - a.created_at); + } catch (e) { + console.log(`failed to fetch nostr messages`, e); + } + } - messages = messages - .filter( - (m) => m.recipient?.id === subject.id || m.author?.id === subject.id - ) - .sort((a, b) => a.created_at - b.created_at); + try { + notes = await get(`/${pubkey}/notes`); + } catch (e) { + console.log(`failed to fetch nostr notes for ${pubkey}`, e); + } - if (user.id === subject.id) - messages = messages.filter((m) => m.recipient.id === m.author.id); - } catch (e) { - console.log(`failed to fetch nostr messages`, e); + notes.map((e) => { + e.seen = e.created_at; + }); } - return { messages, user }; + return { messages, notes } } diff --git a/src/routes/(app)/[username]/messages/+page.svelte b/src/routes/(app)/[username]/messages/+page.svelte index 48855a26..4738ea2c 100644 --- a/src/routes/(app)/[username]/messages/+page.svelte +++ b/src/routes/(app)/[username]/messages/+page.svelte @@ -1,182 +1,92 @@ <script> - import { cubicInOut } from "svelte/easing"; - import { browser } from "$app/environment"; import { t } from "$lib/translations"; - import { format } from "date-fns"; - import { scale, fade, fly } from "svelte/transition"; - import { enhance } from "$app/forms"; import Avatar from "$comp/Avatar.svelte"; - import Icon from "$comp/Icon.svelte"; - import { back, fail, focus } from "$lib/utils"; - import { sign, send, encrypt, decrypt } from "$lib/nostr"; import { event as e, password } from "$lib/store"; - import { tick, onMount } from "svelte"; - import { getEventHash } from "nostr-tools"; + import { browser } from "$app/environment"; + import { decrypt } from "$lib/nostr"; export let data; - function fadeScale( - node, - { delay = 0, duration = 200, easing = (x) => x, baseScale = 0 } - ) { - const o = +getComputedStyle(node).opacity; - const m = getComputedStyle(node).transform.match(/scale\(([0-9.]+)\)/); - const s = m ? m[1] : 1; - const is = 1 - baseScale; - - return { - delay, - duration, - css: (t) => { - const eased = easing(t); - return `opacity: ${eased * o}; transform: scale(${ - eased * s * is + baseScale - })`; - }, - }; - } + let { messages, notes, invoices, sent, received, subject, user, rates } = + data; + $: refresh(data); + let refresh = (d) => + ({ messages, notes, invoices, sent, received, subject, user, rates } = d); - let { messages, subject, user } = data; - let input, pane; + let keys = new Set(); + let latest = []; + let ready; - $: initialize($password); - let initialize = async (p) => { - await Promise.all( - messages.map( - async (event) => (event.message = await decrypt({ event, user })) - ) - ); + e.subscribe(async (event) => { + if (!(browser && event && ready)) return; + if ( + event.recipient?.id === user.id && + !~latest.findIndex((m) => m.id === event.id) + ) { + event.content = await decrypt({ event, user }); - messages = messages; - tick().then(() => pane && (pane.scrollTop = pane.scrollHeight)); - }; + let i = latest.findIndex((m) => m.pubkey === event.pubkey); + let popped; + if (~i) popped = latest.splice(i, 1); + else popped = latest.pop(); - e.subscribe(async (event) => { - let found = ~messages.findIndex((m) => m.id === event?.id); - if (event?.recipient.id === user.id && !found) { - event.message = await decrypt({ event, user }); - messages.push(event); - messages = messages; - tick().then(() => pane && (pane.scrollTop = pane.scrollHeight)); + latest.unshift(event); + latest = latest; } }); - let sent, submitting, message; - let submit = async () => { - submitting = true; - - let event = { - pubkey: user.pubkey, - created_at: Math.floor(Date.now() / 1000), - kind: 4, - tags: [["p", subject.pubkey]], - }; - - event.message = message; - - message = ""; - - event.author = user; - event.recipient = subject; + $: initialize($password); + let initialize = async (p) => { + let i = 0; + ready = false; + if (!(browser && user)) return; + while (i < messages.length) { + let event = messages[i]; + i++; - messages.push(event); - messages = messages; - tick().then(() => pane && (pane.scrollTop = pane.scrollHeight)); + if (!(event.author && event.recipient)) continue; - try { - event.content = await encrypt({ - message: event.message, - recipient: subject.pubkey, - user, - }); - event.id = getEventHash(event); - await sign({ event, user }); - await send(event); + let k = + event.author.id === user.id + ? event.recipient?.pubkey + : event.author.pubkey; - sent = true; - } catch (e) { - console.log(e); - fail("Failed to send message"); + if (!keys.has(k)) { + keys.add(k); + event.content = await decrypt({ event, user }); + if (event.content) latest.push(event); + } } - message = ""; - submitting = false; - }; - - let keydown = (e) => { - if (e.key === "Enter") { - e.preventDefault(); - submit(); - } + latest = latest; + ready = true; }; </script> -<div class="container max-w-xl mx-auto px-4 space-y-5"> - <div - class="h-[50vh] max-h-[50vh] overflow-y-scroll scrollbar-thin scrollbar-thumb-[#F2F6FC] scrollbar-track-white pr-8" - bind:this={pane} - > - {#each messages as { id, author, message, created_at, pubkey }} - {@const ours = pubkey === user.pubkey} - {@const theirs = !ours} - - {#if message} - <div - class="flex gap-2" - class:flex-row-reverse={theirs} - class:justify-end={theirs} - in:fadeScale={{ - easing: cubicInOut, - baseScale: 0.5, - }} - > - <div - class="rounded-2xl px-4 py-2 max-w-[300px] mb-1 text-lg" - class:ours - class:theirs - :key={id} - > - {message} +<div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5"> + <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10"> + {$t("user.messages")} + </h1> + {#if latest.length && user?.id === subject.id} + <div class="relative"> + {#each latest as { content, pubkey, author, recipient }} + {@const user = author.id === user.id ? recipient : author} + <a href={`/${subject.username}/messages/${user.username}`}> + <div class="flex hover:bg-gray-100 p-4 rounded-2xl"> + <div class="my-auto"> + <Avatar {user} size={"20"} disabled={true} /> + </div> + <div class="my-auto truncate"> + <div class="my-auto ml-1 text-lg font-bold">{user.username}</div> + <div class="my-auto ml-1 text-secondary text-lg truncate"> + {content} + </div> + </div> </div> - <div class="mt-auto"> - <Avatar user={author} size={"12"} /> - </div> - </div> - <div class="text-sm text-gray-400 mb-6" class:text-right={ours}> - {format(new Date(created_at * 1000), "MMM d")}, - {format(new Date(created_at * 1000), "h:mm aa")} - </div> - {/if} - {/each} - </div> - - <form - method="POST" - class="w-full border rounded-xl outline-none gap-4 flex p-0 pr-2" - on:submit|preventDefault={submit} - > - <input type="hidden" name="requester_id" value={user.id} /> - <input type="hidden" name="recipient" value={subject.username} /> - - <div - use:focus - contenteditable - class="grow break-all py-4 outline-none mt-0 pl-4" - bind:innerHTML={message} - on:keydown={keydown} - /> - <button type="submit" class="my-auto shrink-0"> - <Icon icon="send" style="w-8" /> - </button> - </form> + </a> + {/each} + </div> + {:else} + <div class="text-center">{$t("user.noMessages")}</div> + {/if} </div> - -<style> - .ours { - @apply bg-gradient-to-r from-[#F2F6FC] to-[#E1E3FF] text-black rounded-br-none ml-auto; - } - - .theirs { - @apply bg-gray-100 text-black rounded-bl-none; - } -</style> diff --git a/src/routes/(app)/[username]/messages/[recipient]/+page.server.js b/src/routes/(app)/[username]/messages/[recipient]/+page.server.js new file mode 100644 index 00000000..f0148be7 --- /dev/null +++ b/src/routes/(app)/[username]/messages/[recipient]/+page.server.js @@ -0,0 +1,25 @@ +import { get } from "$lib/utils"; + +export async function load({ params, parent, url }) { + let { user, subject } = await parent(); + let { since = 0 } = params; + + let messages = []; + + try { + messages = await get(`/${user.pubkey}/${since}/messages`); + + messages = messages + .filter( + (m) => m.recipient?.id === subject.id || m.author?.id === subject.id + ) + .sort((a, b) => a.created_at - b.created_at); + + if (user.id === subject.id) + messages = messages.filter((m) => m.recipient.id === m.author.id); + } catch (e) { + console.log(`failed to fetch nostr messages`, e); + } + + return { messages, user }; +} diff --git a/src/routes/(app)/[username]/messages/[recipient]/+page.svelte b/src/routes/(app)/[username]/messages/[recipient]/+page.svelte new file mode 100644 index 00000000..dfff6ed2 --- /dev/null +++ b/src/routes/(app)/[username]/messages/[recipient]/+page.svelte @@ -0,0 +1,191 @@ +<script> + import { cubicInOut } from "svelte/easing"; + import { browser } from "$app/environment"; + import { t } from "$lib/translations"; + import { format } from "date-fns"; + import { scale, fade, fly } from "svelte/transition"; + import { enhance } from "$app/forms"; + import Avatar from "$comp/Avatar.svelte"; + import Icon from "$comp/Icon.svelte"; + import { back, fail, focus } from "$lib/utils"; + import { sign, send, encrypt, decrypt } from "$lib/nostr"; + import { event as e, password } from "$lib/store"; + import { tick, onMount } from "svelte"; + import { getEventHash } from "nostr-tools"; + + export let data; + + function fadeScale( + node, + { delay = 0, duration = 200, easing = (x) => x, baseScale = 0 } + ) { + const o = +getComputedStyle(node).opacity; + const m = getComputedStyle(node).transform.match(/scale\(([0-9.]+)\)/); + const s = m ? m[1] : 1; + const is = 1 - baseScale; + + return { + delay, + duration, + css: (t) => { + const eased = easing(t); + return `opacity: ${eased * o}; transform: scale(${ + eased * s * is + baseScale + })`; + }, + }; + } + + let { messages, subject, user } = data; + let input, pane; + + $: initialize($password); + let initialize = async (p) => { + await Promise.all( + messages.map( + async (event) => (event.message = await decrypt({ event, user })) + ) + ); + + messages = messages; + tick().then(() => pane && (pane.scrollTop = pane.scrollHeight)); + }; + + e.subscribe(async (event) => { + let found = ~messages.findIndex((m) => m.id === event?.id); + if (event?.recipient.id === user.id && !found) { + event.message = await decrypt({ event, user }); + messages.push(event); + messages = messages; + tick().then(() => pane && (pane.scrollTop = pane.scrollHeight)); + } + }); + + let sent, submitting, message; + let submit = async () => { + submitting = true; + + let event = { + pubkey: user.pubkey, + created_at: Math.floor(Date.now() / 1000), + kind: 4, + tags: [["p", subject.pubkey]], + }; + + event.message = message; + + message = ""; + + event.author = user; + event.recipient = subject; + + messages.push(event); + messages = messages; + tick().then(() => pane && (pane.scrollTop = pane.scrollHeight)); + + try { + event.content = await encrypt({ + message: event.message, + recipient: subject.pubkey, + user, + }); + event.id = getEventHash(event); + await sign({ event, user }); + await send(event); + + sent = true; + } catch (e) { + console.log(e); + fail("Failed to send message"); + } + + message = ""; + submitting = false; + }; + + let keydown = (e) => { + if (e.key === "Enter") { + e.preventDefault(); + submit(); + } + }; +</script> + +<div class="container max-w-xl mx-auto px-4 space-y-5"> + <button + type="button" + class="hover:opacity-80" + data-sveltekit-preload-data="false" + on:click={back} + > + <Icon icon="arrow-left" style="w-10" /> + </button> + + <div + class="h-[50vh] max-h-[50vh] overflow-y-scroll scrollbar-thin scrollbar-thumb-[#F2F6FC] scrollbar-track-white pr-8" + bind:this={pane} + > + {#each messages as { id, author, message, created_at, pubkey }} + {@const ours = pubkey === user.pubkey} + {@const theirs = !ours} + + {#if message} + <div + class="flex gap-2" + class:flex-row-reverse={theirs} + class:justify-end={theirs} + in:fadeScale={{ + easing: cubicInOut, + baseScale: 0.5, + }} + > + <div + class="rounded-2xl px-4 py-2 max-w-[300px] mb-1 text-lg" + class:ours + class:theirs + :key={id} + > + {message} + </div> + <div class="mt-auto"> + <Avatar user={author} size={"12"} /> + </div> + </div> + <div class="text-sm text-gray-400 mb-6" class:text-right={ours}> + {format(new Date(created_at * 1000), "MMM d")}, + {format(new Date(created_at * 1000), "h:mm aa")} + </div> + {/if} + {/each} + </div> + + <form + method="POST" + class="w-full border rounded-xl outline-none gap-4 flex p-0 pr-2" + on:submit|preventDefault={submit} + > + <input type="hidden" name="requester_id" value={user.id} /> + <input type="hidden" name="recipient" value={subject.username} /> + + <div + use:focus + contenteditable + class="grow break-all py-4 outline-none mt-0 pl-4" + bind:innerHTML={message} + on:keydown={keydown} + /> + <button type="submit" class="my-auto shrink-0"> + <Icon icon="send" style="w-8" /> + </button> + </form> +</div> + +<style> + .ours { + @apply bg-gradient-to-r from-[#F2F6FC] to-[#E1E3FF] text-black rounded-br-none ml-auto; + } + + .theirs { + @apply bg-gray-100 text-black rounded-bl-none; + } +</style> diff --git a/src/routes/(app)/send/[...text]/+page.svelte b/src/routes/(app)/send/[...text]/+page.svelte index 489650a5..e7490d63 100644 --- a/src/routes/(app)/send/[...text]/+page.svelte +++ b/src/routes/(app)/send/[...text]/+page.svelte @@ -20,7 +20,6 @@ let el, text; - let keypress = (e) => e.key === "Enter" && (e.preventDefault() || el.click()); let paste = async () => { @@ -36,7 +35,7 @@ {$t("payments.send")} </h1> - <form method="POST" use:enhance> + <form method="POST" use:enhance class="space-y-2 text-xl"> {#if form?.error} <div class="mb-5"> <div class="text-red-600"> @@ -49,31 +48,18 @@ <textarea use:focus name="text" - placeholder={$t("user.send.placeholder")} + placeholder={$t("user.send.placeholder")} on:keypress={keypress} - class="w-full p-4 border rounded-xl h-48" + class="w-full p-4 border rounded-xl h-48 text-xl" bind:value={text} /> </div> - <div class="grid grid-cols-2 md:grid-cols-4 gap-2"> - <a - href={`/qr/${encodeURIComponent(text)}`} - class:invisible={!text?.length} - > - <button - type="button" - class="flex border rounded-full px-6 py-2 font-bold hover:opacity-80 mr-1 w-full" - > - <Icon icon="qr" style="mr-2 w-6 my-auto invert" /> - <div class="my-auto">{$t("user.send.makeQR")}</div> - </button> - </a> - + <div class="flex gap-2"> <a href="/scan" class="w-full"> <button type="button" - class="flex border rounded-full px-6 py-2 font-bold hover:opacity-80 mr-1 w-full" + class="flex border rounded-2xl px-6 py-5 font-bold hover:opacity-80 w-full bg-primary justify-center" > <Icon icon="camera" style="mr-2 w-6 my-auto" /> <div class="my-auto">{$t("user.send.scan")}</div> @@ -82,28 +68,26 @@ <button type="button" - class="flex border rounded-full px-6 py-2 font-bold hover:opacity-80 mr-1" + class="flex border rounded-2xl px-6 py-5 font-bold hover:opacity-80 w-full bg-primary justify-center" on:click={paste} > <Icon icon="paste" style="mr-2 w-6 my-auto" /> <div class="my-auto">{$t("user.send.paste")}</div> </button> - - <button - bind:this={el} - type="submit" - class="{!text - ? 'opacity-50' - : 'opacity-100 hover:opacity-80'} bg-black text-white border rounded-full px-6 py-2 font-bold" - > - {$t("user.send.next")} - </button> </div> + + <button + bind:this={el} + type="submit" + class="bg-black text-white border rounded-2xl px-6 py-5 w-full font-bold" + > + {$t("user.send.next")} + </button> </form> {#if contacts.length} <div class="space-y-5"> - <h1 class="px-3 md:px-0 text-xl font-semibold mt-10"> + <h1 class="px-3 md:px-0 text-2xl font-semibold mt-10"> {$t("user.send.contacts")} </h1> <div> diff --git a/vite.config.js b/vite.config.js index 83626bd5..88ac65cb 100644 --- a/vite.config.js +++ b/vite.config.js @@ -8,6 +8,7 @@ const config = { resolve: { alias: { $comp: path.resolve("src/components"), + "nostr-tools": "@asoltys/nostr-tools", }, }, server: { |
