aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2023-03-04 23:17:37 -0800
committerAdam Soltys <[email protected]>2023-03-04 23:17:37 -0800
commite6707b3ef14730e25a90b055c45eae8bd729f9ea (patch)
tree0e024a96b5214b7b40466ad2b250d578dd6c80de /src
parent6c31cea7d17856a0d0d2f6cb71e3e4f6c0e24d74 (diff)
downloadcoinos-ui-layout-updates.tar.gz
coinos-ui-layout-updates.zip
experimentinglayout-updates
Diffstat (limited to 'src')
-rw-r--r--src/lib/utils.js80
-rw-r--r--src/routes/(app)/[username]/(profile)/+layout.svelte11
-rw-r--r--src/routes/(app)/[username]/(profile)/messages/+page.server.js2
-rw-r--r--src/routes/(app)/[username]/(profile)/messages/+page.svelte128
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"