mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-30 00:41:12 +00:00
Switch from cache-first to cache-last by always attempting to retrieve messages from the network with an aggressive timeout.
This commit is contained in:
parent
de3f75b2a3
commit
7a3338eeaa
@ -36,6 +36,15 @@ If you like Coracle and want to support its development, you can donate sats via
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.2.2
|
||||||
|
|
||||||
|
- [x] Show notification for new notes rather than automatically adding them to the feed
|
||||||
|
- [x] Improve slow relay pruning by using a timeout for each relay
|
||||||
|
- [x] Re-work feed loading - go to network first and fall back to cache to ensure results that are as complete as possible
|
||||||
|
- [x] Slightly improved context fetching to reduce subscriptions
|
||||||
|
- [x] Split person feeds out into separate components
|
||||||
|
- [x] Add timeout in scroller to keep polling for new results
|
||||||
|
|
||||||
## 0.2.1
|
## 0.2.1
|
||||||
|
|
||||||
- [x] Exclude people from search who have no profile data available
|
- [x] Exclude people from search who have no profile data available
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
import {Router, Route, links, navigate} from "svelte-routing"
|
import {Router, Route, links, navigate} from "svelte-routing"
|
||||||
import {globalHistory} from "svelte-routing/src/history"
|
import {globalHistory} from "svelte-routing/src/history"
|
||||||
import {hasParent} from 'src/util/html'
|
import {hasParent} from 'src/util/html'
|
||||||
import {timedelta, getLastSync, now} from 'src/util/misc'
|
import {timedelta, now} from 'src/util/misc'
|
||||||
import {store as toast} from "src/state/toast"
|
import {store as toast} from "src/state/toast"
|
||||||
import {modal, settings, alerts} from "src/state/app"
|
import {modal, settings, alerts} from "src/state/app"
|
||||||
import relay, {user, connections} from 'src/relay'
|
import relay, {user, connections} from 'src/relay'
|
||||||
|
@ -1,32 +1,52 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import {sortBy, uniqBy, reject, prop} from 'ramda'
|
||||||
import {onDestroy} from 'svelte'
|
import {onDestroy} from 'svelte'
|
||||||
|
import {slide} from 'svelte/transition'
|
||||||
|
import {quantify} from 'hurdak/lib/hurdak'
|
||||||
import {createScroller} from 'src/util/misc'
|
import {createScroller} from 'src/util/misc'
|
||||||
|
import {findReply} from 'src/util/nostr'
|
||||||
import Spinner from 'src/partials/Spinner.svelte'
|
import Spinner from 'src/partials/Spinner.svelte'
|
||||||
import Note from "src/partials/Note.svelte"
|
import Note from "src/partials/Note.svelte"
|
||||||
import relay from 'src/relay'
|
import relay from 'src/relay'
|
||||||
|
|
||||||
export let loadNotes
|
export let loadNotes
|
||||||
|
export const addNewNotes = xs => {
|
||||||
|
newNotes = newNotes.concat(xs)
|
||||||
|
}
|
||||||
|
|
||||||
let notes
|
let notes = []
|
||||||
let limit = 0
|
let newNotes = []
|
||||||
|
let newNotesLength = 0
|
||||||
|
|
||||||
|
$: newNotesLength = reject(findReply, newNotes).length
|
||||||
|
|
||||||
const scroller = createScroller(async () => {
|
const scroller = createScroller(async () => {
|
||||||
limit += 20
|
addNotes(await loadNotes())
|
||||||
|
|
||||||
notes = relay.lq(() => loadNotes(limit))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const addNotes = async xs => {
|
||||||
|
const chunk = await relay.annotateChunk(xs)
|
||||||
|
|
||||||
|
notes = sortBy(e => -e.created_at, uniqBy(prop('id'), notes.concat(chunk)))
|
||||||
|
}
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
scroller.stop()
|
scroller.stop()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if notes}
|
|
||||||
<ul class="py-4 flex flex-col gap-2 max-w-xl m-auto">
|
<ul class="py-4 flex flex-col gap-2 max-w-xl m-auto">
|
||||||
{#each ($notes || []) as n (n.id)}
|
{#if newNotesLength > 0}
|
||||||
|
<div
|
||||||
|
transition:slide
|
||||||
|
class="mb-2 cursor-pointer text-center underline text-light"
|
||||||
|
on:click={() => { addNotes(newNotes); newNotes = [] }}>
|
||||||
|
Load {quantify(newNotesLength, 'new note')}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#each notes as n (n.id)}
|
||||||
<li><Note note={n} depth={2} /></li>
|
<li><Note note={n} depth={2} /></li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
{/if}
|
|
||||||
|
|
||||||
<Spinner />
|
<Spinner />
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {liveQuery} from 'dexie'
|
import {liveQuery} from 'dexie'
|
||||||
import extractUrls from 'extract-urls'
|
import extractUrls from 'extract-urls'
|
||||||
import {get} from 'svelte/store'
|
import {get} from 'svelte/store'
|
||||||
import {intersection, pluck, sortBy, uniq, uniqBy, groupBy, concat, without, prop, isNil, identity} from 'ramda'
|
import {intersection, find, sortBy, propEq, uniqBy, groupBy, concat, without, prop, isNil, identity} from 'ramda'
|
||||||
import {ensurePlural, first, createMap, ellipsize} from 'hurdak/lib/hurdak'
|
import {ensurePlural, first, createMap, ellipsize} from 'hurdak/lib/hurdak'
|
||||||
import {escapeHtml} from 'src/util/html'
|
import {escapeHtml} from 'src/util/html'
|
||||||
import {filterTags, getTagValues, findReply, findRoot} from 'src/util/nostr'
|
import {filterTags, getTagValues, findReply, findRoot} from 'src/util/nostr'
|
||||||
@ -103,7 +103,7 @@ const findNote = async (id, {showEntire = false, depth = 1} = {}) => {
|
|||||||
? []
|
? []
|
||||||
: await Promise.all(
|
: await Promise.all(
|
||||||
sortBy(e => -e.created_at, replies)
|
sortBy(e => -e.created_at, replies)
|
||||||
.slice(0, showEntire ? Infinity : 5)
|
.slice(0, showEntire ? Infinity : 3)
|
||||||
.map(r => findNote(r.id, {depth: depth - 1}))
|
.map(r => findNote(r.id, {depth: depth - 1}))
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -134,10 +134,12 @@ const annotateChunk = async chunk => {
|
|||||||
allNotes
|
allNotes
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const notes = await Promise.all(Object.keys(notesByRoot).map(findNote))
|
||||||
|
|
||||||
// Re-sort, since events come in order regardless of level in the hierarchy.
|
// Re-sort, since events come in order regardless of level in the hierarchy.
|
||||||
// This is really a hack, since a single like can bump an old note back up to the
|
// This is really a hack, since a single like can bump an old note back up to the
|
||||||
// top of the feed
|
// top of the feed. Also, discard non-notes (e.g. reactions)
|
||||||
return sortBy(e => -e.created_at, await Promise.all(Object.keys(notesByRoot).map(findNote)))
|
return sortBy(e => -e.created_at, notes.filter(propEq('kind', 1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderNote = async (note, {showEntire = false}) => {
|
const renderNote = async (note, {showEntire = false}) => {
|
||||||
@ -218,27 +220,27 @@ const unfollow = async pubkey => {
|
|||||||
// This is intended only for bootstrapping listeners
|
// This is intended only for bootstrapping listeners
|
||||||
|
|
||||||
const loadNoteContext = async (note, {loadParent = false} = {}) => {
|
const loadNoteContext = async (note, {loadParent = false} = {}) => {
|
||||||
// Load note context - this assumes that we are looking at a feed, and so
|
const $people = get(people)
|
||||||
// we already have the note's parent and its likes loaded.
|
|
||||||
const filter = [{kinds: [1, 5, 7], '#e': [note.id]}]
|
const filter = [{kinds: [1, 5, 7], '#e': [note.id]}]
|
||||||
|
|
||||||
if (!prop(note.pubkey, get(db.people))) {
|
// Load the author if needed
|
||||||
|
if (!$people[note.pubkey]) {
|
||||||
filter.push({kinds: [0], authors: [note.pubkey]})
|
filter.push({kinds: [0], authors: [note.pubkey]})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the events
|
// Load the note's parent
|
||||||
|
const parentId = findReply(note)
|
||||||
|
if (loadParent && parentId) {
|
||||||
|
filter.push({kinds: [1], ids: [parentId]})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the events
|
||||||
const events = await pool.loadEvents(filter)
|
const events = await pool.loadEvents(filter)
|
||||||
|
|
||||||
// Load any related people we're missing
|
// Load the note's context as well
|
||||||
const $people = get(people)
|
const parent = find(propEq('id', parentId), events)
|
||||||
await pool.loadPeople(
|
if (loadParent && parent) {
|
||||||
uniq(pluck('pubkey', events)).filter(k => !$people[k])
|
await loadNoteContext(parent)
|
||||||
)
|
|
||||||
|
|
||||||
// Load the note's parent
|
|
||||||
const replyId = findReply(note)
|
|
||||||
if (loadParent && replyId) {
|
|
||||||
await getOrLoadNote(replyId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,8 +36,18 @@ class Channel {
|
|||||||
// Start our subscription, wait for only our fastest relays to eose before calling it done.
|
// Start our subscription, wait for only our fastest relays to eose before calling it done.
|
||||||
// We were waiting for all before, but that made the slowest relay a bottleneck. Waiting for
|
// We were waiting for all before, but that made the slowest relay a bottleneck. Waiting for
|
||||||
// only one meant we might be settling for very incomplete data
|
// only one meant we might be settling for very incomplete data
|
||||||
|
const start = new Date().valueOf()
|
||||||
|
const lastEvent = {}
|
||||||
const eoseRelays = []
|
const eoseRelays = []
|
||||||
const sub = pool.sub({filter, cb: onEvent}, this.name, r => {
|
|
||||||
|
// Keep track of when we last heard from each relay, and close unresponsive ones
|
||||||
|
const cb = (e, r) => {
|
||||||
|
lastEvent[r] = new Date().valueOf()
|
||||||
|
onEvent(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have lots of relays, ignore the slowest ones
|
||||||
|
const onRelayEose = r => {
|
||||||
eoseRelays.push(r)
|
eoseRelays.push(r)
|
||||||
|
|
||||||
// If we have only a few, wait for all of them, otherwise ignore the slowest 1/5
|
// If we have only a few, wait for all of them, otherwise ignore the slowest 1/5
|
||||||
@ -45,21 +55,30 @@ class Channel {
|
|||||||
if (eoseRelays.length >= relays.length - threshold) {
|
if (eoseRelays.length >= relays.length - threshold) {
|
||||||
onEose()
|
onEose()
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// Create our subscription
|
||||||
|
const sub = pool.sub({filter, cb}, this.name, onRelayEose)
|
||||||
|
|
||||||
|
// Watch for relays that are slow to respond and give up on them
|
||||||
|
const interval = !opts.timeout ? null : setInterval(() => {
|
||||||
|
for (const r of relays) {
|
||||||
|
if ((lastEvent[r] || start) < new Date().valueOf() - opts.timeout) {
|
||||||
|
onRelayEose(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
|
|
||||||
|
// Clean everything up when we're done
|
||||||
const done = () => {
|
const done = () => {
|
||||||
if (this.status === 'busy') {
|
if (this.status === 'busy') {
|
||||||
sub.unsub()
|
sub.unsub()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearInterval(interval)
|
||||||
this.release()
|
this.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the relay takes to long, just give up
|
|
||||||
if (opts.timeout) {
|
|
||||||
setTimeout(done, opts.timeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {unsub: done}
|
return {unsub: done}
|
||||||
}
|
}
|
||||||
all(filter, opts = {}) {
|
all(filter, opts = {}) {
|
||||||
@ -75,7 +94,7 @@ class Channel {
|
|||||||
|
|
||||||
resolve(uniqBy(prop('id'), result))
|
resolve(uniqBy(prop('id'), result))
|
||||||
},
|
},
|
||||||
{timeout: 30000, ...opts},
|
{timeout: 3000, ...opts},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -159,6 +178,8 @@ const listenForEvents = async (key, filter, onEvent) => {
|
|||||||
onEvent(e)
|
onEvent(e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return listenForEvents.subs[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
listenForEvents.subs = {}
|
listenForEvents.subs = {}
|
||||||
@ -177,7 +198,7 @@ const syncNetwork = async () => {
|
|||||||
let pubkeys = []
|
let pubkeys = []
|
||||||
if ($user) {
|
if ($user) {
|
||||||
// Get this user's profile to start with
|
// Get this user's profile to start with
|
||||||
await loadPeople([$user.pubkey], {timeout: null})
|
await loadPeople([$user.pubkey])
|
||||||
|
|
||||||
// Get our refreshed person
|
// Get our refreshed person
|
||||||
const people = get(db.people)
|
const people = get(db.people)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import {alerts} from 'src/state/app'
|
import {alerts} from 'src/state/app'
|
||||||
import {findReply} from 'src/util/nostr'
|
import {findReply} from 'src/util/nostr'
|
||||||
import relay, {people, user} from 'src/relay'
|
import relay, {people, user} from 'src/relay'
|
||||||
import {now, timedelta, createScroller, Cursor, getLastSync} from 'src/util/misc'
|
import {now, timedelta, createScroller, Cursor} from 'src/util/misc'
|
||||||
import Spinner from "src/partials/Spinner.svelte"
|
import Spinner from "src/partials/Spinner.svelte"
|
||||||
import Note from 'src/partials/Note.svelte'
|
import Note from 'src/partials/Note.svelte'
|
||||||
import Like from 'src/partials/Like.svelte'
|
import Like from 'src/partials/Like.svelte'
|
||||||
@ -15,10 +15,7 @@
|
|||||||
let notes
|
let notes
|
||||||
let limit = 0
|
let limit = 0
|
||||||
|
|
||||||
const cursor = new Cursor(
|
const cursor = new Cursor(timedelta(1, 'hours'))
|
||||||
getLastSync('routes/Alerts'),
|
|
||||||
timedelta(1, 'days')
|
|
||||||
)
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
sub = await relay.pool.listenForEvents(
|
sub = await relay.pool.listenForEvents(
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
<script>
|
<script>
|
||||||
import {find, take, when, propEq} from 'ramda'
|
import {find, when, propEq} from 'ramda'
|
||||||
import {onMount, onDestroy} from 'svelte'
|
import {onMount, onDestroy} from 'svelte'
|
||||||
import {fly} from 'svelte/transition'
|
import {fly} from 'svelte/transition'
|
||||||
import {navigate} from 'svelte-routing'
|
import {navigate} from 'svelte-routing'
|
||||||
import {getLastSync} from 'src/util/misc'
|
import {now, timedelta} from 'src/util/misc'
|
||||||
import {getTagValues} from 'src/util/nostr'
|
|
||||||
import Tabs from "src/partials/Tabs.svelte"
|
import Tabs from "src/partials/Tabs.svelte"
|
||||||
import Button from "src/partials/Button.svelte"
|
import Button from "src/partials/Button.svelte"
|
||||||
import Notes from "src/partials/Notes.svelte"
|
import Notes from "src/views/person/Notes.svelte"
|
||||||
|
import Likes from "src/views/person/Likes.svelte"
|
||||||
|
import Network from "src/views/person/Network.svelte"
|
||||||
import {modal} from "src/state/app"
|
import {modal} from "src/state/app"
|
||||||
import relay, {user, people} from 'src/relay'
|
import relay, {user, people} from 'src/relay'
|
||||||
|
|
||||||
@ -16,12 +17,11 @@
|
|||||||
|
|
||||||
let sub = null
|
let sub = null
|
||||||
let following = $user && find(t => t[1] === pubkey, $user.petnames)
|
let following = $user && find(t => t[1] === pubkey, $user.petnames)
|
||||||
let since = getLastSync(['Person', pubkey])
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
sub = await relay.pool.listenForEvents(
|
sub = await relay.pool.listenForEvents(
|
||||||
'routes/Person',
|
'routes/Person',
|
||||||
[{kind: [0, 1, 5, 7], authors: [pubkey], since}],
|
[{kinds: [0, 1, 5, 7], authors: [pubkey], since: now()}],
|
||||||
when(propEq('kind', 1), relay.loadNoteContext)
|
when(propEq('kind', 1), relay.loadNoteContext)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -34,32 +34,6 @@
|
|||||||
|
|
||||||
const getPerson = () => $people[pubkey]
|
const getPerson = () => $people[pubkey]
|
||||||
|
|
||||||
const loadNotes = async limit => {
|
|
||||||
const filter = {kinds: [1], authors: [pubkey]}
|
|
||||||
|
|
||||||
return relay.annotateChunk(take(limit, await relay.filterEvents(filter)))
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadLikes = async limit => {
|
|
||||||
const events = await relay.annotateChunk(
|
|
||||||
take(limit, await relay.filterEvents({
|
|
||||||
kinds: [7],
|
|
||||||
authors: [pubkey],
|
|
||||||
muffle: getTagValues($user?.muffle || []),
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
|
|
||||||
return events.filter(e => e.kind === 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadNetwork = async limit => {
|
|
||||||
return relay.annotateChunk(take(limit, await relay.filterEvents({
|
|
||||||
kinds: [1],
|
|
||||||
authors: getTagValues(getPerson().petnames),
|
|
||||||
muffle: getTagValues($user?.muffle || []),
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
const setActiveTab = tab => navigate(`/people/${pubkey}/${tab}`)
|
const setActiveTab = tab => navigate(`/people/${pubkey}/${tab}`)
|
||||||
|
|
||||||
const follow = () => {
|
const follow = () => {
|
||||||
@ -115,12 +89,12 @@
|
|||||||
|
|
||||||
<Tabs tabs={['notes', 'likes', 'network']} {activeTab} {setActiveTab} />
|
<Tabs tabs={['notes', 'likes', 'network']} {activeTab} {setActiveTab} />
|
||||||
{#if activeTab === 'notes'}
|
{#if activeTab === 'notes'}
|
||||||
<Notes loadNotes={loadNotes} />
|
<Notes {pubkey} />
|
||||||
{:else if activeTab === 'likes'}
|
{:else if activeTab === 'likes'}
|
||||||
<Notes loadNotes={loadLikes} />
|
<Likes {pubkey} />
|
||||||
{:else if activeTab === 'network'}
|
{:else if activeTab === 'network'}
|
||||||
{#if getPerson()}
|
{#if getPerson()}
|
||||||
<Notes shouldMuffle loadNotes={loadNetwork} />
|
<Network person={getPerson()} />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="py-16 max-w-xl m-auto flex justify-center">
|
<div class="py-16 max-w-xl m-auto flex justify-center">
|
||||||
Unable to show network for this person.
|
Unable to show network for this person.
|
||||||
|
@ -77,7 +77,9 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
<small class="text-center">Found {($knownRelays || []).length} known relays</small>
|
<small class="text-center">
|
||||||
|
Showing {Math.min(($knownRelays || []).length, 50)} of {($knownRelays || []).length} known relays
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import {first} from 'hurdak/lib/hurdak'
|
|
||||||
|
|
||||||
export const copyToClipboard = text => {
|
export const copyToClipboard = text => {
|
||||||
const {activeElement} = document
|
const {activeElement} = document
|
||||||
const input = document.createElement("textarea")
|
const input = document.createElement("textarea")
|
||||||
|
@ -53,6 +53,7 @@ export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|||||||
export const createScroller = loadMore => {
|
export const createScroller = loadMore => {
|
||||||
let done = false
|
let done = false
|
||||||
let didLoad = false
|
let didLoad = false
|
||||||
|
let timeout = null
|
||||||
const check = async () => {
|
const check = async () => {
|
||||||
// While we have empty space, fill it
|
// While we have empty space, fill it
|
||||||
const {scrollY, innerHeight} = window
|
const {scrollY, innerHeight} = window
|
||||||
@ -61,7 +62,15 @@ export const createScroller = loadMore => {
|
|||||||
|
|
||||||
// Only trigger loading the first time we reach the threshhold
|
// Only trigger loading the first time we reach the threshhold
|
||||||
if (shouldLoad && !didLoad) {
|
if (shouldLoad && !didLoad) {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
|
||||||
await loadMore()
|
await loadMore()
|
||||||
|
|
||||||
|
// If nothing loads, the page doesn't reflow and we get stuck.
|
||||||
|
// Give it a generous timeout from last time something did load
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
didLoad = false
|
||||||
|
}, 5000)
|
||||||
}
|
}
|
||||||
|
|
||||||
didLoad = shouldLoad
|
didLoad = shouldLoad
|
||||||
@ -72,6 +81,7 @@ export const createScroller = loadMore => {
|
|||||||
if (!done) {
|
if (!done) {
|
||||||
requestAnimationFrame(check)
|
requestAnimationFrame(check)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(check)
|
requestAnimationFrame(check)
|
||||||
@ -99,14 +109,15 @@ export const getLastSync = (k, fallback = 0) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Cursor {
|
export class Cursor {
|
||||||
constructor(since, delta) {
|
constructor(delta) {
|
||||||
this.since = (since || now()) - delta,
|
this.since = now()
|
||||||
|
this.until = now()
|
||||||
this.delta = delta
|
this.delta = delta
|
||||||
}
|
}
|
||||||
step() {
|
step() {
|
||||||
const until = this.since
|
this.until = this.since
|
||||||
this.since -= this.delta
|
this.since -= this.delta
|
||||||
|
|
||||||
return [this.since, until]
|
return [this.since, this.until]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if !note}
|
{#if !note}
|
||||||
<div class="text-white" in:fly={{y: 20}}>
|
<div class="p-4 text-center text-white" in:fly={{y: 20}}>
|
||||||
Sorry, we weren't able to find this note.
|
Sorry, we weren't able to find this note.
|
||||||
</div>
|
</div>
|
||||||
{:else if $observable}
|
{:else if $observable}
|
||||||
|
@ -2,19 +2,21 @@
|
|||||||
import {when, propEq} from 'ramda'
|
import {when, propEq} from 'ramda'
|
||||||
import {onMount, onDestroy} from 'svelte'
|
import {onMount, onDestroy} from 'svelte'
|
||||||
import Notes from "src/partials/Notes.svelte"
|
import Notes from "src/partials/Notes.svelte"
|
||||||
import {timedelta, now, Cursor} from 'src/util/misc'
|
import {timedelta, Cursor} from 'src/util/misc'
|
||||||
import {getTagValues} from 'src/util/nostr'
|
import {getTagValues} from 'src/util/nostr'
|
||||||
import relay, {user} from 'src/relay'
|
import relay, {user} from 'src/relay'
|
||||||
|
|
||||||
let sub
|
let notes, sub
|
||||||
|
|
||||||
const cursor = new Cursor(now(), timedelta(1, 'minutes'))
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
sub = await relay.pool.listenForEvents(
|
sub = await relay.pool.listenForEvents(
|
||||||
'views/notes/Global',
|
'views/notes/Global',
|
||||||
[{kinds: [1, 5, 7], since: cursor.since}],
|
[{kinds: [1, 5, 7], since: cursor.since}],
|
||||||
when(propEq('kind', 1), relay.loadNoteContext)
|
when(propEq('kind', 1), async e => {
|
||||||
|
await relay.loadNoteContext(e)
|
||||||
|
|
||||||
|
notes.addNewNotes([e])
|
||||||
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -24,24 +26,23 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const loadNotes = async limit => {
|
const cursor = new Cursor(timedelta(1, 'minutes'))
|
||||||
const notes = await relay.filterEvents({
|
|
||||||
limit,
|
const loadNotes = async () => {
|
||||||
|
const [since, until] = cursor.step()
|
||||||
|
|
||||||
|
await relay.pool.loadEvents(
|
||||||
|
[{kinds: [1, 5, 7], since, until}],
|
||||||
|
when(propEq('kind', 1), relay.loadNoteContext)
|
||||||
|
)
|
||||||
|
|
||||||
|
return relay.filterEvents({
|
||||||
|
since,
|
||||||
|
until,
|
||||||
kinds: [1],
|
kinds: [1],
|
||||||
muffle: getTagValues($user?.muffle || []),
|
muffle: getTagValues($user?.muffle || []),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (notes.length <= limit) {
|
|
||||||
const [since, until] = cursor.step()
|
|
||||||
|
|
||||||
relay.pool.loadEvents(
|
|
||||||
[{kinds: [1, 5, 7], since, until}],
|
|
||||||
when(propEq('kind', 1), relay.loadNoteContext)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return relay.annotateChunk(notes.slice(0, limit))
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Notes shouldMuffle loadNotes={loadNotes} />
|
<Notes bind:this={notes} shouldMuffle {loadNotes} />
|
||||||
|
@ -2,17 +2,11 @@
|
|||||||
import {when, propEq} from 'ramda'
|
import {when, propEq} from 'ramda'
|
||||||
import {onMount, onDestroy} from 'svelte'
|
import {onMount, onDestroy} from 'svelte'
|
||||||
import Notes from "src/partials/Notes.svelte"
|
import Notes from "src/partials/Notes.svelte"
|
||||||
import {timedelta, Cursor, getLastSync} from 'src/util/misc'
|
import {timedelta, Cursor} from 'src/util/misc'
|
||||||
import {getTagValues} from 'src/util/nostr'
|
import {getTagValues} from 'src/util/nostr'
|
||||||
import relay, {user, network} from 'src/relay'
|
import relay, {user, network} from 'src/relay'
|
||||||
|
|
||||||
let sub
|
let notes, sub, networkUnsub
|
||||||
let networkUnsub
|
|
||||||
|
|
||||||
const cursor = new Cursor(
|
|
||||||
getLastSync('views/notes/Network'),
|
|
||||||
timedelta(1, 'hours')
|
|
||||||
)
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// We need to re-create the sub when network changes, since this is where
|
// We need to re-create the sub when network changes, since this is where
|
||||||
@ -22,7 +16,11 @@
|
|||||||
sub = await relay.pool.listenForEvents(
|
sub = await relay.pool.listenForEvents(
|
||||||
'views/notes/Network',
|
'views/notes/Network',
|
||||||
[{kinds: [1, 5, 7], authors: $network, since: cursor.since}],
|
[{kinds: [1, 5, 7], authors: $network, since: cursor.since}],
|
||||||
when(propEq('kind', 1), relay.loadNoteContext)
|
when(propEq('kind', 1), async e => {
|
||||||
|
await relay.loadNoteContext(e)
|
||||||
|
|
||||||
|
notes.addNewNotes([e])
|
||||||
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -35,28 +33,25 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const loadNotes = async limit => {
|
const cursor = new Cursor(timedelta(10, 'minutes'))
|
||||||
const notes = await relay.filterEvents({
|
|
||||||
limit,
|
const loadNotes = async () => {
|
||||||
|
const [since, until] = cursor.step()
|
||||||
|
|
||||||
|
await relay.pool.loadEvents(
|
||||||
|
[{kinds: [1, 5, 7], authors: $network, since, until}],
|
||||||
|
when(propEq('kind', 1), relay.loadNoteContext)
|
||||||
|
)
|
||||||
|
|
||||||
|
return relay.filterEvents({
|
||||||
|
since,
|
||||||
|
until,
|
||||||
kinds: [1],
|
kinds: [1],
|
||||||
authors: $network.concat($user.pubkey),
|
authors: $network.concat($user.pubkey),
|
||||||
muffle: getTagValues($user?.muffle || []),
|
muffle: getTagValues($user?.muffle || []),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (notes.length <= limit) {
|
|
||||||
const [since, until] = cursor.step()
|
|
||||||
|
|
||||||
relay.pool.loadEvents(
|
|
||||||
[{kinds: [1, 5, 7], authors: $network, since, until}],
|
|
||||||
when(propEq('kind', 1), relay.loadNoteContext)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return relay.annotateChunk(notes.slice(0, limit))
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- hack to reload notes when our network initiall loads, see onMount -->
|
<Notes bind:this={notes} shouldMuffle {loadNotes} />
|
||||||
{#key $network.map(n => n[0]).join('')}
|
|
||||||
<Notes shouldMuffle loadNotes={loadNotes} />
|
|
||||||
{/key}
|
|
||||||
|
23
src/views/person/Likes.svelte
Normal file
23
src/views/person/Likes.svelte
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<script>
|
||||||
|
import Notes from "src/partials/Notes.svelte"
|
||||||
|
import {timedelta, Cursor} from 'src/util/misc'
|
||||||
|
import {getTagValues} from 'src/util/nostr'
|
||||||
|
import relay, {user} from 'src/relay'
|
||||||
|
|
||||||
|
export let pubkey
|
||||||
|
|
||||||
|
const cursor = new Cursor(timedelta(1, 'days'))
|
||||||
|
|
||||||
|
const loadNotes = async () => {
|
||||||
|
const [since, until] = cursor.step()
|
||||||
|
const filter = {kinds: [7], authors: [pubkey], since, until}
|
||||||
|
const muffle = getTagValues($user?.muffle || [])
|
||||||
|
|
||||||
|
await relay.pool.loadEvents(filter)
|
||||||
|
|
||||||
|
return relay.filterEvents({...filter, muffle})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Notes shouldMuffle {loadNotes} />
|
||||||
|
|
24
src/views/person/Network.svelte
Normal file
24
src/views/person/Network.svelte
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script>
|
||||||
|
import Notes from "src/partials/Notes.svelte"
|
||||||
|
import {timedelta, Cursor} from 'src/util/misc'
|
||||||
|
import {getTagValues} from 'src/util/nostr'
|
||||||
|
import relay, {user} from 'src/relay'
|
||||||
|
|
||||||
|
export let person
|
||||||
|
|
||||||
|
const cursor = new Cursor(timedelta(1, 'hours'))
|
||||||
|
|
||||||
|
const loadNotes = async () => {
|
||||||
|
const [since, until] = cursor.step()
|
||||||
|
const authors = getTagValues(person.petnames)
|
||||||
|
const filter = {kinds: [1], authors, since, until}
|
||||||
|
const muffle = getTagValues($user?.muffle || [])
|
||||||
|
|
||||||
|
await relay.pool.loadEvents(filter)
|
||||||
|
|
||||||
|
return relay.filterEvents({...filter, muffle})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Notes shouldMuffle {loadNotes} />
|
||||||
|
|
21
src/views/person/Notes.svelte
Normal file
21
src/views/person/Notes.svelte
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<script>
|
||||||
|
import Notes from "src/partials/Notes.svelte"
|
||||||
|
import {timedelta, Cursor} from 'src/util/misc'
|
||||||
|
import relay from 'src/relay'
|
||||||
|
|
||||||
|
export let pubkey
|
||||||
|
|
||||||
|
const cursor = new Cursor(timedelta(1, 'days'))
|
||||||
|
|
||||||
|
const loadNotes = async () => {
|
||||||
|
const [since, until] = cursor.step()
|
||||||
|
const filter = {kinds: [1], authors: [pubkey], since, until}
|
||||||
|
|
||||||
|
await relay.pool.loadEvents(filter, relay.loadNoteContext)
|
||||||
|
|
||||||
|
return relay.filterEvents(filter)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Notes shouldMuffle {loadNotes} />
|
||||||
|
|
Loading…
Reference in New Issue
Block a user