diff options
| author | Adam Soltys <[email protected]> | 2023-03-04 23:17:37 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2023-03-04 23:17:37 -0800 |
| commit | e6707b3ef14730e25a90b055c45eae8bd729f9ea (patch) | |
| tree | 0e024a96b5214b7b40466ad2b250d578dd6c80de | |
| parent | 6c31cea7d17856a0d0d2f6cb71e3e4f6c0e24d74 (diff) | |
| download | coinos-ui-layout-updates.tar.gz coinos-ui-layout-updates.zip | |
experimentinglayout-updates
| -rw-r--r-- | src/lib/utils.js | 80 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+layout.svelte | 11 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/messages/+page.server.js | 2 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/messages/+page.svelte | 128 |
4 files changed, 125 insertions, 96 deletions
diff --git a/src/lib/utils.js b/src/lib/utils.js index 7b10c638..049617f4 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -7,16 +7,16 @@ import { PUBLIC_COINOS_URL } from '$env/static/public'; import { page } from '$app/stores'; import { get as getStore } from 'svelte/store'; -export function scroll(section) { +export let scroll = (section) => { if (getStore(page).url.pathname !== '/') goto('/'); setTimeout(() => section.scrollIntoView({ behavior: 'smooth' }), 500); -} +}; -const base = browser ? '' : PUBLIC_COINOS_URL; +let base = browser ? '' : PUBLIC_COINOS_URL; -export const punk = (k) => Math.floor((parseInt(k.slice(-2), 16) / 256) * 64) + 1 + '.webp'; +export let punk = (k) => Math.floor((parseInt(k.slice(-2), 16) / 256) * 64) + 1 + '.webp'; -export const g = (url, fetch, headers) => +export let g = (url, fetch, headers) => fetch(base + url, { headers }) .then((r) => r.text()) .then((body) => { @@ -27,7 +27,7 @@ export const g = (url, fetch, headers) => } }); -export const get = (url, headers = {}) => +export let get = (url, headers = {}) => fetch(base + url, { headers }) .then(async (r) => { if (r.ok) return r.text(); @@ -41,7 +41,7 @@ export const get = (url, headers = {}) => } }); -export const post = (url, body, headers) => { +export let post = (url, body, headers) => { headers = { ...headers, 'content-type': 'application/json', accept: 'application/json' }; return fetch(base + url, { method: 'POST', body: JSON.stringify(body), headers }) .then(async (r) => { @@ -71,23 +71,23 @@ export const post = (url, body, headers) => { }); }; -export const copy = (text) => { +export let copy = (text) => { navigator.clipboard.writeText(text); success('Copied!'); }; -export function reverseFormat(val, locale) { +export let reverseFormat = (val, locale) => { 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 = [/customers/, /settings/, /payments/]; -export const success = (m) => { +export let success = (m) => { toast.pop(); toast.push(m, { theme: { @@ -96,7 +96,7 @@ export const success = (m) => { }); }; -export const warning = (m) => { +export let warning = (m) => { toast.pop(); toast.push(m, { theme: { @@ -104,7 +104,7 @@ export const warning = (m) => { } }); }; -export const fail = (m) => { +export let fail = (m) => { toast.pop(); toast.push(m, { theme: { @@ -112,7 +112,7 @@ export const fail = (m) => { } }); }; -export const info = (m) => { +export let info = (m) => { toast.pop(); toast.push(m, { theme: { @@ -121,7 +121,7 @@ export const info = (m) => { }); }; -export const login = async (user, cookies) => { +export let login = async (user, cookies) => { let maxAge = 30 * 24 * 60 * 60; let res = await fetch(base + '/login', { @@ -145,17 +145,17 @@ export const login = async (user, cookies) => { cookies.set('token', token, opts); }; -export const auth = (cookies) => ({ authorization: `Bearer ${cookies.get('token')}` }); +export let auth = (cookies) => ({ authorization: `Bearer ${cookies.get('token')}` }); -export const fiat = (amount, rate) => (amount * rate) / sats; -export const fd = async (req) => { +export let fiat = (amount, rate) => (amount * rate) / sats; +export let fd = async (req) => { let obj = 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 const f = (s, currency) => +export let f = (s, currency) => new Intl.NumberFormat('en-US', { style: 'currency', currency @@ -163,8 +163,8 @@ export const f = (s, currency) => .format(s) .replace('CA', ''); -export const s = (s) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(s); -export const sat = (s) => { +export let s = (s) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(s); +export let sat = (s) => { s = Math.abs(s); let p = Math.floor(Math.log(s) / Math.LN10 + 0.000000001); let d = Math.floor((p + 1) / 3); @@ -179,20 +179,20 @@ export const sat = (s) => { ); }; -export const sats = 100000000; +export let sats = 100000000; -export const back = () => browser && history.go(-1); -export const focus = (el) => browser && screen.width > 1280 && setTimeout(() => el.focus(), 1); +export let back = () => browser && history.go(-1); +export let focus = (el) => browser && screen.width > 1280 && setTimeout(() => el.focus(), 1); -export const sleep = (n) => new Promise((r) => setTimeout(r, n)); -export const wait = async (f, n = 100, s = 300) => { +export let sleep = (n) => new Promise((r) => setTimeout(r, n)); +export let wait = async (f, n = 100, s = 300) => { let i = 0; while (!(await f()) && i < s) (await sleep(n)) && i++; if (i >= s) throw new Error('timeout'); return f(); }; -export const stretch = async (password, salt) => +export let stretch = async (password, salt) => crypto.subtle.deriveKey( { name: 'PBKDF2', @@ -212,18 +212,18 @@ export const stretch = async (password, salt) => ['encrypt', 'decrypt'] ); -export const bip32 = await BIP32Factory(ecc); +export let bip32 = await BIP32Factory(ecc); -function get_bigrams(string) { +export let get_bigrams = (string) => { var s = string.toLowerCase(); var v = s.split(''); for (var i = 0; i < v.length; i++) { v[i] = s.slice(i, i + 2); } return v; -} +}; -export function ss(str1, str2) { +export let ss = (str1, str2) => { if (str1.length > 0 && str2.length > 0) { var pairs1 = get_bigrams(str1); var pairs2 = get_bigrams(str2); @@ -240,10 +240,26 @@ export function ss(str1, str2) { return 0.0; } -export const types = { +export let types = { bitcoin: 'bitcoin', lightning: 'lightning', internal: 'internal', pot: 'pot', classic: 'classic' }; + +export let fadeScale = (node, { delay = 0, duration = 200, easing = (x) => x, baseScale = 0 }) => { + let o = +getComputedStyle(node).opacity; + let m = getComputedStyle(node).transform.match(/scale\(([0-9.]+)\)/); + let s = m ? m[1] : 1; + let is = 1 - baseScale; + + return { + delay, + duration, + css: (t) => { + let eased = easing(t); + return `opacity: ${eased * o}; transform: scale(${eased * s * is + baseScale})`; + } + }; +}; diff --git a/src/routes/(app)/[username]/(profile)/+layout.svelte b/src/routes/(app)/[username]/(profile)/+layout.svelte index ec37867c..7b280b77 100644 --- a/src/routes/(app)/[username]/(profile)/+layout.svelte +++ b/src/routes/(app)/[username]/(profile)/+layout.svelte @@ -127,17 +127,6 @@ <a href={`/${username}/payments`} class="w-full"> <div class="relative"> <button class="mx-auto flex hover:opacity-80" class:text-black={paymentsTab}> - Feed - </button> - <div - class="absolute w-full border-b-4 border-black -bottom-3" - class:hidden={!paymentsTab} - /> - </div> - </a> - <a href={`/${username}/payments`} class="w-full"> - <div class="relative"> - <button class="mx-auto flex hover:opacity-80" class:text-black={paymentsTab}> Payments </button> <div diff --git a/src/routes/(app)/[username]/(profile)/messages/+page.server.js b/src/routes/(app)/[username]/(profile)/messages/+page.server.js index 7b651389..31ef799f 100644 --- a/src/routes/(app)/[username]/(profile)/messages/+page.server.js +++ b/src/routes/(app)/[username]/(profile)/messages/+page.server.js @@ -9,7 +9,7 @@ export async function load({ params, parent, url }) { try { messages = await get(`/${user.pubkey}/${since}/messages`); messages = messages - .filter((m) => m.recipient.id === subject.id || m.author.id === subject.id) + // .filter((m) => m.recipient.id === subject.id || m.author.id === subject.id) .sort((a, b) => a.created_at - b.created_at); } catch (e) { console.log(`failed to fetch nostr messages`, e); diff --git a/src/routes/(app)/[username]/(profile)/messages/+page.svelte b/src/routes/(app)/[username]/(profile)/messages/+page.svelte index 9ab95433..83f99d0f 100644 --- a/src/routes/(app)/[username]/(profile)/messages/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/messages/+page.svelte @@ -6,43 +6,51 @@ import { scale, fade, fly } from 'svelte/transition'; import { enhance } from '$app/forms'; import { Avatar, Icon } from '$comp'; - import { back, fail, focus } from '$lib/utils'; + import { back, fail, focus, fadeScale } 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 { calculateId } from 'nostr'; + import { page } from '$app/stores'; 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 latest = []; let { messages, subject, user } = data; let input, pane; + let i = 0; + let ready; + let keys = new Set(); + + $: update(data, $page); + let update = (data) => ({ messages, subject, user } = data); - $: initialize($password); + $: initialize($password, $page); let initialize = async (p) => { if (!p) return; + ready = false; + await Promise.all( messages.map(async (event) => (event.message = await decrypt({ event, user }))) ); - messages = messages; + messages = messages.filter((m) => m.recipient.id === subject.id || m.author.id === subject.id) + console.log("MESSAGES", messages.length) tick().then(() => pane && (pane.scrollTop = pane.scrollHeight)); + + while (i < messages.length) { + let event = messages[i]; + 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); + } + i++; + } + + latest = latest; + ready = true; }; e.subscribe(async (event) => { @@ -99,6 +107,21 @@ submit(); } }; + + e.subscribe(async (event) => { + if (!(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; + } + }); </script> <div class="container max-w-xl mx-auto px-4 space-y-5"> @@ -121,44 +144,45 @@ </a> {/each} </div> - {/if} - <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} + {:else} + <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 - }} - > + {#if message} <div - class="rounded-2xl px-4 py-2 max-w-[300px] mb-1 text-lg" - class:ours - class:theirs - :key={id} + class="flex gap-2" + class:flex-row-reverse={theirs} + class:justify-end={theirs} + in:fadeScale={{ + easing: cubicInOut, + baseScale: 0.5 + }} > - {message} + <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="mt-auto"> - <Avatar user={author} size={'12'} /> + <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> - </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> + {/if} + {/each} + </div> + {/if} <form method="POST" |
