Designate certain channels for certain uses

This commit is contained in:
Jonathan Staab 2022-11-25 22:26:04 -08:00
parent 1fda020029
commit acf7af1eff
8 changed files with 50 additions and 68 deletions

View File

@ -1,8 +1,7 @@
Bugs
- [ ] Make sure to unsubscribe from all stores when necessary
- [ ] Be sure to deduplicate all events if needed
- [ ] Format text with line breaks - pre, or split/br
- [ ] Reduce concurrent subscriptions
- [ ] Remove dexie, or use it instead of localstorage for cached data
- [ ] rename /user to /users
- [ ] Memoize room list, currently every time the user switches chat rooms it pulls the full list
@ -14,3 +13,9 @@ Features
- [ ] Followers
- [ ] Server discovery
- [ ] Favorite chat rooms
Nostr implementation comments
- [ ] It's impossible to get deletes for an event's replies/mentions in one query, since deletes can't tag anything other than what is to be deleted.
- [ ] Recursive queries are really painful, e.g. to get all notes for an account, you need to 1. get the account's notes, then get everything with those notes in their tags, then get deletions for those.
- [ ] The limit of 3 channels makes things difficult. I want to show a modal without losing all the state in the background. I am reserving one channel for one-off recursive queries.

View File

@ -1,13 +1,20 @@
<script>
import {onMount} from 'svelte'
import {findNotes} from "src/state/app"
import {channels} from "src/state/nostr"
import {user} from "src/state/user"
import Note from 'src/partials/Note.svelte'
export let note
onMount(() => {
return findNotes(
[{ids: [note.id]}, {'#e': [note.id]}],
channels.modal,
[{ids: [note.id]},
{'#e': [note.id]},
// We can't target deletes by e tag, so get them all so we can
// support toggling like/flags
{kinds: [5], authors: [$user.pubkey]}],
$notes => {
note = $notes[0] || note
}

View File

@ -44,7 +44,7 @@
toast.show("info", `Your room has been ${values.id ? 'updated' : 'created'}!`)
navigate(`/chat/${room}`)
navigate(`/chat/${room || event.id}`)
}
}
</script>

View File

@ -5,7 +5,7 @@
import {prop, last} from 'ramda'
import {switcherFn} from 'hurdak/src/core'
import UserBadge from 'src/partials/UserBadge.svelte'
import {nostr} from 'src/state/nostr'
import {channels} from 'src/state/nostr'
import {rooms, accounts, ensureAccount} from 'src/state/app'
import {dispatch} from 'src/state/dispatch'
import {user} from 'src/state/user'
@ -46,7 +46,7 @@
return top + height < bodyRect.height
}
const sub = nostr.sub({
channels.main.sub({
filter: {kinds: [42, 43, 44], '#e': [room]},
cb: e => {
switcherFn(e.kind, {
@ -70,8 +70,6 @@
})
},
})
return () => sub.unsub()
})
const edit = () => {

View File

@ -7,9 +7,9 @@
import {timedelta, now, formatTimestamp} from 'src/util/misc'
import Anchor from "src/partials/Anchor.svelte"
import Note from "src/partials/Note.svelte"
import {nostr, relays} from "src/state/nostr"
import {channels, relays} from "src/state/nostr"
import {user} from "src/state/user"
import {findNotes, modal} from "src/state/app"
import {findNotes} from "src/state/app"
import {db} from "src/state/db"
let notes
@ -19,14 +19,10 @@
}
onMount(() => {
return findNotes({
return findNotes(channels.main, {
since: new Date().valueOf() / 1000 - 7 * 24 * 60 * 60,
}, $notes => {
notes = $notes
// if ($modal?.note) {
// modal.set({note: find(propEq('id', $modal.note.id), $notes)})
// }
})
})
</script>

View File

@ -6,29 +6,30 @@
import {ellipsize} from 'hurdak/src/core'
import {formatTimestamp} from 'src/util/misc'
import Note from "src/partials/Note.svelte"
import {nostr} from 'src/state/nostr'
import {channels} from 'src/state/nostr'
import {user as currentUser} from 'src/state/user'
import {accounts, ensureAccount} from "src/state/app"
import {accounts, ensureAccount, findNotes} from "src/state/app"
export let pubkey
let user
let notes = []
let notes
$: user = $accounts[pubkey]
onMount(async () => {
await ensureAccount(pubkey)
const sub = nostr.sub({
filter: {authors: [pubkey], kinds: [1]},
cb: e => {
notes = uniqBy(prop('id'), notes.concat(e))
},
return findNotes(channels.main, {
authors: [pubkey],
}, $notes => {
notes = $notes
})
return () => sub.unsub()
})
$: {
console.log(notes)
}
</script>
{#if user}
@ -53,8 +54,8 @@
</div>
<div class="h-px bg-medium" in:fly={{y: 20, delay: 200}} />
<div class="flex flex-col gap-4" in:fly={{y: 20, delay: 400}}>
{#each reverse(notes) as note}
<Note interactive note={note} />
{#each reverse(notes || []) as n (n.id)}
<Note interactive note={n} />
{/each}
</div>
</div>

View File

@ -1,9 +1,9 @@
import {prop, find, last, groupBy} from 'ramda'
import {prop, uniqBy, find, last, groupBy} from 'ramda'
import {writable, derived, get} from 'svelte/store'
import {switcherFn, ensurePlural} from 'hurdak/lib/hurdak'
import {getLocalJson, setLocalJson, now, timedelta} from "src/util/misc"
import {user} from 'src/state/user'
import {nostr} from 'src/state/nostr'
import {channels} from 'src/state/nostr'
export const modal = writable(null)
@ -31,7 +31,7 @@ export const ensureAccount = pubkey => {
let $account = prop(pubkey, get(accounts))
if (!$account || $account.lastRefreshed < now() - timedelta(10, 'minutes')) {
const accountSub = nostr.sub({
channels.getter.sub({
filter: {kinds: [0], authors: [pubkey]},
cb: e => {
$account = {
@ -44,23 +44,19 @@ export const ensureAccount = pubkey => {
accounts.update($accounts => ({...$accounts, [pubkey]: $account}))
},
})
setTimeout(() => {
accountSub.unsub()
}, 1000)
}
}
export const findNotes = (queries, cb) => {
export const findNotes = (channel, queries, cb) => {
const notes = writable([])
const reactions = writable([])
const sub = nostr.sub({
channel.sub({
filter: ensurePlural(queries).map(q => ({kinds: [1, 5, 7], ...q})),
cb: async e => {
switcherFn(e.kind, {
1: () => {
notes.update($xs => $xs.concat(e))
notes.update($xs => uniqBy(prop('id'), $xs.concat(e)))
ensureAccount(e.pubkey)
},
@ -105,10 +101,5 @@ export const findNotes = (queries, cb) => {
}
)
const unsubscribe = annotatedNotes.subscribe(cb)
return () => {
sub.unsub()
unsubscribe()
}
return annotatedNotes.subscribe(cb)
}

View File

@ -1,9 +1,16 @@
import {writable} from 'svelte/store'
import {relayPool, getPublicKey} from 'nostr-tools'
import {noop} from 'hurdak/lib/hurdak'
import {getLocalJson, setLocalJson} from "src/util/misc"
export const nostr = relayPool()
export const channels = {
main: nostr.sub({filter: {}, cb: noop}),
modal: nostr.sub({filter: {}, cb: noop}),
getter: nostr.sub({filter: {}, cb: noop}),
}
// Augment nostr with some extra methods
nostr.login = privkey => {
@ -18,41 +25,18 @@ nostr.event = (kind, content = '', tags = []) => {
return {kind, content, tags, pubkey, created_at: createdAt}
}
nostr.find = (filter, timeout = 300) => {
return new Promise(resolve => {
const sub = nostr.sub({
filter,
cb: e => {
resolve(e)
sub.unsub()
},
})
setTimeout(() => {
resolve(null)
sub.unsub()
}, timeout)
})
}
nostr.findLast = (filter, timeout = 300) => {
return new Promise(resolve => {
let result = null
const sub = nostr.sub({
channels.getter.sub({
filter,
cb: e => {
result = e
},
})
setTimeout(() => {
resolve(result)
sub.unsub()
}, timeout)
setTimeout(() => resolve(result), timeout)
})
}