Fix alerts

This commit is contained in:
Jonathan Staab 2022-12-23 11:59:40 -08:00
parent 7ed121f560
commit a7a41a659c
18 changed files with 359 additions and 186 deletions

View File

@ -19,11 +19,13 @@ Coracle is currently in _alpha_ - expect bugs, slow loading times, and rough edg
- [ ] An actual readme
- [ ] Server discovery and relay publishing - https://github.com/nostr-protocol/nips/pull/32/files
- [ ] Support invoices https://twitter.com/jb55/status/1604131336247476224
- [ ] Indexing server
- [ ] Add a coracle relay
- [ ] Expand/collapse large threads
- [ ] Don't send note on enter, allow linebreaks
- [ ] NIP 05
# Bugs
- [ ] Use https://nostr.watch/relays.json to populate relays
- [ ] Completely redo notes fetching, it's buggy as heck
- [ ] uniq and sortBy are sprinkled all over the place, figure out a better solution
- [ ] Search page is slow and likes don't show up. Probably move this server-side
@ -35,16 +37,22 @@ Coracle is currently in _alpha_ - expect bugs, slow loading times, and rough edg
- [ ] We're sending client=astral tags, event id 125ff9dc495f65d302e8d95ea6f9385106cc31b81c80e8c582b44be92fa50c44
- [ ] Add notification for slow relays
- [ ] Wait for 60% or so of relays to eose to balance completeness with speed
- [ ] Add a CSP
- [ ] Add a CSP, check for XSS in image urls
# Current update
- [ ] Add depth to findNote
- [ ] Add date to alerts
- [ ] Re-implement muffle
- Don't store muffled events, when muffle changes delete them
- [ ] Delete old events
- [ ] Sync account updates to user for e.g. muffle settings
- [ ] Make sure login/out, no user usage works
- [ ] Add a re-sync/clear cache button
- [ ] Note detail context not showing when navigating between note details (e.g. to parent)
- [ ] Show reply to on feed
- [ ] Write blog post
- [ ] Get rid of dispatch
- https://vitejs.dev/guide/features.html#web-workers
- https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
- https://web.dev/module-workers/
@ -52,20 +60,17 @@ Coracle is currently in _alpha_ - expect bugs, slow loading times, and rough edg
- [x] Sync user
- [x] Based on petnames, sync network to 2 or 3 degrees of separation
- When a user is added/removed, sync them and add to or remove from network
- [ ] Add cursor object to handle since/until/last sync
- [ ] Separate fetching and loading from the db
- [x] Add cursor object to handle since/until/last sync
- [x] Separate fetching and loading from the db
- Each route should have a fetcher and loader.
- The fetcher should keep track of the range of notes it has already gotten
- Separate helper functions into loaders and fetchers
- [ ] Main fetch requests:
- [x] Main fetch requests:
- Fetch feed by name, since last sync
- Fetch person, including feed
- Fetch note, including context
- This is based on detail pages. Each request should check local db and fall back to network, all within an await.
# Problems to solve
- [ ] How will newcomers get followed?
# Quick fixes
- Add default relay and relay list
- Show relay nav item when no user
- check security = user.name, image, use https://github.com/cure53/DOMPurify

View File

@ -10,7 +10,7 @@
import {Router, Route, links, navigate} from "svelte-routing"
import {globalHistory} from "svelte-routing/src/history"
import {hasParent} from 'src/util/html'
import {timedelta} from 'src/util/misc'
import {timedelta, now} from 'src/util/misc'
import {store as toast} from "src/state/toast"
import {modal, alerts, settings} from "src/state/app"
import relay, {user, connections} from 'src/relay'
@ -30,6 +30,8 @@
import Person from "src/routes/Person.svelte"
import NoteCreate from "src/routes/NoteCreate.svelte"
window.relay = relay
export let url = ""
const menuIsOpen = writable(false)
@ -42,9 +44,7 @@
let scrollY
let suspendedSubs = []
let mostRecentAlert = relay.lq(async () => {
const [e] = await relay
.filterAlerts($user, {since: $alerts.since})
.limit(1).reverse().sortBy('created_at')
const [e] = await relay.filterAlerts($user, 1)
return e?.created_at
})
@ -70,10 +70,6 @@
}, 200)
}
if ($user) {
relay.pool.syncNetwork()
}
onMount(() => {
// Close menu on click outside
document.querySelector("html").addEventListener("click", e => {
@ -82,7 +78,18 @@
}
})
return modal.subscribe($modal => {
let prevPubkey = null
const unsubUser = user.subscribe($user => {
if ($user && $user.pubkey !== prevPubkey) {
relay.pool.syncNetwork()
relay.pool.listenForEvents('App/alerts', {'#p': [$user.pubkey], since: now()})
}
prevPubkey = $user?.pubkey
})
const unsubModal = modal.subscribe($modal => {
// Keep scroll position on body, but don't allow scrolling
if ($modal) {
// This is not idempotent, so don't duplicate it
@ -97,6 +104,11 @@
window.scrollTo(0, scrollY)
}
})
return () => {
unsubUser()
unsubModal()
}
})
</script>

View File

@ -1,10 +1,15 @@
<script>
import {Link} from 'svelte-routing'
export let person
</script>
<a href={`/people/${person.pubkey}/notes`} class="flex gap-2 items-center relative z-10">
<Link
to={`/people/${person.pubkey}/notes`}
class="flex gap-2 items-center relative z-10"
on:click={e => e.stopPropagation()}>
<div
class="overflow-hidden w-4 h-4 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
style="background-image: url({person.picture})" />
<span class="text-lg font-bold">{person.name || person.pubkey.slice(0, 8)}</span>
</a>
</Link>

49
src/partials/Like.svelte Normal file
View File

@ -0,0 +1,49 @@
<script>
import {fly} from 'svelte/transition'
import {ellipsize, quantify} from 'hurdak/src/core'
import Badge from "src/partials/Badge.svelte"
import {formatTimestamp} from 'src/util/misc'
import {modal} from 'src/state/app'
export let note
let isOpen = false
const openPopover = e => {
e.stopPropagation()
isOpen = true
}
const closePopover = e => {
e.stopPropagation()
isOpen = false
}
</script>
<div
class="py-2 px-3 flex flex-col gap-2 text-white cursor-pointer transition-all
border border-solid border-black hover:border-medium hover:bg-dark"
on:click={() => modal.set({note})}>
<div class="flex gap-2 items-center justify-between relative">
<span class="cursor-pointer" on:click={openPopover}>
{quantify(note.people.length, 'person', 'people')} liked your note.
</span>
{#if isOpen}
<div transition:fly={{y: 20}} class="fixed inset-0 z-10" on:click={closePopover} />
<div
transition:fly={{y: 20}}
class="absolute bottom-0 -ml-3 mb-12 py-2 px-4 rounded border border-solid border-medium
bg-dark grid grid-cols-3 gap-y-2 gap-x-4 z-10">
{#each note.people as person (person.pubkey)}
<Badge {person} />
{/each}
</div>
{/if}
<p class="text-sm text-light">{formatTimestamp(note.created_at)}</p>
</div>
<div class="ml-6 text-light">
{ellipsize(note.content, 120)}
</div>
</div>

View File

@ -2,7 +2,7 @@
import {onDestroy} from 'svelte'
import {createScroller} from 'src/util/misc'
import Spinner from 'src/partials/Spinner.svelte'
import Note from "src/views/Note.svelte"
import Note from "src/partials/Note.svelte"
import relay from 'src/relay'
export let loadNotes
@ -11,11 +11,15 @@
let notes
let limit = 0
onDestroy(createScroller(async () => {
const scroller = createScroller(async () => {
limit += 20
notes = relay.lq(() => loadNotes(limit))
}))
})
onDestroy(() => {
scroller.stop()
})
</script>
{#if notes}

View File

@ -1,7 +1,7 @@
import {liveQuery} from 'dexie'
import {get} from 'svelte/store'
import {pluck, uniqBy, groupBy, concat, without, prop, uniq, objOf, isNil, identity} from 'ramda'
import {ensurePlural, createMap, ellipsize, first} from 'hurdak/lib/hurdak'
import {pluck, take, uniqBy, groupBy, concat, without, prop, isNil, identity} from 'ramda'
import {ensurePlural, createMap, ellipsize} from 'hurdak/lib/hurdak'
import {escapeHtml} from 'src/util/html'
import {filterTags, findReply, findRoot} from 'src/util/nostr'
import {db} from 'src/relay/db'
@ -17,21 +17,6 @@ const lq = f => liveQuery(async () => {
}
})
// Filter builders
export const buildNoteContextFilter = async (note, extra = {}) => {
const replyId = findReply(note)
const filter = [
{...extra, kinds: [1, 5, 7], '#e': [note.id]},
{kinds: [0], authors: [note.pubkey]}]
if (replyId && !await db.events.get(replyId)) {
filter.push({...extra, kinds: [1], ids: [replyId]})
}
return filter
}
// Utils for querying dexie - these return collections, not arrays
const prefilterEvents = filter => {
@ -64,22 +49,20 @@ const filterEvents = filter => {
return true
})
.reverse()
.sortBy('created_at')
}
const filterReplies = async (id, filter) => {
const tags = db.tags.where('value').equals(id).filter(t => t.mark === 'reply')
const ids = pluck('event', await tags.toArray())
const replies = await filterEvents({...filter, kinds: [1], ids}).toArray()
const events = await db.events.where('reply').equals(id).toArray()
return replies
return events.filter(e => e.kind === 1)
}
const filterReactions = async (id, filter) => {
const tags = db.tags.where('value').equals(id).filter(t => t.mark === 'reply')
const ids = pluck('event', await tags.toArray())
const reactions = await filterEvents({...filter, kinds: [7], ids}).toArray()
const events = await db.events.where('reply').equals(id).toArray()
return reactions
return events.filter(e => e.kind === 7)
}
const findNote = async (id, {showEntire = false} = {}) => {
@ -117,7 +100,7 @@ const findNote = async (id, {showEntire = false} = {}) => {
const annotateChunk = async chunk => {
const ancestorIds = concat(chunk.map(findRoot), chunk.map(findReply)).filter(identity)
const ancestors = await filterEvents({kinds: [1], ids: ancestorIds}).toArray()
const ancestors = await filterEvents({kinds: [1], ids: ancestorIds})
const allNotes = uniqBy(prop('id'), chunk.concat(ancestors))
const notesById = createMap('id', allNotes)
@ -171,12 +154,24 @@ const renderNote = async (note, {showEntire = false}) => {
})
}
const filterAlerts = async (person, filter) => {
const filterAlerts = async (person, limit) => {
const tags = db.tags.where('value').equals(person.pubkey)
const ids = pluck('event', await tags.toArray())
const events = await filterEvents({...filter, kinds: [1, 7], ids})
const alerts = take(limit + 1, await filterEvents({kinds: [1, 7], ids}))
return events
return alerts.filter(e => {
// Don't show people's own stuff
if (e.pubkey === person.pubkey) {
return false
}
// Only notify users about positive reactions
if (e.kind === 7 && !['', '+'].includes(e.content)) {
return false
}
return true
})
}
// Synchronization
@ -210,11 +205,32 @@ const unfollow = async pubkey => {
// Methods that wil attempt to load from the database and fall back to the network.
// This is intended only for bootstrapping listeners
const getOrLoadNote = async (id, {showEntire = false} = {}) => {
const loadNoteContext = async (note, {loadParent = false} = {}) => {
// Load note context - this assumes that we are looking at a feed, and so
// we already have the note's parent and its likes loaded.
const filter = [{kinds: [1, 5, 7], '#e': [note.id]}]
if (!prop(note.pubkey, get(db.people))) {
filter.push({kinds: [0], authors: [note.pubkey]})
}
await pool.loadEvents(filter)
const replyId = findReply(note)
if (loadParent && replyId) {
await getOrLoadNote(replyId)
}
}
const getOrLoadNote = async id => {
if (!await db.events.get(id)) {
await pool.loadEvents({kinds: [1], ids: [id]})
}
const note = await db.events.get(id)
if (!note) {
return first(await pool.loadEvents({kinds: [1], ids: [id]}))
if (note) {
await loadNoteContext(note, {loadParent: true})
}
return note
@ -254,7 +270,7 @@ export const network = db.network
export const connections = db.connections
export default {
db, pool, lq, buildNoteContextFilter, filterEvents, getOrLoadNote,
filterReplies, findNote, annotateChunk, renderNote, filterAlerts,
login, addRelay, removeRelay, follow, unfollow,
db, pool, lq, filterEvents, getOrLoadNote, filterReplies, findNote,
annotateChunk, renderNote, filterAlerts, login, addRelay, removeRelay,
follow, unfollow, loadNoteContext,
}

View File

@ -37,7 +37,9 @@ class Channel {
const sub = pool.sub({filter, cb: onEvent}, this.name, onEose)
const done = () => {
sub.unsub()
if (this.status === 'busy') {
sub.unsub()
}
this.release()
}
@ -183,6 +185,11 @@ const syncNetwork = async () => {
if (pubkeys.length === 0) {
pubkeys = [
"97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322", // hodlbod
"472f440f29ef996e92a186b8d320ff180c855903882e59d50de1b8bd5669301e", // Marty Bent
"82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2", // Jack
"85080d3bad70ccdcd7f74c29a44f55bb85cbcd3dd0cbb957da1d215bdb931204", // Preston
"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245", // jb55
"c4eabae1be3cf657bc1855ee05e69de9f059cb7a059227168b80b89761cbc4e0", // Jack Mallers
]
}

View File

@ -1,55 +1,119 @@
<script>
import {propEq, uniqBy, prop, sortBy} from 'ramda'
import {onMount, onDestroy} from 'svelte'
import {fly} from 'svelte/transition'
import {now} from 'src/util/misc'
import {findReply} from 'src/util/nostr'
import {ellipsize} from 'hurdak/src/core'
import relay, {user} from 'src/relay'
import {alerts, modal} from 'src/state/app'
import Badge from "src/partials/Badge.svelte"
import Note from 'src/views/Note.svelte'
import relay, {people, user} from 'src/relay'
import {alerts} from 'src/state/app'
import {now, timedelta, createScroller, Cursor, getLastSync} from 'src/util/misc'
import Spinner from "src/partials/Spinner.svelte"
import Note from 'src/partials/Note.svelte'
import Like from 'src/partials/Like.svelte'
const events = relay.lq(async () => {
const alerts = await relay.filterAlerts($user)
const events = await alerts.limit(10).reverse().sortBy('created_at')
let sub
let scroller
let notes
let limit = 0
return events
// Add parent in
.map(e => ({...e, parent: relay.filterEvents({ids: [findReply(e)]}).first()}))
// Only show stuff if it's a direct reply to my note
.filter(e => e.parent?.pubkey === $user.pubkey)
const cursor = new Cursor(
getLastSync('routes/Alerts'),
timedelta(1, 'days')
)
onMount(async () => {
sub = await relay.pool.listenForEvents(
'routes/Alerts',
[{kinds: [1, 7], '#p': [$user.pubkey], since: cursor.since}],
onEvent
)
scroller = createScroller(async () => {
limit += 20
notes = relay.lq(() => loadNotes(limit))
})
})
onDestroy(() => {
sub?.unsub()
scroller?.stop()
})
const onEvent = e => {
if (e.kind === 1) {
relay.loadNoteContext(e)
}
if (e.kind === 7) {
const replyId = findReply(e)
if (replyId) {
relay.getOrLoadNote(replyId)
}
}
}
const loadNotes = async limit => {
const events = await relay.filterAlerts($user, limit + 1)
const notes = await relay.annotateChunk(events.filter(propEq('kind', 1)))
const reactions = await Promise.all(
events
.filter(e => e.kind === 7)
.map(async e => ({
...e,
person: $people[e.pubkey] || {pubkey: e.pubkey},
parent: await relay.findNote(findReply(e)),
}))
)
if (events.length <= limit) {
const [since, until] = cursor.step()
relay.pool.loadEvents(
[{kinds: [1, 7], '#p': [$user.pubkey], since, until}],
onEvent
)
} else {
setTimeout(scroller.check, 300)
}
// Combine likes of a single note
const likesById = {}
const alerts = notes.filter(e => e.pubkey !== $user.pubkey)
for (const reaction of reactions) {
if (!likesById[reaction.parent.id]) {
likesById[reaction.parent.id] = {...reaction.parent, people: []}
}
likesById[reaction.parent.id].people.push(reaction.person)
}
return sortBy(
e => -e.created_at,
uniqBy(prop('id'), alerts.concat(Object.values(likesById)))
)
}
// Clear notification badge
alerts.set({since: now()})
</script>
<ul class="py-4 flex flex-col gap-2 max-w-xl m-auto">
{#each ($events || []) as e (e.id)}
{#if e.kind === 7}
<li
in:fly={{y: 20}}
class="py-2 px-3 flex flex-col gap-2 text-white cursor-pointer transition-all
border border-solid border-black hover:border-medium hover:bg-dark"
on:click={() => modal.set({note: e.parent})}>
<div class="flex gap-2 items-center">
<Badge person={e.person} />
<span>liked your note.</span>
</div>
<div class="ml-6 text-light">
{ellipsize(e.parent.content, 240)}
</div>
</li>
{#each ($notes || []) as e (e.id)}
{#if e.people}
<li in:fly={{y: 20}}><Like note={e} /></li>
{:else}
<li in:fly={{y: 20}}><Note showParent note={e} /></li>
{/if}
{/each}
</ul>
{#if $events?.length === 0}
{#if $notes?.length === 0}
<div in:fly={{y: 20}} class="flex w-full justify-center items-center py-16">
<div class="text-center max-w-md">
No recent activity found.
</div>
</div>
{:else}
<Spinner />
{/if}

View File

@ -1,13 +1,10 @@
<script>
import {onMount, onDestroy} from 'svelte'
import {navigate} from 'svelte-routing'
import {findReply} from 'src/util/nostr'
import Anchor from "src/partials/Anchor.svelte"
import Tabs from "src/partials/Tabs.svelte"
import Network from "src/views/notes/Network.svelte"
import Global from "src/views/notes/Global.svelte"
import {now, timedelta} from 'src/util/misc'
import relay, {connections} from 'src/relay'
import {connections} from 'src/relay'
export let activeTab

View File

@ -1,13 +1,13 @@
<script>
import {find} from 'ramda'
import {find, take, when, propEq} from 'ramda'
import {onMount, onDestroy} from 'svelte'
import {fly} from 'svelte/transition'
import {navigate} from 'svelte-routing'
import {getLastSync} from 'src/util/misc'
import {getTagValues, findReply} from 'src/util/nostr'
import {getTagValues} from 'src/util/nostr'
import Tabs from "src/partials/Tabs.svelte"
import Button from "src/partials/Button.svelte"
import Notes from "src/views/Notes.svelte"
import Notes from "src/partials/Notes.svelte"
import {t, dispatch} from 'src/state/dispatch'
import {modal} from "src/state/app"
import relay, {user, people} from 'src/relay'
@ -23,22 +23,7 @@
sub = await relay.pool.listenForEvents(
'routes/Person',
[{kind: [0, 1, 5, 7], authors: [pubkey], since}],
async e => {
if (e.kind === 1) {
const filter = await relay.buildNoteContextFilter(e, {since})
await relay.pool.loadEvents(filter)
}
if (e.kind === 7) {
const replyId = findReply(e)
if (replyId && !await relay.db.events.get(replyId)) {
await relay.pool.loadEvents({kinds: [1], ids: [replyId]})
}
}
}
when(propEq('kind', 1), relay.loadNoteContext)
)
})
@ -52,23 +37,20 @@
const loadNotes = async limit => {
const filter = {kinds: [1], authors: [pubkey]}
const notes = await relay.filterEvents(filter).reverse().sortBy('created_at')
return relay.annotateChunk(notes.slice(0, limit))
return relay.annotateChunk(take(limit, await relay.filterEvents(filter)))
}
const loadLikes = async limit => {
const filter = {kinds: [7], authors: [pubkey]}
const notes = await relay.filterEvents(filter).reverse().sortBy('created_at')
return relay.annotateChunk(notes.slice(0, limit))
return relay.annotateChunk(take(limit, await relay.filterEvents(filter)))
}
const loadNetwork = async limit => {
const filter = {kinds: [1], authors: getTagValues(getPerson().petnames)}
const notes = await relay.filterEvents(filter).reverse().sortBy('created_at')
return relay.annotateChunk(notes.slice(0, limit))
return relay.annotateChunk(take(limit, await relay.filterEvents(filter)))
}
const setActiveTab = tab => navigate(`/people/${pubkey}/${tab}`)

View File

@ -76,8 +76,14 @@ export const createScroller = loadMore => {
requestAnimationFrame(check)
return () => {
done = true
return {
check: () => {
didLoad = false
check()
},
stop: () => {
done = true
},
}
}

59
src/views/Global.svelte Normal file
View File

@ -0,0 +1,59 @@
<script>
import {onMount, onDestroy} from 'svelte'
import {findReply} from 'src/util/nostr'
import Notes from "src/views/Notes.svelte"
import {now, timedelta, getLastSync} from 'src/util/misc'
import relay, {network} from 'src/relay'
let sub
let since = getLastSync('views/Global')
onMount(async () => {
sub = await subscribe(now())
})
onDestroy(() => {
if (sub) {
sub.unsub()
}
})
const subscribe = until =>
relay.pool.listenForEvents(
'views/Network',
[{kinds: [1, 5, 7], authors: $network, since, until}],
async e => {
if (e.kind === 1) {
const filter = await relay.buildNoteContextFilter(e, {since})
await relay.pool.loadEvents(filter)
}
if (e.kind === 7) {
const replyId = findReply(e)
if (replyId && !await relay.db.events.get(replyId)) {
await relay.pool.loadEvents({kinds: [1], ids: [replyId]})
}
}
}
)
const loadNotes = async limit => {
const filter = {kinds: [1], authors: $network}
const notes = await relay.filterEvents(filter).reverse().sortBy('created_at')
if (notes.length < limit) {
const until = notes.reduce((t, n) => Math.min(n.created_at), since)
since = until - timedelta(1, 'hours')
sub = await subscribe(since)
}
return relay.annotateChunk(notes.slice(0, limit))
}
</script>
<Notes shouldMuffle loadNotes={loadNotes} />

1
src/views/Network.svelte Normal file
View File

@ -0,0 +1 @@

View File

@ -1,8 +1,8 @@
<script>
import {when, propEq} from 'ramda'
import {onMount, onDestroy} from 'svelte'
import relay from 'src/relay'
import {getLastSync} from 'src/util/misc'
import Note from 'src/views/Note.svelte'
import Note from 'src/partials/Note.svelte'
import Spinner from 'src/partials/Spinner.svelte'
export let note
@ -12,10 +12,14 @@
onMount(async () => {
note = await relay.getOrLoadNote(note.id)
// Log this for debugging purposes
console.log('NoteDetail', note)
if (note) {
sub = await relay.pool.listenForEvents(
'routes/NoteDetail',
await relay.buildNoteContextFilter(note)
[{kinds: [1, 5, 7], '#e': [note.id]}],
when(propEq('kind', 1), relay.loadNoteContext)
)
}
})
@ -27,14 +31,7 @@
})
onMount(() => {
observable = relay.lq(async () => {
const details = await relay.findNote(note.id, {showEntire: true})
// Log this for debugging purposes
console.log('NoteDetail', details)
return details
})
observable = relay.lq(() => relay.findNote(note.id, {showEntire: true}))
return () => {
if (sub) {

View File

@ -1,7 +1,8 @@
<script>
import {take} from 'ramda'
import {fly} from 'svelte/transition'
import {fuzzy} from "src/util/misc"
import Note from "src/views/Note.svelte"
import Note from "src/partials/Note.svelte"
import relay from 'src/relay'
export let q
@ -9,8 +10,7 @@
let results = []
const search = relay.lq(async () => {
const notes = await relay.filterEvents({kinds: [1]})
.limit(5000).reverse().sortBy('created_at')
const notes = take(5000, await relay.filterEvents({kinds: [1]}))
return fuzzy(notes, {keys: ["content"]})
})

View File

@ -1,8 +1,8 @@
<script>
import {when, take, propEq} from 'ramda'
import {onMount, onDestroy} from 'svelte'
import {findReply} from 'src/util/nostr'
import Notes from "src/views/Notes.svelte"
import {now, timedelta, Cursor, getLastSync} from 'src/util/misc'
import Notes from "src/partials/Notes.svelte"
import {timedelta, Cursor, getLastSync} from 'src/util/misc'
import relay from 'src/relay'
let sub
@ -12,28 +12,11 @@
timedelta(1, 'minutes')
)
const onEvent = async e => {
if (e.kind === 1) {
const filter = await relay.buildNoteContextFilter(e)
await relay.pool.loadEvents(filter)
}
if (e.kind === 7) {
const replyId = findReply(e)
if (replyId && !await relay.db.events.get(replyId)) {
await relay.pool.loadEvents({kinds: [1], ids: [replyId]})
}
}
}
onMount(async () => {
sub = await relay.pool.listenForEvents(
'views/notes/Global',
[{kinds: [1, 5, 7], since: cursor.since}],
onEvent
when(propEq('kind', 1), relay.loadNoteContext)
)
})
@ -44,12 +27,15 @@
})
const loadNotes = async limit => {
const notes = await relay.filterEvents({kinds: [1]}).reverse().sortBy('created_at')
const notes = take(limit + 1, await relay.filterEvents({kinds: [1]}))
if (notes.length < limit) {
if (notes.length <= limit) {
const [since, until] = cursor.step()
relay.pool.loadEvents([{kinds: [1, 5, 7], since, until}], onEvent)
relay.pool.loadEvents(
[{kinds: [1, 5, 7], since, until}],
when(propEq('kind', 1), relay.loadNoteContext)
)
}
return relay.annotateChunk(notes.slice(0, limit))

View File

@ -1,8 +1,8 @@
<script>
import {when, take, propEq} from 'ramda'
import {onMount, onDestroy} from 'svelte'
import {findReply} from 'src/util/nostr'
import Notes from "src/views/Notes.svelte"
import {now, timedelta, Cursor, getLastSync} from 'src/util/misc'
import Notes from "src/partials/Notes.svelte"
import {timedelta, Cursor, getLastSync} from 'src/util/misc'
import relay, {network} from 'src/relay'
let sub
@ -12,28 +12,11 @@
timedelta(1, 'hours')
)
const onEvent = async e => {
if (e.kind === 1) {
const filter = await relay.buildNoteContextFilter(e)
await relay.pool.loadEvents(filter)
}
if (e.kind === 7) {
const replyId = findReply(e)
if (replyId && !await relay.db.events.get(replyId)) {
await relay.pool.loadEvents({kinds: [1], ids: [replyId]})
}
}
}
onMount(async () => {
sub = await relay.pool.listenForEvents(
'views/notes/Network',
[{kinds: [1, 5, 7], authors: $network, since: cursor.since}],
onEvent
when(propEq('kind', 1), relay.loadNoteContext)
)
})
@ -45,14 +28,14 @@
const loadNotes = async limit => {
const filter = {kinds: [1], authors: $network}
const notes = await relay.filterEvents(filter).reverse().sortBy('created_at')
const notes = take(limit + 1, await relay.filterEvents(filter))
if (notes.length < limit) {
if (notes.length <= limit) {
const [since, until] = cursor.step()
relay.pool.loadEvents(
[{kinds: [1, 5, 7], authors: $network, since, until}],
onEvent
when(propEq('kind', 1), relay.loadNoteContext)
)
}