Change pagination, fix alerts, all kinds of good stuff

This commit is contained in:
Jonathan Staab 2023-01-13 06:14:05 -08:00
parent 98d3897e7c
commit 933e107500
21 changed files with 183 additions and 144 deletions

View File

@ -38,6 +38,7 @@ If you like Coracle and want to support its development, you can donate sats via
- Capture certain events in a local db
- File import/export from db, NFC transfer
- [ ] Save user notes to db
- [ ] Release to android with https://svelte-native.technology/docs
# Bugs
@ -62,18 +63,17 @@ If you like Coracle and want to support its development, you can donate sats via
- [ ] Add settings storage on nostr, maybe use kind 0?
- [ ] Warn that everything will be cleared on logout
- [ ] Clean up login page to prefer extension, make private key entry "advanced"
- [ ] Do I need to implement re-connecting now?
- [ ] Connection management
- [ ] Do I need to implement re-connecting now?
- [ ] Handle failed connections
- [ ] Close connections that haven't been used in a while
- [ ] Improve login UX for bootstrap delay. Nostr facts?
- [ ] Revisit pagination. Use bigger timedelta + limit, set earliest seen timestamp when paginating? Handle no results on page.
- [ ] We often get the root as the reply, figure out why that is, compared to astral/damus
- [ ] Alerts still aren't great. Maybe lazy load? We delete old events, so context will disappear and notes will become empty.
- [ ] Only use dexie for alerts rather than all events
- Load feeds by setting a listener since now, paginating using limit (of 2 maybe), and awaiting context for each page. Listener appends to "newNotes", cursor appends to "notes", load more moves new notes into notes. Use the note originally loaded as the anchor, don't re-process the whole list
- [ ] Close connections that haven't been used in a while
- [ ] Load feeds from network rather than user relays? This could make global feed more useful: global for _my_ relays
- [ ] Release to android with https://svelte-native.technology/docs
- [ ] Compress is taking too long and messing up the ui, maybe use dexie for people?
- [ ] Move people to nprofile via bech32 entity (or redirect from there)
## 0.2.7
- [x]
## 0.2.6

View File

@ -49,10 +49,11 @@
let menuIcon
let scrollY
let suspendedSubs = []
let {since, latest} = alerts
let {lastCheckedAlerts, mostRecentAlert} = alerts
onMount(() => {
if ($user) {
alerts.load(getRelays(), $user.pubkey)
alerts.listen(getRelays(), $user.pubkey)
}
@ -128,7 +129,7 @@
<li class="cursor-pointer relative">
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/alerts">
<i class="fa-solid fa-bell mr-2" /> Alerts
{#if $latest > $since}
{#if $mostRecentAlert > $lastCheckedAlerts}
<div class="w-2 h-2 rounded bg-accent absolute top-3 left-6" />
{/if}
</a>
@ -184,7 +185,7 @@
<img src="/images/favicon.png" class="w-8" />
<h1 class="staatliches text-3xl">Coracle</h1>
</Anchor>
{#if $latest > $since}
{#if $mostRecentAlert > $lastCheckedAlerts}
<div class="w-2 h-2 rounded bg-accent absolute top-4 left-12" />
{/if}
</div>

View File

@ -1,5 +1,6 @@
import {relayInit} from 'nostr-tools'
import {uniqBy, filter, identity, prop} from 'ramda'
import {uniqBy, is, filter, identity, prop} from 'ramda'
import {isRelay} from 'src/util/nostr'
import {ensurePlural} from 'hurdak/lib/hurdak'
const relays = {}
@ -41,7 +42,7 @@ const connect = url => {
const publish = async (urls, event) => {
return Promise.all(
urls.map(async url => {
urls.filter(isRelay).map(async url => {
const relay = await connect(url)
if (relay) {
@ -51,9 +52,33 @@ const publish = async (urls, event) => {
)
}
const describeFilter = filter => {
let parts = []
for (const [key, value] of Object.entries(filter)) {
if (is(Array, value)) {
parts.push(`${key}[${value.length}]`)
} else {
parts.push(key)
}
}
return parts.join(',')
}
const subscribe = async (urls, filters) => {
filters = ensurePlural(filters)
// Create a human readable subscription id for debugging
const id = [
Math.random().toString().slice(2, 6),
filters.map(describeFilter).join(':'),
].join('-')
console.log(filters, id)
const subs = filter(identity, await Promise.all(
urls.map(async url => {
urls.filter(isRelay).map(async url => {
const relay = await connect(url)
// If the relay failed to connect, give up
@ -61,7 +86,8 @@ const subscribe = async (urls, filters) => {
return null
}
const sub = relay.sub(ensurePlural(filters))
const sub = relay.sub(filters, {id})
sub.relay = relay
sub.relay.stats.activeCount += 1
@ -103,6 +129,8 @@ const subscribe = async (urls, filters) => {
}
const request = (urls, filters) => {
urls = urls.filter(isRelay)
return new Promise(async resolve => {
const subscription = await subscribe(urls, filters)
const now = Date.now()

View File

@ -1,16 +1,34 @@
import {sortBy, pluck} from 'ramda'
import {first} from 'hurdak/lib/hurdak'
import {synced, batch, now, timedelta} from 'src/util/misc'
import {get} from 'svelte/store'
import {synced, batch, now} from 'src/util/misc'
import {isAlert} from 'src/util/nostr'
import {listen as _listen, getMuffle, db} from 'src/agent'
import {load as _load, listen as _listen, getMuffle, db} from 'src/agent'
import loaders from 'src/app/loaders'
import query from 'src/app/query'
let listener
const start = now() - timedelta(30, 'days')
const since = synced("app/alerts/since", start)
const latest = synced("app/alerts/latest", start)
const mostRecentAlert = synced("app/alerts/mostRecentAlert", 0)
const lastCheckedAlerts = synced("app/alerts/lastCheckedAlerts", 0)
const onChunk = async (relays, pubkey, events) => {
events = events.filter(e => isAlert(e, pubkey))
if (events.length > 0) {
const context = await loaders.loadContext(relays, events)
const notes = query.threadify(events, context, {muffle: getMuffle()})
await db.alerts.bulkPut(notes)
mostRecentAlert.update($t => events.reduce((t, e) => Math.max(t, e.created_at), $t))
}
}
const load = async (relays, pubkey) => {
const since = get(lastCheckedAlerts)
const events = await _load(relays, {kinds: [1, 7], '#p': [pubkey], since, limit: 100})
onChunk(relays, pubkey, events)
}
const listen = async (relays, pubkey) => {
if (listener) {
@ -19,23 +37,11 @@ const listen = async (relays, pubkey) => {
listener = await _listen(
relays,
{kinds: [1, 7], '#p': [pubkey], since: start},
batch(300, async events => {
events = events.filter(e => isAlert(e, pubkey))
if (events.length > 0) {
const context = await loaders.loadContext(relays, events)
const notes = query.threadify(events, context, {muffle: getMuffle()})
await db.alerts.bulkPut(notes)
latest.update(
$latest =>
Math.max(first(sortBy(t => -t, pluck('created_at', events))), $latest)
)
}
{kinds: [1, 7], '#p': [pubkey], since: now()},
batch(300, events => {
onChunk(relays, pubkey, events)
})
)
}
export default {listen, since, latest}
export default {load, listen, mostRecentAlert, lastCheckedAlerts}

View File

@ -1,7 +1,6 @@
import {without} from 'ramda'
import {updateIn, mergeRight} from 'hurdak/lib/hurdak'
import {get} from 'svelte/store'
import {getPerson, getRelays, people, load, keys} from 'src/agent'
import {getPerson, getRelays, load, keys} from 'src/agent'
import {toast, modal, settings} from 'src/app/ui'
import cmd from 'src/app/cmd'
import alerts from 'src/app/alerts'
@ -17,8 +16,11 @@ export const login = async ({privkey, pubkey}) => {
keys.setPublicKey(pubkey)
}
await loaders.loadNetwork(getRelays(), pubkey)
await alerts.listen(getRelays(), pubkey)
await Promise.all([
loaders.loadNetwork(getRelays(), pubkey),
alerts.load(getRelays(), pubkey),
alerts.listen(getRelays(), pubkey),
])
}
export const addRelay = async url => {
@ -26,12 +28,13 @@ export const addRelay = async url => {
const person = getPerson(pubkey)
const relays = (person?.relays || []).concat(url)
// Persist to our local copy immediately so we can publish to the new one
people.update(updateIn(pubkey, mergeRight({pubkey, relays})))
await cmd.setRelays(relays)
await loaders.loadNetwork(relays, pubkey)
await alerts.listen(relays, pubkey)
await Promise.all([
loaders.loadNetwork(getRelays(), pubkey),
alerts.load(getRelays(), pubkey),
alerts.listen(getRelays(), pubkey),
])
}
export const removeRelay = async url => {

View File

@ -1,5 +1,5 @@
import {uniq, pluck, groupBy, identity} from 'ramda'
import {ensurePlural, createMap} from 'hurdak/lib/hurdak'
import {uniq, flatten, pluck, groupBy, identity} from 'ramda'
import {ensurePlural, createMap, chunk} from 'hurdak/lib/hurdak'
import {findReply, personKinds, Tags, getTagValues} from 'src/util/nostr'
import {now, timedelta} from 'src/util/misc'
import {load, getPerson} from 'src/agent'
@ -47,36 +47,40 @@ const loadNetwork = async (relays, pubkey) => {
}
const loadContext = async (relays, notes, {loadParents = true} = {}) => {
// TODO: remove this and batch context loading, or load less at a time
notes = ensurePlural(notes).slice(0, 256)
notes = ensurePlural(notes)
if (notes.length === 0) {
return notes
}
const authors = getStalePubkeys(pluck('pubkey', notes))
const parentTags = loadParents ? uniq(notes.map(findReply).filter(identity)) : []
const filter = [{kinds: [1, 7], '#e': pluck('id', notes)}]
return flatten(await Promise.all(
chunk(256, notes).map(async chunk => {
const authors = getStalePubkeys(pluck('pubkey', notes))
const parentTags = loadParents ? uniq(notes.map(findReply).filter(identity)) : []
const combinedRelays = uniq(relays.concat(Tags.wrap(parentTags).relays()))
const filter = [{kinds: [1, 7], '#e': pluck('id', notes)}]
if (authors.length > 0) {
filter.push({kinds: personKinds, authors})
}
if (authors.length > 0) {
filter.push({kinds: personKinds, authors})
}
if (parentTags.length > 0) {
filter.push({kinds: [1], ids: getTagValues(parentTags)})
relays = uniq(relays.concat(Tags.wrap(parentTags).relays()))
}
if (parentTags.length > 0) {
filter.push({kinds: [1], ids: getTagValues(parentTags)})
}
const events = await load(relays, filter)
const events = await load(combinedRelays, filter)
if (parentTags.length === 0) {
return events
}
if (parentTags.length === 0) {
return events
}
const eventsById = createMap('id', events)
const parents = getTagValues(parentTags).map(id => eventsById[id]).filter(identity)
const eventsById = createMap('id', events)
const parents = getTagValues(parentTags).map(id => eventsById[id]).filter(identity)
const parentRelays = Tags.from(parents).relays()
return events.concat(await loadContext(relays, parents, {loadParents: false}))
return events.concat(await loadContext(parentRelays, parents, {loadParents: false}))
})
))
}
export default {loadNetwork, loadPeople, personKinds, loadContext}

View File

@ -2,7 +2,7 @@ import {get} from 'svelte/store'
import {sortBy, identity} from 'ramda'
import {createMap, ellipsize} from 'hurdak/lib/hurdak'
import {renderContent} from 'src/util/html'
import {Tags, displayPerson, findReply} from 'src/util/nostr'
import {Tags, displayPerson, findReplyId} from 'src/util/nostr'
import {people, getPerson} from 'src/agent'
import {routes} from "src/app/ui"
@ -41,8 +41,8 @@ const renderNote = (note, {showEntire = false}) => {
}
const annotate = (note, context, {showEntire = false, depth = 1} = {}) => {
const reactions = context.filter(e => e.kind === 7 && findReply(e) === note.id)
const replies = context.filter(e => e.kind === 1 && findReply(e) === note.id)
const reactions = context.filter(e => e.kind === 7 && findReplyId(e) === note.id)
const replies = context.filter(e => e.kind === 1 && findReplyId(e) === note.id)
return {
...note, reactions,
@ -65,13 +65,13 @@ const threadify = (events, context, {muffle = []} = {}) => {
const notes = sortBy(
e => -e.created_at,
events
.map(e => contextById[findReply(e)] || (e.kind === 1 ? e : null))
.map(e => contextById[findReplyId(e)] || (e.kind === 1 ? e : null))
.filter(e => e && !muffle.includes(e.pubkey))
)
// Annotate our feed with parents, reactions, replies
return notes.map(note => {
let parent = contextById[findReply(note)]
let parent = contextById[findReplyId(note)]
if (parent) {
parent = annotate(parent, context)

View File

@ -7,7 +7,7 @@
import {navigate} from 'svelte-routing'
import {quantify} from 'hurdak/lib/hurdak'
import {hasParent} from 'src/util/html'
import {findReply, isLike} from "src/util/nostr"
import {findReply, findReplyId, isLike} from "src/util/nostr"
import Preview from 'src/partials/Preview.svelte'
import Anchor from 'src/partials/Anchor.svelte'
import {settings, modal} from "src/app"
@ -47,9 +47,10 @@
}
const goToParent = async () => {
const parent = {id: findReply(note)[1]}
const [id, url] = findReply(note).slice(1)
const relays = getEventRelays(note).concat(url)
modal.set({note: parent, relays})
modal.set({note: {id}, relays})
}
const react = async content => {
@ -125,7 +126,7 @@
<div class="ml-6 flex flex-col gap-2">
{#if findReply(note) && showParent}
<small class="text-light">
Reply to <Anchor on:click={goToParent}>{findReply(note)[1].slice(0, 8)}</Anchor>
Reply to <Anchor on:click={goToParent}>{findReplyId(note).slice(0, 8)}</Anchor>
</small>
{/if}
{#if flag}

View File

@ -1,5 +1,6 @@
<script>
import {onMount} from 'svelte'
import {uniqBy, prop} from 'ramda'
import {slide} from 'svelte/transition'
import {quantify} from 'hurdak/lib/hurdak'
import {createScroller} from 'src/util/misc'
@ -18,7 +19,7 @@
const showNewNotes = () => {
// Drop notes at the end if there are a lot
notes = newNotes.concat(notes).slice(0, maxNotes)
notes = uniqBy(prop('id'), newNotes.concat(notes).slice(0, maxNotes))
newNotes = []
}
@ -30,7 +31,7 @@
const scroller = createScroller(async () => {
// Drop notes at the top if there are a lot
notes = notes.concat(await loadNotes()).slice(-maxNotes)
notes = uniqBy(prop('id'), notes.concat(await loadNotes()).slice(-maxNotes))
})
return async () => {

View File

@ -1,12 +1,11 @@
<script>
import {propEq, identity, uniq, prop, sortBy} from 'ramda'
import {sortBy} from 'ramda'
import {onMount} from 'svelte'
import {fly} from 'svelte/transition'
import {createMap} from 'hurdak/lib/hurdak'
import {now, createScroller} from 'src/util/misc'
import {getPerson, user, db} from 'src/agent'
import {isLike} from 'src/util/nostr'
import {user, db} from 'src/agent'
import {alerts} from 'src/app'
import query from 'src/app/query'
import Note from 'src/partials/Note.svelte'
import Like from 'src/partials/Like.svelte'
@ -14,38 +13,23 @@
let annotatedNotes = []
onMount(async () => {
alerts.since.set(now())
alerts.lastCheckedAlerts.set(now())
return createScroller(async () => {
limit += 10
const events = await db.alerts.toArray()
const parentIds = uniq(events.map(prop('reply')).filter(identity))
const parents = await Promise.all(parentIds.map(query.findNote))
const parentsById = createMap('id', parents.filter(identity))
const notes = await Promise.all(
events.filter(propEq('kind', 1)).map(n => query.findNote(n.id))
)
const reactions = await Promise.all(
events
.filter(e => e.kind === 7 && parentsById[e.reply])
.map(async e => ({
...e,
person: getPerson(e.pubkey, true),
parent: parentsById[e.reply],
}))
)
const notes = events.filter(e => e.kind === 1)
const likes = events.filter(e => e.kind === 7 && isLike(e.content))
// Combine likes of a single note. Remove grandchild likes
const likesById = {}
for (const reaction of reactions.filter(e => e.parent?.pubkey === $user.pubkey)) {
if (!likesById[reaction.parent.id]) {
likesById[reaction.parent.id] = {...reaction.parent, people: []}
for (const like of likes.filter(e => e.parent?.pubkey === $user.pubkey)) {
if (!likesById[like.parent.id]) {
likesById[like.parent.id] = {...like.parent, people: []}
}
likesById[reaction.parent.id].people.push(reaction.person)
likesById[like.parent.id].people.push(like.person)
}
annotatedNotes = sortBy(

View File

@ -1,6 +1,5 @@
<script>
import {nip19} from 'nostr-tools'
import {getRelays} from 'src/agent'
import NoteDetail from 'src/views/NoteDetail.svelte'
import Person from 'src/routes/Person.svelte'
@ -13,7 +12,7 @@
{#if type === "nevent"}
<NoteDetail note={{id: data.id}} relays={data.relays} />
{:else if type === "note"}
<NoteDetail note={{id: data}} relays={getRelays()} />
<NoteDetail note={{id: data}} />
{:else if type === "nprofile"}
<Person npub={nip19.npubEncode(data.pubkey)} relays={data.relays} activeTab="notes" />
{:else if type === "npub"}

View File

@ -1,9 +1,11 @@
<script>
import {fly} from 'svelte/transition'
import {db} from 'src/agent'
setTimeout(async () => {
// Clear localstorage
// Clear localstorage and database
localStorage.clear()
db.delete()
// do a hard refresh so everything gets totally cleared
window.location = '/login'

View File

@ -12,7 +12,9 @@
let q = ""
let search
let relays = $user?.relays || defaults.relays
let relays = []
$: relays = $user?.relays || defaults.relays
fetch(get(settings).dufflepudUrl + '/relay').then(r => r.json()).then(({relays}) => {
for (const url of relays) {

View File

@ -129,16 +129,12 @@ export const getLastSync = (k, fallback = 0) => {
}
export class Cursor {
constructor(delta) {
this.since = now()
constructor(limit = 10) {
this.until = now()
this.delta = delta
this.limit = limit
}
step() {
this.until = this.since
this.since -= this.delta
return [this.since, this.until]
onChunk(events) {
this.until = events.reduce((t, e) => Math.min(t, e.created_at), this.until)
}
}

View File

@ -1,4 +1,4 @@
import {last, prop} from 'ramda'
import {last, prop, flatten, uniq} from 'ramda'
import {nip19} from 'nostr-tools'
import {ensurePlural, first} from 'hurdak/lib/hurdak'
@ -24,12 +24,10 @@ export class Tags {
return last(this.tags)
}
relays() {
return this.tags
.map(t => t[3])
.filter(url => typeof url === 'string' && url.startsWith('ws'))
return uniq(flatten(this.tags).filter(isRelay))
}
values() {
this.tags = this.tags.map(t => t[0])
this.tags = this.tags.map(t => t[1])
return this
}
@ -51,9 +49,13 @@ export const getTagValues = tags => tags.map(t => t[1])
export const findReply = e =>
Tags.from(e).type("e").mark("reply").first() || Tags.from(e).type("e").first()
export const findReplyId = e => first(Tags.wrap(findReply(e)).values())
export const findRoot = e =>
Tags.from(e).type("e").mark("root").first()
export const findRootId = e => first(Tags.wrap(findRoot(e)).values())
export const displayPerson = p => {
if (p.name) {
return p.name
@ -77,3 +79,6 @@ export const isAlert = (e, pubkey) => {
return true
}
export const isRelay = url => typeof url === 'string' && url.match(/^wss?:\/\/.+/)

View File

@ -1,11 +1,12 @@
<script>
import {fly} from 'svelte/transition'
import {loadNote} from 'src/app'
import {getRelays} from 'src/agent'
import Note from 'src/partials/Note.svelte'
import Spinner from 'src/partials/Spinner.svelte'
export let note
export let relays
export let relays = getRelays()
if (!note.pubkey) {
loadNote(relays, note.id).then(found => {

View File

@ -1,13 +1,13 @@
<script>
import Notes from "src/partials/Notes.svelte"
import {timedelta, Cursor, now, batch} from 'src/util/misc'
import {Cursor, now, batch} from 'src/util/misc'
import {getRelays, getMuffle, listen, load} from 'src/agent'
import loaders from 'src/app/loaders'
import query from 'src/app/query'
const relays = getRelays()
const filter = {kinds: [1, 5, 7]}
const cursor = new Cursor(timedelta(1, 'minutes'))
const cursor = new Cursor()
const listenForNotes = onNotes =>
listen(relays, {...filter, since: now()}, batch(300, async notes => {
@ -17,10 +17,14 @@
}))
const loadNotes = async () => {
const [since, until] = cursor.step()
const notes = await load(relays, {...filter, since, until})
const {limit, until} = cursor
const notes = await load(relays, {...filter, limit, until})
const context = await loaders.loadContext(relays, notes)
console.log('========')
console.log({notes, context})
console.log(query.threadify(notes, context, {muffle: getMuffle()}))
return query.threadify(notes, context, {muffle: getMuffle()})
}
</script>

View File

@ -1,6 +1,6 @@
<script>
import Notes from "src/partials/Notes.svelte"
import {now, timedelta, shuffle, batch, Cursor} from 'src/util/misc'
import {now, Cursor, shuffle, batch} from 'src/util/misc'
import {user, getRelays, getFollows, getMuffle, listen, load} from 'src/agent'
import loaders from 'src/app/loaders'
import query from 'src/app/query'
@ -12,7 +12,7 @@
const network = shuffle(follows.flatMap(getFollows)).slice(0, 50)
const authors = follows.concat(network)
const filter = {kinds: [1, 7], authors}
const cursor = new Cursor(timedelta(20, 'minutes'))
const cursor = new Cursor()
const listenForNotes = onNotes =>
listen(relays, {...filter, since: now()}, batch(300, async notes => {
@ -22,10 +22,12 @@
}))
const loadNotes = async () => {
const [since, until] = cursor.step()
const notes = await load(relays, {...filter, since, until})
const {limit, until} = cursor
const notes = await load(relays, {...filter, limit, until})
const context = await loaders.loadContext(relays, notes)
cursor.onChunk(notes)
return query.threadify(notes, context, {muffle: getMuffle()})
}
</script>

View File

@ -1,6 +1,6 @@
<script>
import Notes from "src/partials/Notes.svelte"
import {timedelta, now, batch, Cursor} from 'src/util/misc'
import {now, batch, Cursor} from 'src/util/misc'
import {load, listen, getRelays, getMuffle} from 'src/agent'
import loaders from 'src/app/loaders'
import query from 'src/app/query'
@ -9,7 +9,7 @@
const relays = getRelays(pubkey)
const filter = {kinds: [7], authors: [pubkey]}
const cursor = new Cursor(timedelta(1, 'days'))
const cursor = new Cursor()
const listenForNotes = onNotes =>
listen(relays, {...filter, since: now()}, batch(300, async notes => {
@ -19,8 +19,8 @@
}))
const loadNotes = async () => {
const [since, until] = cursor.step()
const notes = await load(relays, {...filter, since, until})
const {limit, until} = cursor
const notes = await load(relays, {...filter, limit, until})
const context = await loaders.loadContext(relays, notes)
return query.threadify(notes, context, {muffle: getMuffle()})

View File

@ -1,6 +1,6 @@
<script>
import Notes from "src/partials/Notes.svelte"
import {now, timedelta, shuffle, batch, Cursor} from 'src/util/misc'
import {now, shuffle, batch, Cursor} from 'src/util/misc'
import {getRelays, getFollows, getMuffle, listen, load} from 'src/agent'
import loaders from 'src/app/loaders'
import query from 'src/app/query'
@ -12,7 +12,7 @@
const network = shuffle(follows.flatMap(getFollows)).slice(0, 50)
const authors = follows.concat(network)
const filter = {kinds: [1, 7], authors}
const cursor = new Cursor(timedelta(1, 'hours'))
const cursor = new Cursor()
const listenForNotes = onNotes =>
listen(relays, {...filter, since: now()}, batch(300, async notes => {
@ -22,8 +22,8 @@
}))
const loadNotes = async () => {
const [since, until] = cursor.step()
const notes = await load(relays, {...filter, since, until})
const {limit, until} = cursor
const notes = await load(relays, {...filter, limit, until})
const context = await loaders.loadContext(relays, notes)
return query.threadify(notes, context, {muffle: getMuffle()})

View File

@ -1,6 +1,6 @@
<script>
import Notes from "src/partials/Notes.svelte"
import {timedelta, now, batch, Cursor} from 'src/util/misc'
import {now, batch, Cursor} from 'src/util/misc'
import {load, listen, getRelays, getMuffle} from 'src/agent'
import loaders from 'src/app/loaders'
import query from 'src/app/query'
@ -9,7 +9,7 @@
const relays = getRelays(pubkey)
const filter = {kinds: [1], authors: [pubkey]}
const cursor = new Cursor(timedelta(1, 'days'))
const cursor = new Cursor()
const listenForNotes = onNotes =>
listen(relays, {...filter, since: now()}, batch(300, async notes => {
@ -19,8 +19,8 @@
}))
const loadNotes = async () => {
const [since, until] = cursor.step()
const notes = await load(relays, {...filter, since, until})
const {limit, until} = cursor
const notes = await load(relays, {...filter, limit, until})
const context = await loaders.loadContext(relays, notes)
return query.threadify(notes, context, {muffle: getMuffle()})