mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-30 00:41:12 +00:00
Fix scrolling bug, figure out pattern for listen/load
This commit is contained in:
parent
b38d502034
commit
7ed121f560
@ -40,9 +40,9 @@ Coracle is currently in _alpha_ - expect bugs, slow loading times, and rough edg
|
|||||||
# Current update
|
# Current update
|
||||||
|
|
||||||
- [ ] Re-implement muffle
|
- [ ] Re-implement muffle
|
||||||
|
- Don't store muffled events, when muffle changes delete them
|
||||||
- [ ] Delete old events
|
- [ ] Delete old events
|
||||||
- [ ] Sync account updates to user for e.g. muffle settings
|
- [ ] Sync account updates to user for e.g. muffle settings
|
||||||
- [ ] Test nos2x
|
|
||||||
- [ ] Make sure login/out, no user usage works
|
- [ ] Make sure login/out, no user usage works
|
||||||
- [ ] Add a re-sync/clear cache button
|
- [ ] Add a re-sync/clear cache button
|
||||||
- https://vitejs.dev/guide/features.html#web-workers
|
- https://vitejs.dev/guide/features.html#web-workers
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
import {hasParent} from 'src/util/html'
|
import {hasParent} from 'src/util/html'
|
||||||
import {timedelta} from 'src/util/misc'
|
import {timedelta} from 'src/util/misc'
|
||||||
import {store as toast} from "src/state/toast"
|
import {store as toast} from "src/state/toast"
|
||||||
import {modal, alerts} from "src/state/app"
|
import {modal, alerts, settings} from "src/state/app"
|
||||||
import relay, {user, connections} from 'src/relay'
|
import relay, {user, connections} from 'src/relay'
|
||||||
import Anchor from 'src/partials/Anchor.svelte'
|
import Anchor from 'src/partials/Anchor.svelte'
|
||||||
import NoteDetail from "src/views/NoteDetail.svelte"
|
import NoteDetail from "src/views/NoteDetail.svelte"
|
||||||
@ -53,6 +53,7 @@
|
|||||||
// Give any animations a moment to finish
|
// Give any animations a moment to finish
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const $connections = get(connections)
|
const $connections = get(connections)
|
||||||
|
const $settings = get(settings)
|
||||||
|
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
|
|
||||||
@ -60,8 +61,9 @@
|
|||||||
relay.db.events.clear()
|
relay.db.events.clear()
|
||||||
relay.db.tags.clear()
|
relay.db.tags.clear()
|
||||||
|
|
||||||
// Remember the user's relay selection
|
// Remember the user's relay selection and settings
|
||||||
connections.set($connections)
|
connections.set($connections)
|
||||||
|
settings.set($settings)
|
||||||
|
|
||||||
// Do a hard refresh so everything gets totally cleared
|
// Do a hard refresh so everything gets totally cleared
|
||||||
window.location = '/login'
|
window.location = '/login'
|
||||||
|
@ -8,6 +8,7 @@ import {db} from 'src/relay/db'
|
|||||||
import pool from 'src/relay/pool'
|
import pool from 'src/relay/pool'
|
||||||
|
|
||||||
// Livequery appears to swallow errors
|
// Livequery appears to swallow errors
|
||||||
|
|
||||||
const lq = f => liveQuery(async () => {
|
const lq = f => liveQuery(async () => {
|
||||||
try {
|
try {
|
||||||
return await f()
|
return await f()
|
||||||
@ -31,36 +32,7 @@ export const buildNoteContextFilter = async (note, extra = {}) => {
|
|||||||
return filter
|
return filter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context getters attempt to retrieve from the db and fall back to the network
|
// Utils for querying dexie - these return collections, not arrays
|
||||||
|
|
||||||
const ensurePerson = async ({pubkey}) => {
|
|
||||||
await pool.syncPersonInfo({...prop(pubkey, get(db.people)), pubkey})
|
|
||||||
}
|
|
||||||
|
|
||||||
const ensureContext = async events => {
|
|
||||||
const promises = []
|
|
||||||
const people = uniq(pluck('pubkey', events)).map(objOf('pubkey'))
|
|
||||||
const ids = events.flatMap(e => filterTags({tag: "e"}, e).concat(e.id))
|
|
||||||
|
|
||||||
if (people.length > 0) {
|
|
||||||
for (const p of people.map(ensurePerson)) {
|
|
||||||
promises.push(p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ids.length > 0) {
|
|
||||||
promises.push(
|
|
||||||
pool.loadEvents([
|
|
||||||
{kinds: [1, 5, 7], '#e': ids},
|
|
||||||
{kinds: [1, 5], ids},
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(promises)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utils for qurying dexie
|
|
||||||
|
|
||||||
const prefilterEvents = filter => {
|
const prefilterEvents = filter => {
|
||||||
if (filter.ids) {
|
if (filter.ids) {
|
||||||
@ -78,7 +50,7 @@ const prefilterEvents = filter => {
|
|||||||
return db.events
|
return db.events
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utils for filtering db
|
// Utils for filtering db - nothing below should load events from the network
|
||||||
|
|
||||||
const filterEvents = filter => {
|
const filterEvents = filter => {
|
||||||
return prefilterEvents(filter)
|
return prefilterEvents(filter)
|
||||||
@ -110,22 +82,6 @@ const filterReactions = async (id, filter) => {
|
|||||||
return reactions
|
return reactions
|
||||||
}
|
}
|
||||||
|
|
||||||
const findReaction = async (id, filter) =>
|
|
||||||
first(await filterReactions(id, filter))
|
|
||||||
|
|
||||||
const countReactions = async (id, filter) =>
|
|
||||||
(await filterReactions(id, filter)).length
|
|
||||||
|
|
||||||
const getOrLoadNote = async (id, {showEntire = false} = {}) => {
|
|
||||||
const note = await db.events.get(id)
|
|
||||||
|
|
||||||
if (!note) {
|
|
||||||
return first(await pool.loadEvents({kinds: [1], ids: [id]}))
|
|
||||||
}
|
|
||||||
|
|
||||||
return note
|
|
||||||
}
|
|
||||||
|
|
||||||
const findNote = async (id, {showEntire = false} = {}) => {
|
const findNote = async (id, {showEntire = false} = {}) => {
|
||||||
const note = await db.events.get(id)
|
const note = await db.events.get(id)
|
||||||
|
|
||||||
@ -251,6 +207,19 @@ const unfollow = async pubkey => {
|
|||||||
db.network.update($network => $network.concat(pubkey))
|
db.network.update($network => $network.concat(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 note = await db.events.get(id)
|
||||||
|
|
||||||
|
if (!note) {
|
||||||
|
return first(await pool.loadEvents({kinds: [1], ids: [id]}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return note
|
||||||
|
}
|
||||||
|
|
||||||
// Initialization
|
// Initialization
|
||||||
|
|
||||||
db.user.subscribe($user => {
|
db.user.subscribe($user => {
|
||||||
@ -277,14 +246,15 @@ db.connections.subscribe($connections => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Export stores on their own for convenience
|
||||||
|
|
||||||
export const user = db.user
|
export const user = db.user
|
||||||
export const people = db.people
|
export const people = db.people
|
||||||
export const network = db.network
|
export const network = db.network
|
||||||
export const connections = db.connections
|
export const connections = db.connections
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
db, pool, lq, buildNoteContextFilter, ensurePerson, ensureContext, filterEvents,
|
db, pool, lq, buildNoteContextFilter, filterEvents, getOrLoadNote,
|
||||||
filterReactions, getOrLoadNote,
|
filterReplies, findNote, annotateChunk, renderNote, filterAlerts,
|
||||||
countReactions, findReaction, filterReplies, findNote, annotateChunk, renderNote,
|
login, addRelay, removeRelay, follow, unfollow,
|
||||||
filterAlerts, login, addRelay, removeRelay, follow, unfollow,
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import {uniqBy, prop, uniq} from 'ramda'
|
import {uniqBy, find, propEq, prop, uniq} from 'ramda'
|
||||||
import {get} from 'svelte/store'
|
import {get} from 'svelte/store'
|
||||||
import {relayPool, getPublicKey} from 'nostr-tools'
|
import {relayPool, getPublicKey} from 'nostr-tools'
|
||||||
import {noop, range} from 'hurdak/lib/hurdak'
|
import {noop, range, sleep} from 'hurdak/lib/hurdak'
|
||||||
import {now, timedelta, randomChoice, getLocalJson, setLocalJson} from "src/util/misc"
|
|
||||||
import {getTagValues, filterTags} from "src/util/nostr"
|
import {getTagValues, filterTags} from "src/util/nostr"
|
||||||
import {db} from 'src/relay/db'
|
import {db} from 'src/relay/db'
|
||||||
|
|
||||||
@ -14,9 +13,15 @@ const pool = relayPool()
|
|||||||
class Channel {
|
class Channel {
|
||||||
constructor(name) {
|
constructor(name) {
|
||||||
this.name = name
|
this.name = name
|
||||||
this.p = Promise.resolve()
|
this.status = 'idle'
|
||||||
}
|
}
|
||||||
async sub(filter, onEvent, onEose = noop, timeout = 30000) {
|
claim() {
|
||||||
|
this.status = 'busy'
|
||||||
|
}
|
||||||
|
release() {
|
||||||
|
this.status = 'idle'
|
||||||
|
}
|
||||||
|
sub(filter, onEvent, onEose = noop, opts = {}) {
|
||||||
// If we don't have any relays, we'll wait forever for an eose, but
|
// If we don't have any relays, we'll wait forever for an eose, but
|
||||||
// we already know we're done. Use a timeout since callers are
|
// we already know we're done. Use a timeout since callers are
|
||||||
// expecting this to be async and we run into errors otherwise.
|
// expecting this to be async and we run into errors otherwise.
|
||||||
@ -26,17 +31,6 @@ class Channel {
|
|||||||
return {unsub: noop}
|
return {unsub: noop}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab our spot in the queue, save resolve for later
|
|
||||||
let resolve
|
|
||||||
let p = this.p
|
|
||||||
this.p = new Promise(r => {
|
|
||||||
resolve = r
|
|
||||||
})
|
|
||||||
|
|
||||||
// Make sure callers have to wait for the previous sub to be done
|
|
||||||
// before they can get a new one.
|
|
||||||
await p
|
|
||||||
|
|
||||||
// Start our subscription, wait for only one relay to eose before
|
// Start our subscription, wait for only one relay to eose before
|
||||||
// calling it done. We were waiting for all before, but that made
|
// calling it done. We were waiting for all before, but that made
|
||||||
// the slowest relay a bottleneck
|
// the slowest relay a bottleneck
|
||||||
@ -45,22 +39,22 @@ class Channel {
|
|||||||
const done = () => {
|
const done = () => {
|
||||||
sub.unsub()
|
sub.unsub()
|
||||||
|
|
||||||
resolve()
|
this.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the relay takes to long, just give up
|
// If the relay takes to long, just give up
|
||||||
if (timeout) {
|
if (opts.timeout) {
|
||||||
setTimeout(done, 1000)
|
setTimeout(done, opts.timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {unsub: done}
|
return {unsub: done}
|
||||||
}
|
}
|
||||||
all(filter) {
|
all(filter, opts = {}) {
|
||||||
/* eslint no-async-promise-executor: 0 */
|
/* eslint no-async-promise-executor: 0 */
|
||||||
return new Promise(async resolve => {
|
return new Promise(async resolve => {
|
||||||
const result = []
|
const result = []
|
||||||
|
|
||||||
const sub = await this.sub(
|
const sub = this.sub(
|
||||||
filter,
|
filter,
|
||||||
e => result.push(e),
|
e => result.push(e),
|
||||||
r => {
|
r => {
|
||||||
@ -68,6 +62,7 @@ class Channel {
|
|||||||
|
|
||||||
resolve(uniqBy(prop('id'), result))
|
resolve(uniqBy(prop('id'), result))
|
||||||
},
|
},
|
||||||
|
{timeout: 30000, ...opts},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -75,8 +70,25 @@ class Channel {
|
|||||||
|
|
||||||
export const channels = range(0, 10).map(i => new Channel(i.toString()))
|
export const channels = range(0, 10).map(i => new Channel(i.toString()))
|
||||||
|
|
||||||
const req = (...args) => randomChoice(channels).all(...args)
|
const getChannel = async () => {
|
||||||
const sub = (...args) => randomChoice(channels).sub(...args)
|
/*eslint no-constant-condition: 0*/
|
||||||
|
|
||||||
|
// Find a channel that isn't busy, or wait for one to become available
|
||||||
|
while (true) {
|
||||||
|
const channel = find(propEq('status', 'idle'), channels)
|
||||||
|
|
||||||
|
if (channel) {
|
||||||
|
channel.claim()
|
||||||
|
|
||||||
|
return channel
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(300)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = async (...args) => (await getChannel()).all(...args)
|
||||||
|
const sub = async (...args) => (await getChannel()).sub(...args)
|
||||||
|
|
||||||
const getPubkey = () => {
|
const getPubkey = () => {
|
||||||
return pool._pubkey || getPublicKey(pool._privkey)
|
return pool._pubkey || getPublicKey(pool._privkey)
|
||||||
@ -122,14 +134,12 @@ const loadEvents = async filter => {
|
|||||||
return events
|
return events
|
||||||
}
|
}
|
||||||
|
|
||||||
const subs = {}
|
|
||||||
|
|
||||||
const listenForEvents = async (key, filter, onEvent) => {
|
const listenForEvents = async (key, filter, onEvent) => {
|
||||||
if (subs[key]) {
|
if (listenForEvents.subs[key]) {
|
||||||
subs[key].unsub()
|
listenForEvents.subs[key].unsub()
|
||||||
}
|
}
|
||||||
|
|
||||||
subs[key] = await sub(filter, e => {
|
listenForEvents.subs[key] = await sub(filter, e => {
|
||||||
db.events.process(e)
|
db.events.process(e)
|
||||||
|
|
||||||
if (onEvent) {
|
if (onEvent) {
|
||||||
@ -138,8 +148,14 @@ const listenForEvents = async (key, filter, onEvent) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadPeople = pubkeys => {
|
listenForEvents.subs = {}
|
||||||
return pubkeys.length ? loadEvents({kinds: [0, 3, 12165], authors: pubkeys}) : []
|
|
||||||
|
const loadPeople = (pubkeys, opts = {}) => {
|
||||||
|
if (pubkeys.length === 0) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadEvents({kinds: [0, 3, 12165], authors: pubkeys}, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncNetwork = async () => {
|
const syncNetwork = async () => {
|
||||||
@ -148,7 +164,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])
|
await loadPeople([$user.pubkey], {timeout: null})
|
||||||
|
|
||||||
// Get our refreshed person
|
// Get our refreshed person
|
||||||
const people = get(db.people)
|
const people = get(db.people)
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
import Anchor from "src/partials/Anchor.svelte"
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
import Input from "src/partials/Input.svelte"
|
import Input from "src/partials/Input.svelte"
|
||||||
import toast from "src/state/toast"
|
import toast from "src/state/toast"
|
||||||
import relay from 'src/relay'
|
import relay, {connections} from 'src/relay'
|
||||||
|
|
||||||
let privkey = ''
|
let privkey = ''
|
||||||
let hasExtension = false
|
let hasExtension = false
|
||||||
@ -30,10 +30,14 @@
|
|||||||
toast.show("info", "Your private key has been re-generated.")
|
toast.show("info", "Your private key has been re-generated.")
|
||||||
}
|
}
|
||||||
|
|
||||||
const logIn = ({privkey, pubkey}) => {
|
const logIn = async ({privkey, pubkey}) => {
|
||||||
relay.login({privkey, pubkey})
|
relay.login({privkey, pubkey})
|
||||||
|
|
||||||
|
if ($connections.length === 0) {
|
||||||
navigate('/relays')
|
navigate('/relays')
|
||||||
|
} else {
|
||||||
|
navigate('/notes/network')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const logInWithExtension = async () => {
|
const logInWithExtension = async () => {
|
||||||
|
@ -4,69 +4,14 @@
|
|||||||
import {findReply} from 'src/util/nostr'
|
import {findReply} from 'src/util/nostr'
|
||||||
import Anchor from "src/partials/Anchor.svelte"
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
import Tabs from "src/partials/Tabs.svelte"
|
import Tabs from "src/partials/Tabs.svelte"
|
||||||
import Notes from "src/views/Notes.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 {now, timedelta} from 'src/util/misc'
|
||||||
import relay, {network, connections} from 'src/relay'
|
import relay, {connections} from 'src/relay'
|
||||||
|
|
||||||
export let activeTab
|
export let activeTab
|
||||||
|
|
||||||
let sub
|
|
||||||
let delta = timedelta(1, 'minutes')
|
|
||||||
let since = now() - delta
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
sub = await subscribe(now())
|
|
||||||
})
|
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
if (sub) {
|
|
||||||
sub.unsub()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const setActiveTab = tab => navigate(`/notes/${tab}`)
|
const setActiveTab = tab => navigate(`/notes/${tab}`)
|
||||||
|
|
||||||
const subscribe = until =>
|
|
||||||
relay.pool.listenForEvents(
|
|
||||||
'routes/Notes',
|
|
||||||
[{kinds: [1, 5, 7], 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 loadNetworkNotes = async limit => {
|
|
||||||
const filter = {kinds: [1], authors: $network}
|
|
||||||
const notes = await relay.filterEvents(filter).reverse().sortBy('created_at')
|
|
||||||
|
|
||||||
return relay.annotateChunk(notes.slice(0, limit))
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadGlobalNotes = async limit => {
|
|
||||||
const filter = {kinds: [1], since}
|
|
||||||
const notes = await relay.filterEvents(filter).reverse().sortBy('created_at')
|
|
||||||
|
|
||||||
if (notes.length < limit) {
|
|
||||||
since -= delta
|
|
||||||
|
|
||||||
sub = await subscribe(since + delta)
|
|
||||||
}
|
|
||||||
|
|
||||||
return relay.annotateChunk(notes.slice(0, limit))
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $connections.length === 0}
|
{#if $connections.length === 0}
|
||||||
@ -82,7 +27,7 @@
|
|||||||
{#if activeTab === 'network'}
|
{#if activeTab === 'network'}
|
||||||
<Network />
|
<Network />
|
||||||
{:else}
|
{:else}
|
||||||
<Notes shouldMuffle loadNotes={loadGlobalNotes} />
|
<Global />
|
||||||
{/if}
|
{/if}
|
||||||
<div class="fixed bottom-0 right-0 p-8">
|
<div class="fixed bottom-0 right-0 p-8">
|
||||||
<a
|
<a
|
||||||
|
@ -51,23 +51,23 @@ export const formatTimestamp = ts => {
|
|||||||
export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
|
||||||
export const createScroller = loadMore => {
|
export const createScroller = loadMore => {
|
||||||
/* eslint no-constant-condition: 0 */
|
|
||||||
|
|
||||||
let done = false
|
let done = false
|
||||||
|
let didLoad = false
|
||||||
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
|
||||||
const {scrollHeight} = document.body
|
const {scrollHeight} = document.body
|
||||||
|
const shouldLoad = scrollY + innerHeight + 300 > scrollHeight
|
||||||
|
|
||||||
if (scrollY + innerHeight + 2000 > scrollHeight) {
|
// Only trigger loading the first time we reach the threshhold
|
||||||
|
if (shouldLoad && !didLoad) {
|
||||||
await loadMore()
|
await loadMore()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a gross hack, basically, keep loading if the user doesn't scroll again,
|
didLoad = shouldLoad
|
||||||
// but wait a long time because otherwise we'll send off multiple concurrent requests
|
|
||||||
// that will clog up our channels and stall the app.
|
// No need to check all that often
|
||||||
await sleep(30000)
|
await sleep(300)
|
||||||
|
|
||||||
if (!done) {
|
if (!done) {
|
||||||
requestAnimationFrame(check)
|
requestAnimationFrame(check)
|
||||||
@ -91,3 +91,16 @@ export const getLastSync = (k, fallback) => {
|
|||||||
|
|
||||||
return lastSync
|
return lastSync
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class Cursor {
|
||||||
|
constructor(since, delta) {
|
||||||
|
this.since = since || now() - delta,
|
||||||
|
this.delta = delta
|
||||||
|
}
|
||||||
|
step() {
|
||||||
|
const until = this.since
|
||||||
|
this.since -= this.delta
|
||||||
|
|
||||||
|
return [this.since, until]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
<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 Notes from "src/views/Notes.svelte"
|
|
||||||
import {now, timedelta} from 'src/util/misc'
|
|
||||||
import relay, {network, connections} from 'src/relay'
|
|
||||||
|
|
||||||
let sub
|
|
||||||
let since = getLastSync('views/Network')
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
sub = await subscribe(now())
|
|
||||||
})
|
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
if (sub) {
|
|
||||||
sub.unsub()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const setActiveTab = tab => navigate(`/notes/${tab}`)
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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} />
|
|
@ -50,10 +50,30 @@
|
|||||||
} else {
|
} else {
|
||||||
navigate('/login')
|
navigate('/login')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (content === '+') {
|
||||||
|
like = true
|
||||||
|
likes += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content === '-') {
|
||||||
|
flag = true
|
||||||
|
flags += 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteReaction = e => {
|
const deleteReaction = e => {
|
||||||
dispatch('event/delete', [e.id])
|
dispatch('event/delete', [e.id])
|
||||||
|
|
||||||
|
if (e.content === '+') {
|
||||||
|
like = false
|
||||||
|
likes -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.content === '-') {
|
||||||
|
flag = false
|
||||||
|
flags -= 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const startReply = () => {
|
const startReply = () => {
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
export let note
|
export let note
|
||||||
|
|
||||||
let observable, sub
|
let observable, sub
|
||||||
let since = getLastSync(['NoteDetail', note.id])
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
note = await relay.getOrLoadNote(note.id)
|
note = await relay.getOrLoadNote(note.id)
|
||||||
@ -16,7 +15,7 @@
|
|||||||
if (note) {
|
if (note) {
|
||||||
sub = await relay.pool.listenForEvents(
|
sub = await relay.pool.listenForEvents(
|
||||||
'routes/NoteDetail',
|
'routes/NoteDetail',
|
||||||
await relay.buildNoteContextFilter(note, {since})
|
await relay.buildNoteContextFilter(note)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
59
src/views/notes/Global.svelte
Normal file
59
src/views/notes/Global.svelte
Normal 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, Cursor, getLastSync} from 'src/util/misc'
|
||||||
|
import relay from 'src/relay'
|
||||||
|
|
||||||
|
let sub
|
||||||
|
|
||||||
|
const cursor = new Cursor(
|
||||||
|
getLastSync('views/notes/Global'),
|
||||||
|
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
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
if (sub) {
|
||||||
|
sub.unsub()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadNotes = async limit => {
|
||||||
|
const notes = await relay.filterEvents({kinds: [1]}).reverse().sortBy('created_at')
|
||||||
|
|
||||||
|
if (notes.length < limit) {
|
||||||
|
const [since, until] = cursor.step()
|
||||||
|
|
||||||
|
relay.pool.loadEvents([{kinds: [1, 5, 7], since, until}], onEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return relay.annotateChunk(notes.slice(0, limit))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Notes shouldMuffle loadNotes={loadNotes} />
|
63
src/views/notes/Network.svelte
Normal file
63
src/views/notes/Network.svelte
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<script>
|
||||||
|
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 relay, {network} from 'src/relay'
|
||||||
|
|
||||||
|
let sub
|
||||||
|
|
||||||
|
const cursor = new Cursor(
|
||||||
|
getLastSync('views/notes/Network'),
|
||||||
|
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
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
if (sub) {
|
||||||
|
sub.unsub()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
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 [since, until] = cursor.step()
|
||||||
|
|
||||||
|
relay.pool.loadEvents(
|
||||||
|
[{kinds: [1, 5, 7], authors: $network, since, until}],
|
||||||
|
onEvent
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return relay.annotateChunk(notes.slice(0, limit))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Notes shouldMuffle loadNotes={loadNotes} />
|
Loading…
Reference in New Issue
Block a user