Re-work streamContext

This commit is contained in:
Jonathan Staab 2023-04-05 10:40:23 -05:00
parent 6722131c8f
commit ab1c2cb210
3 changed files with 94 additions and 70 deletions

View File

@ -129,35 +129,74 @@ const loadParents = (notes, opts = {}) => {
}) })
} }
const streamContext = ({notes, onChunk, depth = 0}) => const streamContext = ({notes, onChunk, maxDepth = 2}) => {
// Some relays reject very large filters, send multiple subscriptions const subs = []
Promise.all( const seen = new Set()
chunk(256, notes).map(async events => { const relays = sampleRelays(aggregateScores(notes.map(getRelaysForEventChildren)))
// Instead of recurring to depth, trampoline so we can batch requests
while (events.length > 0 && depth > 0) {
const chunk = events.splice(0)
const authors = getStalePubkeys(pluck("pubkey", chunk))
const filter = [{kinds: [1, 7, 9735], "#e": pluck("id", chunk)}] as Array<object>
const relays = sampleRelays(aggregateScores(chunk.map(getRelaysForEventChildren)))
// Load authors and reactions in one subscription const loadChunk = (events, depth) => {
if (authors.length > 0) { // Remove anything from the chunk we've already seen
filter.push({kinds: personKinds, authors}) events = events.filter(e => e.kind === 1 && !seen.has(e.id))
}
depth -= 1 // If we have no new information, no need to re-subscribe
if (events.length === 0) {
return
}
const promise = load({relays, filter, onChunk}) // Add our new events to the list of stuff we've seen
events.forEach(e => seen.add(e.id))
// Don't await the promise when we're on the last level, since we won't be // Unsubscribe our current listeners since we're about to replace them
// displaying those replies, and we await `load` before showing children subs.map(sub => sub.then(s => s.unsub()))
// to reduce reflow
if (depth > 0) { // Add a subscription for each chunk to listen for new likes/replies/zaps
events = await promise chunk(256, Array.from(seen)).forEach(ids => {
} subs.push(
} listen({
relays,
filter: [{kinds: [1, 7, 9735], "#e": ids, since: now()}],
onChunk: newEvents => {
onChunk(newEvents)
if (depth < maxDepth) {
loadChunk(newEvents, depth + 1)
}
},
})
)
}) })
)
const newIds = pluck("id", events)
const pubkeys = pluck("pubkey", events)
// Load any people we should know about
loadPeople(pubkeys)
// Load data prior to now for our new ids
chunk(256, newIds).forEach(ids => {
load({
relays,
filter: [{kinds: [1, 7, 9735], "#e": ids}],
onChunk: newEvents => {
onChunk(newEvents)
if (depth < maxDepth) {
loadChunk(newEvents, depth + 1)
}
},
})
})
}
// Kick things off by loading our first chunk
loadChunk(notes, 1)
return {
unsub: () => {
subs.map(sub => sub.then(s => s.unsub()))
},
}
}
const applyContext = (notes, context) => { const applyContext = (notes, context) => {
context = context.map(assoc("isContext", true)) context = context.map(assoc("isContext", true))

View File

@ -66,7 +66,7 @@
// Stream in additional data and merge it in // Stream in additional data and merge it in
network.streamContext({ network.streamContext({
depth: 2, maxDepth: 2,
notes: combined.filter(propEq("kind", 1)), notes: combined.filter(propEq("kind", 1)),
onChunk: context => { onChunk: context => {
context = user.applyMutes(context) context = user.applyMutes(context)

View File

@ -1,12 +1,14 @@
<script> <script>
import {onMount} from "svelte" import {onMount, onDestroy} from "svelte"
import {pluck, propEq} from "ramda" import {nip19} from "nostr-tools"
import {fly} from "svelte/transition" import {fly} from "svelte/transition"
import {first} from "hurdak/lib/hurdak" import {first} from "hurdak/lib/hurdak"
import {log} from "src/util/logger"
import {asDisplayEvent} from "src/util/nostr" import {asDisplayEvent} from "src/util/nostr"
import Content from "src/partials/Content.svelte" import Content from "src/partials/Content.svelte"
import Spinner from "src/partials/Spinner.svelte" import Spinner from "src/partials/Spinner.svelte"
import Note from "src/views/notes/Note.svelte" import Note from "src/views/notes/Note.svelte"
import user from "src/agent/user"
import network from "src/agent/network" import network from "src/agent/network"
import {sampleRelays} from "src/agent/relays" import {sampleRelays} from "src/agent/relays"
@ -14,58 +16,41 @@
export let relays = [] export let relays = []
export let invertColors = false export let invertColors = false
let found = false let sub = null
let loading = true let loading = true
let seen = new Set()
onMount(() => { onMount(async () => {
const sub = network.listen({ if (!note.pubkey) {
relays: sampleRelays(relays), await network.load({
filter: [ relays: sampleRelays(relays),
{kinds: [1], ids: [note.id]}, filter: {kinds: [1], ids: [note.id]},
{kinds: [1, 7, 9735], "#e": [note.id]}, onChunk: events => {
], note = first(events)
onChunk: chunk => { },
const children = chunk.filter(propEq("kind", 1)) })
}
// Recursively bring in context for children, since reactions etc don't if (note) {
// contain the full chain of ancestors log("NoteDetail", nip19.noteEncode(note.id), note)
network.streamContext({
depth: 5,
notes: children.filter(e => !seen.has(e.id)),
onChunk: childChunk => {
note = first(network.applyContext([note], childChunk))
},
})
// Keep track of the children we've seen, update latest version of our note sub = network.streamContext({
children.forEach(event => { maxDepth: 6,
if (event.id === note.id) { notes: [note],
found = true onChunk: context => {
loading = false note = first(network.applyContext([note], user.applyMutes(context)))
note = {...note, ...event} },
} })
}
seen.add(event.id) loading = false
}) })
// Load authors onDestroy(() => {
network.loadPeople(pluck("pubkey", children)) sub?.unsub()
// Apply context
note = first(network.applyContext([note], chunk))
},
})
setTimeout(() => {
loading = false
}, 3000)
return () => sub.then(s => s.unsub())
}) })
</script> </script>
{#if !loading && !found} {#if !loading && !note.content}
<div in:fly={{y: 20}}> <div in:fly={{y: 20}}>
<Content size="lg" class="text-center">Sorry, we weren't able to find this note.</Content> <Content size="lg" class="text-center">Sorry, we weren't able to find this note.</Content>
</div> </div>