Get everything working again

This commit is contained in:
Jonathan Staab 2022-11-30 10:26:58 -08:00
parent 46062a721e
commit ee04e2987a
10 changed files with 87 additions and 51 deletions

View File

@ -1,7 +1,6 @@
Bugs
- [ ] Make sure to stop loading chunks on component unmount
- [ ] Don't lose scroll position when opening modals
- [ ] Load/publish user preferred relays
- [ ] Optimistically load events the user publishes (e.g. to reduce reflow for reactions/replies). Essentially, we can pretend to be our own in-memory relay.
Features

View File

@ -111,6 +111,12 @@
Reply to <Anchor on:click={showParent}>{parentId.slice(0, 8)}</Anchor>
</small>
{/if}
{#if flag}
<p class="text-light border-l-2 border-solid border-medium pl-4">
You have flagged this content as offensive.
<Anchor on:click={() => deleteReaction(flag)}>Unflag</Anchor>
</p>
{:else}
<p>
{#if note.content.length > 240 && !showEntire}
{ellipsize(note.content, 240)}
@ -131,13 +137,12 @@
on:click={() => like ? deleteReaction(like) : react("+")} />
{uniqBy(prop('pubkey'), note.reactions.filter(whereEq({content: '+'}))).length}
</div>
<div class={cx({'text-accent': flag})}>
<i
class="fa-solid fa-flag cursor-pointer"
on:click={() => flag ? deleteReaction(flag) : react("-")} />
<div>
<i class="fa-solid fa-flag cursor-pointer" on:click={() => react("-")} />
{uniqBy(prop('pubkey'), note.reactions.filter(whereEq({content: '-'}))).length}
</div>
</div>
{/if}
</div>
</div>

View File

@ -1,5 +1,5 @@
<script>
import {onMount, onDestroy} from 'svelte'
import {onMount} from 'svelte'
import {writable} from 'svelte/store'
import {find, propEq} from 'ramda'
import {notesLoader, notesListener, modal} from "src/state/app"

View File

@ -11,8 +11,12 @@
let q = ""
let rooms = {}
let search
let nOtherRooms
$: search = fuzzy(Object.values(rooms), {keys: ["name", "about"]})
$: {
search = fuzzy(Object.values(rooms), {keys: ["name", "about"]})
nOtherRooms = Math.floor(0, Object.keys(rooms).length - 8)
}
const createRoom = () => navigate(`/chat/new`)
@ -60,9 +64,11 @@
{/if}
</li>
{/each}
{#if nOtherRooms > 1}
<li class="px-3">
<small>{Math.floor(0, Object.keys(rooms).length - 8)} more rooms found</small>
<small>Enter a search term to discover {nOtherRooms} more rooms.</small>
</li>
{/if}
<li class="bg-medium m-3 h-px" />
<li class="cursor-pointer font-bold hover:bg-accent transition-all px-3 py-2" on:click={createRoom}>
<i class="fa-solid fa-plus" /> Create Room

View File

@ -87,6 +87,8 @@
</div>
</form>
</div>
<RoomList />
<div class="hidden sm:block">
<RoomList />
</div>
</div>

View File

@ -1,5 +1,5 @@
<script>
import {onMount} from 'svelte'
import {onMount, onDestroy} from 'svelte'
import {fly} from 'svelte/transition'
import {navigate} from 'svelte-routing'
import {prop, uniqBy, sortBy, last} from 'ramda'
@ -7,7 +7,7 @@
import {formatTimestamp} from 'src/util/misc'
import {toHtml} from 'src/util/html'
import UserBadge from 'src/partials/UserBadge.svelte'
import {channels} from 'src/state/nostr'
import {Listener, epoch} from 'src/state/nostr'
import {accounts, ensureAccounts} from 'src/state/app'
import {dispatch} from 'src/state/dispatch'
import {user} from 'src/state/user'
@ -15,6 +15,7 @@
export let room
let listener
let textarea
let messages = []
let annotatedMessages = []
@ -46,45 +47,52 @@
return navigate('/login')
}
const events = await channels.getter.all({kinds: [40, 41], ids: [room]})
events.forEach(({pubkey, content}) => {
roomData = {pubkey, ...roomData, ...JSON.parse(content)}
})
const isVisible = $el => {
const bodyRect = document.body.getBoundingClientRect()
const {top, height} = $el.getBoundingClientRect()
return top + height < bodyRect.height
}
return await channels.listener.sub(
{limit: 100, kinds: [42, 43, 44], '#e': [room]},
listener = new Listener(
[{kinds: [40, 41], ids: [room], since: epoch},
{kinds: [42, 43, 44], '#e': [room], since: epoch}],
e => {
switcherFn(e.kind, {
42: () => {
messages = messages.concat(e)
const {pubkey, kind, content} = e
ensureAccounts([e.pubkey])
if ([40, 41].includes(kind)) {
roomData = {pubkey, ...roomData, ...JSON.parse(content)}
} else {
switcherFn(kind, {
42: () => {
messages = messages.concat(e)
const $prevListItem = last(document.querySelectorAll('.chat-message'))
ensureAccounts([pubkey])
if ($prevListItem && isVisible($prevListItem)) {
setTimeout(() => {
const $li = last(document.querySelectorAll('.chat-message'))
const $prevListItem = last(document.querySelectorAll('.chat-message'))
$li.scrollIntoView({behavior: "smooth"})
}, 100)
}
},
43: () => null,
44: () => null,
})
},
if ($prevListItem && isVisible($prevListItem)) {
setTimeout(() => {
const $li = last(document.querySelectorAll('.chat-message'))
$li.scrollIntoView({behavior: "smooth"})
}, 100)
}
},
43: () => null,
44: () => null,
})
}
}
)
listener.start()
})
onDestroy(() => {
listener?.stop()
})
const isVisible = $el => {
const bodyRect = document.body.getBoundingClientRect()
const {top, height} = $el.getBoundingClientRect()
return top + height < bodyRect.height
}
const edit = () => {
navigate(`/chat/${room}/edit`)
}

View File

@ -37,7 +37,7 @@
<div class="flex flex-col gap-8 w-full">
<div class="flex flex-col gap-1">
<strong>Public Key</strong>
<Input disabled value={$user.pubkey}>
<Input disabled value={$user?.pubkey}>
<i slot="after" class="fa-solid fa-copy cursor-pointer" on:click={() => copyKey('public')} />
</Input>
<p class="text-sm text-light">
@ -47,7 +47,7 @@
</div>
<div class="flex flex-col gap-1">
<strong>Private Key</strong>
<Input disabled type="password" value={$user.privkey}>
<Input disabled type="password" value={$user?.privkey}>
<i slot="after" class="fa-solid fa-copy cursor-pointer" on:click={() => copyKey('private')} />
</Input>
<p class="text-sm text-light">

View File

@ -5,7 +5,7 @@ import {navigate} from "svelte-routing"
import {switcherFn, ensurePlural} from 'hurdak/lib/hurdak'
import {getLocalJson, setLocalJson, now, timedelta, sleep} from "src/util/misc"
import {user} from 'src/state/user'
import {filterMatches, Listener, Cursor, channels, relays, findReplyTo} from 'src/state/nostr'
import {epoch, filterMatches, Listener, Cursor, channels, relays, findReplyTo} from 'src/state/nostr'
export const modal = writable(null)
@ -123,7 +123,7 @@ export const notesLoader = async (
showParents = false,
delta = timedelta(1, 'hours'),
isInModal = false,
since = 1633046400, // nostr epoch
since = epoch,
} = {}
) => {
const cursor = new Cursor(filter, delta)

View File

@ -1,4 +1,4 @@
import {writable} from 'svelte/store'
import {writable, get} from 'svelte/store'
import {relayPool, getPublicKey} from 'nostr-tools'
import {last, intersection, uniqBy, prop} from 'ramda'
import {first, noop, ensurePlural} from 'hurdak/lib/hurdak'
@ -6,6 +6,8 @@ import {getLocalJson, setLocalJson, now, timedelta} from "src/util/misc"
export const nostr = relayPool()
export const epoch = 1633046400
export const filterTags = (where, events) =>
ensurePlural(events)
.flatMap(
@ -55,6 +57,15 @@ export class Channel {
// before they can get a new one.
await this.p
// If we don't have any relays, we'll wait forever for an eose, but
// we already know we're done. Use a timeout since callers are
// expecting this to be async and we run into errors otherwise.
if (get(relays).length === 0) {
setTimeout(onEose)
return {unsub: noop}
}
let resolve
const sub = nostr.sub({filter, cb}, this.name, onEose)
@ -160,10 +171,15 @@ export class Listener {
this.p = Promise.resolve()
}
async start() {
const {filter, since} = this
if (!this.sub) {
this.sub = await channels.listener.sub(
this.filter.map(f => ({...f, since: this.since})),
e => this.onEvent(e)
filter.map(f => ({since, ...f})),
e => {
this.since = e.created_at
this.onEvent(e)
}
)
}
}
@ -171,7 +187,6 @@ export class Listener {
if (this.sub) {
this.sub.unsub()
this.sub = null
this.since = now()
}
}
restart() {

View File

@ -14,6 +14,7 @@ module.exports = {
light: "#CCC5B9",
medium: "#403D39",
dark: "#252422",
danger: "#ff0000",
},
},
plugins: [],