mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-29 08:21:20 +00:00
Use feed for note detail
This commit is contained in:
parent
3d6730793b
commit
34fc5972f6
@ -1,9 +1,7 @@
|
||||
# Current
|
||||
|
||||
- [ ] Refactor
|
||||
- [ ] Remove load, use subscribe with autoClose. Add subscription class and put handlers on that.
|
||||
- [ ] Cancel load requests as well as subscribes when navigating to save bandwidth. Get rid of load?
|
||||
- [ ] Abort context load requests when the next page is requested?
|
||||
- [ ] Add thread view
|
||||
- [ ] Use limit 1 to find most recent notification, then load when visiting the notifications page.
|
||||
- [ ] Remove external dependencies from engine, open source it?
|
||||
- [ ] If connections fail, re-open and re-send active subs
|
||||
@ -43,7 +41,6 @@
|
||||
# Core
|
||||
|
||||
- [ ] Private groups
|
||||
- [ ] Preload quotes
|
||||
- [ ] Add custom emoji support
|
||||
- [ ] Reminders for max time spent on coracle
|
||||
- [ ] Proxy handle requests for CORS
|
||||
|
@ -38,6 +38,24 @@ const engine = createDefaultEngine({
|
||||
})
|
||||
|
||||
export default engine
|
||||
export const Alerts = engine.Alerts
|
||||
export const Builder = engine.Builder
|
||||
export const Content = engine.Content
|
||||
export const Directory = engine.Directory
|
||||
export const Events = engine.Events
|
||||
export const Keys = engine.Keys
|
||||
export const Meta = engine.Meta
|
||||
export const Network = engine.Network
|
||||
export const Nip02 = engine.Nip02
|
||||
export const Nip04 = engine.Nip04
|
||||
export const Nip05 = engine.Nip05
|
||||
export const Nip28 = engine.Nip28
|
||||
export const Nip57 = engine.Nip57
|
||||
export const Nip65 = engine.Nip65
|
||||
export const Outbox = engine.Outbox
|
||||
export const PubkeyLoader = engine.PubkeyLoader
|
||||
export const Storage = engine.Storage
|
||||
export const User = engine.User
|
||||
export const alerts = engine.Alerts
|
||||
export const builder = engine.Builder
|
||||
export const content = engine.Content
|
||||
|
@ -85,6 +85,7 @@
|
||||
depth: 2,
|
||||
relays: getRelays(),
|
||||
filter: compileFilter(filter),
|
||||
shouldLoadParents: true,
|
||||
})
|
||||
|
||||
feed.start()
|
||||
|
@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type {Event} from "src/engine/types"
|
||||
import {onDestroy} from "svelte"
|
||||
import {fly} from "src/util/transition"
|
||||
import {warn} from "src/util/logger"
|
||||
import {modal} from "src/partials/state"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Card from "src/partials/Card.svelte"
|
||||
@ -12,31 +11,24 @@
|
||||
export let note
|
||||
export let value
|
||||
|
||||
let quote = null
|
||||
let muted = false
|
||||
let loading = true
|
||||
|
||||
const openPerson = pubkey => modal.push({type: "person/feed", pubkey})
|
||||
|
||||
const loadQuote = () => {
|
||||
const {id, relays = []} = value
|
||||
const {id, relays = []} = value
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
await network.load({
|
||||
relays: nip65.mergeHints(3, [relays, nip65.getEventHints(3, note)]),
|
||||
filter: [{ids: [id]}],
|
||||
onEvent: event => {
|
||||
muted = user.applyMutes([event]).length === 0
|
||||
|
||||
resolve(event)
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
warn(e)
|
||||
}
|
||||
|
||||
reject()
|
||||
}) as Promise<Event>
|
||||
}
|
||||
const sub = network.subscribe({
|
||||
timeout: 5000,
|
||||
relays: nip65.mergeHints(3, [relays, nip65.getEventHints(3, note)]),
|
||||
filter: [{ids: [id]}],
|
||||
onEvent: event => {
|
||||
loading = false
|
||||
muted = user.applyMutes([event]).length === 0
|
||||
quote = event
|
||||
},
|
||||
})
|
||||
|
||||
const openQuote = e => {
|
||||
// stopPropagation wasn't working for some reason
|
||||
@ -48,15 +40,19 @@
|
||||
const unmute = e => {
|
||||
muted = false
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
sub.close()
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="py-2">
|
||||
<Card interactive invertColors class="my-2" on:click={openQuote}>
|
||||
{#await loadQuote()}
|
||||
{#if loading}
|
||||
<div class="px-20">
|
||||
<Spinner />
|
||||
</div>
|
||||
{:then quote}
|
||||
{:else if quote}
|
||||
{#if muted}
|
||||
<p class="mb-1 py-24 text-center text-gray-5" in:fly={{y: 20}}>
|
||||
You have muted this note.
|
||||
@ -75,10 +71,10 @@
|
||||
</div>
|
||||
<slot name="note-content" {quote} />
|
||||
{/if}
|
||||
{:catch}
|
||||
{:else}
|
||||
<p class="mb-1 py-24 text-center text-gray-5" in:fly={{y: 20}}>
|
||||
Unable to load a preview for quoted event
|
||||
</p>
|
||||
{/await}
|
||||
{/if}
|
||||
</Card>
|
||||
</div>
|
||||
|
@ -12,12 +12,11 @@
|
||||
|
||||
let pubkeys = []
|
||||
|
||||
onMount(async () => {
|
||||
onMount(() => {
|
||||
if (type === "follows") {
|
||||
pubkeys = nip02.getFollows(pubkey)
|
||||
} else {
|
||||
await network.load({
|
||||
shouldProcess: false,
|
||||
const sub = network.subscribe({
|
||||
relays: nip65.getPubkeyHints(user.getSetting("relay_limit"), pubkey, "read"),
|
||||
filter: {kinds: [3], "#p": [pubkey]},
|
||||
onEvent: batch(500, events => {
|
||||
@ -28,6 +27,8 @@
|
||||
pubkeys = uniq(pubkeys.concat(newPubkeys))
|
||||
}),
|
||||
})
|
||||
|
||||
return sub.close
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {onDestroy} from "svelte"
|
||||
import {fly} from "src/util/transition"
|
||||
import {tweened} from "svelte/motion"
|
||||
import {numberFmt, batch} from "src/util/misc"
|
||||
@ -11,17 +11,17 @@
|
||||
const followsCount = nip02.graph.key(pubkey).derived(() => nip02.getFollowsSet(pubkey).size)
|
||||
const interpolate = (a, b) => t => a + Math.round((b - a) * t)
|
||||
|
||||
let sub
|
||||
let canLoadFollowers = true
|
||||
let followersCount = tweened(0, {interpolate, duration: 1000})
|
||||
|
||||
const showFollows = () => {
|
||||
modal.push({type: "person/follows", pubkey})
|
||||
}
|
||||
const showFollows = () => modal.push({type: "person/follows", pubkey})
|
||||
|
||||
const showFollowers = () => {
|
||||
modal.push({type: "person/followers", pubkey})
|
||||
}
|
||||
const showFollowers = () => modal.push({type: "person/followers", pubkey})
|
||||
|
||||
const loadFollowers = async () => {
|
||||
canLoadFollowers = false
|
||||
|
||||
onMount(async () => {
|
||||
// Get our followers count
|
||||
const count = await network.count({kinds: [3], "#p": [pubkey]})
|
||||
|
||||
@ -30,9 +30,10 @@
|
||||
} else {
|
||||
const followers = new Set()
|
||||
|
||||
await network.load({
|
||||
relays: nip65.getPubkeyHints(3, user.getPubkey(), "read"),
|
||||
sub = network.subscribe({
|
||||
timeout: 30_000,
|
||||
shouldProcess: false,
|
||||
relays: nip65.getPubkeyHints(3, user.getPubkey(), "read"),
|
||||
filter: [{kinds: [3], "#p": [pubkey]}],
|
||||
onEvent: batch(300, events => {
|
||||
for (const e of events) {
|
||||
@ -43,6 +44,10 @@
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
sub?.close()
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -51,6 +56,15 @@
|
||||
<strong>{$followsCount}</strong> following
|
||||
</button>
|
||||
<button on:click={showFollowers}>
|
||||
<strong>{numberFmt.format($followersCount)}</strong> followers
|
||||
<strong>
|
||||
{#if canLoadFollowers}
|
||||
<i class="fa fa-download mr-1" on:click|stopPropagation={loadFollowers} />
|
||||
{:else if $followersCount === 0}
|
||||
<i class="fa fa-circle-notch fa-spin mr-1" />
|
||||
{:else}
|
||||
{numberFmt.format($followersCount)}
|
||||
{/if}
|
||||
</strong>
|
||||
followers
|
||||
</button>
|
||||
</div>
|
||||
|
@ -32,7 +32,7 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
network.load({
|
||||
const sub = network.subscribe({
|
||||
relays: nip65.getPubkeyHints(3, user.getPubkey(), "read"),
|
||||
filter: {
|
||||
limit: 1000,
|
||||
@ -44,6 +44,8 @@
|
||||
reviews = reviews.concat(event)
|
||||
},
|
||||
})
|
||||
|
||||
return sub.close
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -114,9 +114,9 @@ export const listen = async () => {
|
||||
;(listen as any)._listener = await network.subscribe({
|
||||
relays: user.getRelayUrls("read"),
|
||||
filter: [
|
||||
{kinds: noteKinds.concat(4), authors: [pubkey], since},
|
||||
{kinds, "#p": [pubkey], since},
|
||||
{kinds, "#e": eventIds, since},
|
||||
{kinds: noteKinds.concat(4), authors: [pubkey], since, limit: 1},
|
||||
{kinds, "#p": [pubkey], since, limit: 1},
|
||||
{kinds, "#e": eventIds, since, limit: 1},
|
||||
{kinds: [42], "#e": channelIds, since},
|
||||
],
|
||||
onEvent: batch(3000, events => {
|
||||
|
@ -5,7 +5,6 @@
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import NoteContent from "src/app/shared/NoteContent.svelte"
|
||||
import Spinner from "src/partials/Spinner.svelte"
|
||||
import {directory, nip65, network} from "src/app/engine"
|
||||
|
||||
export let identifier
|
||||
@ -14,12 +13,12 @@
|
||||
export let relays = []
|
||||
|
||||
let note
|
||||
let loading = true
|
||||
|
||||
const display = directory.displayPubkey(pubkey)
|
||||
|
||||
onMount(async () => {
|
||||
await network.load({
|
||||
const sub = network.subscribe({
|
||||
timeout: 30_000,
|
||||
relays: nip65.selectHints(3, relays),
|
||||
filter: {kinds: [kind], pubkey, "#d": [identifier]},
|
||||
onEvent: event => {
|
||||
@ -27,7 +26,7 @@
|
||||
},
|
||||
})
|
||||
|
||||
loading = false
|
||||
return sub.close
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -56,7 +55,5 @@
|
||||
{/if}
|
||||
{/each}
|
||||
</ul>
|
||||
{:else if loading}
|
||||
<Spinner />
|
||||
{/if}
|
||||
</Content>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import {propEq} from "ramda"
|
||||
import {propEq, find} from "ramda"
|
||||
import {onMount, onDestroy} from "svelte"
|
||||
import {fly} from "src/util/transition"
|
||||
import {isMobile} from "src/util/html"
|
||||
@ -15,8 +15,8 @@
|
||||
export let relays = []
|
||||
export let invertColors = false
|
||||
|
||||
console.log(nip65.selectHints(3, relays))
|
||||
const feed = network.feed({
|
||||
limit: 1,
|
||||
depth: 6,
|
||||
relays: nip65.selectHints(3, relays),
|
||||
filter: {ids: [note.id]},
|
||||
@ -31,17 +31,21 @@
|
||||
let loading = true
|
||||
let feedRelay = null
|
||||
let displayNote = feed.feed.derived($feed => {
|
||||
console.log($feed)
|
||||
const found = find(propEq("id", note.id), $feed)
|
||||
|
||||
//if (found) {
|
||||
loading = false
|
||||
//}
|
||||
if (found) {
|
||||
loading = false
|
||||
}
|
||||
|
||||
return asDisplayEvent(found || note)
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
// If our note came from a feed, we can load faster
|
||||
if (note.replies) {
|
||||
feed.hydrate([note])
|
||||
}
|
||||
|
||||
feed.start()
|
||||
feed.loadAll()
|
||||
})
|
||||
|
@ -14,7 +14,7 @@
|
||||
import Tabs from "src/partials/Tabs.svelte"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Notification from "src/app/views/Notification.svelte"
|
||||
import engine, {user, alerts} from "src/app/engine"
|
||||
import {Events, user, alerts} from "src/app/engine"
|
||||
|
||||
const {lastChecked} = alerts
|
||||
const tabs = ["Mentions & Replies", "Reactions"]
|
||||
@ -39,7 +39,7 @@
|
||||
({notifications}) => -notifications.reduce((a, b) => Math.max(a, b.created_at), 0),
|
||||
$notifications
|
||||
.slice(0, limit)
|
||||
.map(e => [e, engine.events.cache.key(findReplyId(e)).get()])
|
||||
.map(e => [e, Events.cache.key(findReplyId(e)).get()])
|
||||
.filter(([e, ref]) => {
|
||||
if (ref && !noteKinds.includes(ref.kind)) {
|
||||
return false
|
||||
|
@ -24,14 +24,16 @@
|
||||
// If we have a query, search using nostr.band. If not, ask for random profiles.
|
||||
// This allows us to populate results even if search isn't supported by forced urls
|
||||
if (q.length > 2) {
|
||||
network.load({
|
||||
network.subscribe({
|
||||
relays: nip65.getSearchRelays(),
|
||||
filter: [{kinds: [0], search, limit: 10}],
|
||||
timeout: 3000,
|
||||
})
|
||||
} else if (directory.profiles.get().length < 50) {
|
||||
network.load({
|
||||
network.subscribe({
|
||||
relays: user.getRelayUrls("read"),
|
||||
filter: [{kinds: [0], limit: 50}],
|
||||
timeout: 3000,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -7,6 +7,7 @@ import {warn, error, log} from "src/util/logger"
|
||||
import {normalizeRelayUrl} from "src/util/nostr"
|
||||
import type {Event, Filter} from "src/engine/types"
|
||||
import {Cursor, MultiCursor} from "src/engine/util/Cursor"
|
||||
import {Subscription} from "src/engine/util/Subscription"
|
||||
import {Feed} from "src/engine/util/Feed"
|
||||
|
||||
export type SubscribeOpts = {
|
||||
@ -14,7 +15,7 @@ export type SubscribeOpts = {
|
||||
filter: Filter[] | Filter
|
||||
onEvent?: (event: Event) => void
|
||||
onEose?: (url: string) => void
|
||||
autoClose?: boolean
|
||||
timeout?: number
|
||||
shouldProcess?: boolean
|
||||
}
|
||||
|
||||
@ -183,14 +184,15 @@ export class Network {
|
||||
const subscribe = ({
|
||||
relays,
|
||||
filter,
|
||||
onEvent,
|
||||
onEose,
|
||||
autoClose,
|
||||
onEvent,
|
||||
timeout,
|
||||
shouldProcess = true,
|
||||
}: SubscribeOpts) => {
|
||||
const urls = getUrls(relays)
|
||||
const executor = getExecutor(urls)
|
||||
const filters = ensurePlural(filter)
|
||||
const subscription = new Subscription()
|
||||
const now = Date.now()
|
||||
const seen = new Map()
|
||||
const eose = new Set()
|
||||
@ -199,26 +201,14 @@ export class Network {
|
||||
|
||||
Network.emitter.emit("sub:open", urls)
|
||||
|
||||
let closed = false
|
||||
const closeListeners = []
|
||||
subscription.on("close", () => {
|
||||
sub.unsubscribe()
|
||||
executor.target.cleanup()
|
||||
Network.emitter.emit("sub:close", urls)
|
||||
})
|
||||
|
||||
const close = () => {
|
||||
if (!closed) {
|
||||
sub.unsubscribe()
|
||||
executor.target.cleanup()
|
||||
Network.emitter.emit("sub:close", urls)
|
||||
|
||||
for (const f of closeListeners) {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
closed = true
|
||||
}
|
||||
|
||||
// If autoClose is set but we don't get an eose, make sure we clean up after ourselves
|
||||
if (autoClose) {
|
||||
setTimeout(close, 30_000)
|
||||
if (timeout) {
|
||||
setTimeout(subscription.close, timeout)
|
||||
}
|
||||
|
||||
const sub = executor.subscribe(filters, {
|
||||
@ -260,7 +250,7 @@ export class Network {
|
||||
Events.queue.push(event)
|
||||
}
|
||||
|
||||
onEvent(event)
|
||||
onEvent?.(event)
|
||||
},
|
||||
onEose: url => {
|
||||
onEose?.(url)
|
||||
@ -272,77 +262,13 @@ export class Network {
|
||||
|
||||
eose.add(url)
|
||||
|
||||
if (autoClose && eose.size === relays.length) {
|
||||
close()
|
||||
if (timeout && eose.size === relays.length) {
|
||||
subscription.close()
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
close,
|
||||
isClosed: () => closed,
|
||||
onClose: f => closeListeners.push(f),
|
||||
}
|
||||
}
|
||||
|
||||
const load = ({
|
||||
relays,
|
||||
filter,
|
||||
onEvent = null,
|
||||
shouldProcess = true,
|
||||
timeout = 5000,
|
||||
}: {
|
||||
relays: string[]
|
||||
filter: Filter | Filter[]
|
||||
onEvent?: (event: Event) => void
|
||||
shouldProcess?: boolean
|
||||
timeout?: number
|
||||
}) => {
|
||||
return new Promise(resolve => {
|
||||
let completed = false
|
||||
const eose = new Set()
|
||||
const allEvents = []
|
||||
|
||||
const attemptToComplete = force => {
|
||||
// If we've already unsubscribed we're good
|
||||
if (completed) {
|
||||
return
|
||||
}
|
||||
|
||||
const isDone = eose.size === relays.length
|
||||
|
||||
if (force) {
|
||||
relays.forEach(url => {
|
||||
if (!eose.has(url)) {
|
||||
Network.pool.emit("timeout", url)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (isDone || force) {
|
||||
sub.close()
|
||||
resolve(allEvents)
|
||||
completed = true
|
||||
}
|
||||
}
|
||||
|
||||
// If a relay takes too long, give up
|
||||
setTimeout(() => attemptToComplete(true), timeout)
|
||||
|
||||
const sub = subscribe({
|
||||
relays,
|
||||
filter,
|
||||
shouldProcess,
|
||||
onEvent: event => {
|
||||
onEvent?.(event)
|
||||
allEvents.push(event)
|
||||
},
|
||||
onEose: url => {
|
||||
eose.add(url)
|
||||
attemptToComplete(false)
|
||||
},
|
||||
})
|
||||
}) as Promise<Event[]>
|
||||
return subscription
|
||||
}
|
||||
|
||||
const count = async filter => {
|
||||
@ -362,13 +288,13 @@ export class Network {
|
||||
})
|
||||
}
|
||||
|
||||
const cursor = opts => new Cursor({...opts, load})
|
||||
const cursor = opts => new Cursor({...opts, subscribe})
|
||||
|
||||
const multiCursor = ({relays, ...opts}) =>
|
||||
new MultiCursor(relays.map(relay => cursor({relay, ...opts})))
|
||||
|
||||
const feed = opts => new Feed({engine, ...opts})
|
||||
|
||||
return {subscribe, publish, load, count, cursor, multiCursor, feed}
|
||||
return {subscribe, publish, count, cursor, multiCursor, feed}
|
||||
}
|
||||
}
|
||||
|
@ -68,9 +68,10 @@ export class PubkeyLoader {
|
||||
|
||||
await Promise.all(
|
||||
chunk(256, pubkeys).map(async chunk => {
|
||||
await Network.load({
|
||||
await Network.subscribe({
|
||||
relays: getChunkRelays(chunk),
|
||||
filter: getChunkFilter(chunk),
|
||||
timeout: 3000,
|
||||
})
|
||||
})
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {mergeLeft, identity, sortBy} from "ramda"
|
||||
import {all, prop, mergeLeft, identity, sortBy} from "ramda"
|
||||
import {ensurePlural, first} from "hurdak/lib/hurdak"
|
||||
import {now} from "src/util/misc"
|
||||
import type {Filter, Event} from "../types"
|
||||
@ -11,11 +11,13 @@ export type CursorOpts = {
|
||||
}
|
||||
|
||||
export class Cursor {
|
||||
done: boolean
|
||||
until: number
|
||||
buffer: Event[]
|
||||
loading: boolean
|
||||
|
||||
constructor(readonly opts: CursorOpts) {
|
||||
this.done = false
|
||||
this.until = now()
|
||||
this.buffer = []
|
||||
this.loading = false
|
||||
@ -25,7 +27,7 @@ export class Cursor {
|
||||
const limit = n - this.buffer.length
|
||||
|
||||
// If we're already loading, or we have enough buffered, do nothing
|
||||
if (this.loading || limit <= 0) {
|
||||
if (this.done || this.loading || limit <= 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
@ -34,6 +36,8 @@ export class Cursor {
|
||||
|
||||
this.loading = true
|
||||
|
||||
let count = 0
|
||||
|
||||
return this.opts.subscribe({
|
||||
autoClose: true,
|
||||
relays: [relay],
|
||||
@ -42,10 +46,13 @@ export class Cursor {
|
||||
this.until = Math.min(until, event.created_at)
|
||||
this.buffer.push(event)
|
||||
|
||||
count += 1
|
||||
|
||||
onEvent?.(event)
|
||||
},
|
||||
onEose: () => {
|
||||
this.loading = false
|
||||
this.done = count < limit
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -80,6 +87,10 @@ export class MultiCursor {
|
||||
return this.#cursors.map(c => c.load(limit)).filter(identity)
|
||||
}
|
||||
|
||||
done() {
|
||||
return all(prop("done"), this.#cursors)
|
||||
}
|
||||
|
||||
count() {
|
||||
return this.#cursors.reduce((n, c) => n + c.buffer.length, 0)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {matchFilters} from "nostr-tools"
|
||||
import {throttle} from "throttle-debounce"
|
||||
import {
|
||||
omit,
|
||||
pluck,
|
||||
partition,
|
||||
identity,
|
||||
@ -22,7 +23,10 @@ import type {Collection} from "./store"
|
||||
import {Cursor, MultiCursor} from "./Cursor"
|
||||
import type {Event, DisplayEvent, Filter} from "../types"
|
||||
|
||||
const fromDisplayEvent = omit(["zaps", "likes", "replies", "matchesFilter"])
|
||||
|
||||
export type FeedOpts = {
|
||||
limit?: number
|
||||
depth: number
|
||||
relays: string[]
|
||||
filter: Filter | Filter[]
|
||||
@ -37,11 +41,11 @@ export class Feed {
|
||||
context: Collection<Event>
|
||||
feed: Collection<DisplayEvent>
|
||||
seen: Set<string>
|
||||
subs: Record<string, Array<() => void>>
|
||||
subs: Record<string, Array<{close: () => void}>>
|
||||
cursor: MultiCursor
|
||||
|
||||
constructor(readonly opts: FeedOpts) {
|
||||
this.limit = 20
|
||||
this.limit = opts.limit || 20
|
||||
this.since = now()
|
||||
this.stopped = false
|
||||
this.deferred = []
|
||||
@ -62,7 +66,7 @@ export class Feed {
|
||||
for (const sub of ensurePlural(subs)) {
|
||||
this.subs[key].push(sub)
|
||||
|
||||
sub.onClose(() => {
|
||||
sub.on("close", () => {
|
||||
this.subs[key] = without([sub], this.subs[key])
|
||||
})
|
||||
}
|
||||
@ -81,6 +85,10 @@ export class Feed {
|
||||
}
|
||||
|
||||
isMissingParent = e => {
|
||||
if (!this.opts.shouldLoadParents) {
|
||||
return false
|
||||
}
|
||||
|
||||
const parentId = findReplyId(e)
|
||||
|
||||
return parentId && this.matchFilters(e) && !this.context.key(parentId).exists()
|
||||
@ -220,7 +228,7 @@ export class Feed {
|
||||
return
|
||||
}
|
||||
|
||||
this.subs.listeners.forEach(unsubscribe => unsubscribe())
|
||||
this.subs.listeners.forEach(sub => sub.close())
|
||||
|
||||
const contextByParentId = groupBy(findReplyId, this.context.get())
|
||||
|
||||
@ -251,7 +259,7 @@ export class Feed {
|
||||
|
||||
this.loadPubkeys(events)
|
||||
|
||||
if (shouldLoadParents) {
|
||||
if (this.opts.shouldLoadParents && shouldLoadParents) {
|
||||
this.loadParents(events)
|
||||
}
|
||||
|
||||
@ -264,7 +272,7 @@ export class Feed {
|
||||
|
||||
start() {
|
||||
const {since} = this
|
||||
const {relays, filter, engine} = this.opts
|
||||
const {relays, filter, engine, depth} = this.opts
|
||||
|
||||
// No point in subscribing if we have an end date
|
||||
if (!all(prop("until"), ensurePlural(filter))) {
|
||||
@ -273,7 +281,7 @@ export class Feed {
|
||||
relays,
|
||||
filter: ensurePlural(filter).map(assoc("since", since)),
|
||||
onEvent: batch(1000, context =>
|
||||
this.addContext(context, {shouldLoadParents: true, depth: 6})
|
||||
this.addContext(context, {shouldLoadParents: true, depth})
|
||||
),
|
||||
}),
|
||||
])
|
||||
@ -287,7 +295,7 @@ export class Feed {
|
||||
filter,
|
||||
subscribe: engine.Network.subscribe,
|
||||
onEvent: batch(100, context =>
|
||||
this.addContext(context, {shouldLoadParents: true, depth: 6})
|
||||
this.addContext(context, {shouldLoadParents: true, depth})
|
||||
),
|
||||
})
|
||||
)
|
||||
@ -305,6 +313,32 @@ export class Feed {
|
||||
}
|
||||
}
|
||||
|
||||
hydrate(feed) {
|
||||
const {depth} = this.opts
|
||||
const notes = []
|
||||
const context = []
|
||||
|
||||
const addContext = ({zaps, replies, reactions, ...note}) => {
|
||||
context.push(fromDisplayEvent(note))
|
||||
|
||||
zaps.map(zap => context.push(zap))
|
||||
reactions.map(reaction => context.push(reaction))
|
||||
|
||||
replies.map(addContext)
|
||||
}
|
||||
|
||||
feed.forEach(note => {
|
||||
addContext(note)
|
||||
|
||||
notes.push(fromDisplayEvent(note))
|
||||
})
|
||||
|
||||
this.feed.set(notes)
|
||||
this.addContext(context, {depth})
|
||||
}
|
||||
|
||||
// Loading
|
||||
|
||||
async load() {
|
||||
// If we don't have a decent number of notes yet, try to get enough
|
||||
// to avoid out of order notes
|
||||
@ -328,6 +362,12 @@ export class Feed {
|
||||
this.addToFeed(ok)
|
||||
}
|
||||
|
||||
async loadAll() {
|
||||
while (!this.cursor.done()) {
|
||||
await this.load()
|
||||
}
|
||||
}
|
||||
|
||||
deferReactions = notes => {
|
||||
const [defer, ok] = partition(e => !this.isTextNote(e) && this.isMissingParent(e), notes)
|
||||
|
||||
@ -337,7 +377,7 @@ export class Feed {
|
||||
|
||||
this.addToFeed(ready)
|
||||
this.deferred = this.deferred.concat(orphans)
|
||||
}, 3000)
|
||||
}, 1500)
|
||||
|
||||
return ok
|
||||
}
|
||||
@ -346,7 +386,7 @@ export class Feed {
|
||||
// If something has a parent id but we haven't found the parent yet, skip it until we have it.
|
||||
const [defer, ok] = partition(e => this.isTextNote(e) && this.isMissingParent(e), notes)
|
||||
|
||||
setTimeout(() => this.addToFeed(defer), 3000)
|
||||
setTimeout(() => this.addToFeed(defer), 1500)
|
||||
|
||||
return ok
|
||||
}
|
||||
|
19
src/engine/util/Subscription.ts
Normal file
19
src/engine/util/Subscription.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import EventEmitter from "events"
|
||||
|
||||
export class Subscription extends EventEmitter {
|
||||
closed: boolean
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.closed = false
|
||||
}
|
||||
|
||||
close = () => {
|
||||
if (!this.closed) {
|
||||
this.closed = true
|
||||
this.emit("close")
|
||||
this.removeAllListeners()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user