Suspend subscriptions while modal is open

This commit is contained in:
Jonathan Staab 2022-11-26 16:24:45 -08:00
parent 0e8d0815c4
commit 98313091d1
6 changed files with 103 additions and 86 deletions

View File

@ -9,8 +9,7 @@
export let note export let note
onMount(() => { onMount(() => {
return findNotes( const start = findNotes(
channels.watcher,
[{ids: [note.id]}, [{ids: [note.id]},
{'#e': [note.id]}, {'#e': [note.id]},
// We can't target reaction deletes by e tag, so get them // We can't target reaction deletes by e tag, so get them
@ -20,6 +19,8 @@
note = find(propEq('id', note.id), $notes) || note note = find(propEq('id', note.id), $notes) || note
} }
) )
return start()
}) })
</script> </script>

View File

@ -5,41 +5,22 @@
import Anchor from "src/partials/Anchor.svelte" import Anchor from "src/partials/Anchor.svelte"
import Note from "src/partials/Note.svelte" import Note from "src/partials/Note.svelte"
import {channels, relays} from "src/state/nostr" import {channels, relays} from "src/state/nostr"
import {findNotes, modal} from "src/state/app" import {findNotesAndWatchModal, modal} from "src/state/app"
let stop
let notes let notes
const createNote = () => { const createNote = () => {
navigate("/notes/new") navigate("/notes/new")
} }
const start = () => { onMount(() => {
stop = findNotes(channels.watcher, { return findNotesAndWatchModal({
since: now() - timedelta(1, 'days'),
limit: 100, limit: 100,
}, $notes => { }, $notes => {
if ($notes.length) { if ($notes.length) {
notes = $notes notes = $notes
} }
}) })
}
onMount(() => {
const unsub = modal.subscribe($modal => {
console.log('modal', $modal)
if ($modal) {
stop && stop()
} else {
setTimeout(start, 600)
}
})
return () => {
stop()
unsub()
}
}) })
</script> </script>

View File

@ -6,7 +6,7 @@
import Note from "src/partials/Note.svelte" import Note from "src/partials/Note.svelte"
import {channels} from 'src/state/nostr' import {channels} from 'src/state/nostr'
import {user as currentUser} from 'src/state/user' import {user as currentUser} from 'src/state/user'
import {accounts, findNotes} from "src/state/app" import {accounts, findNotesAndWatchModal, modal} from "src/state/app"
export let pubkey export let pubkey
@ -15,13 +15,14 @@
$: user = $accounts[pubkey] $: user = $accounts[pubkey]
onMount(async () => { onMount(() => {
return findNotes(channels.watcher, { return findNotesAndWatchModal({
authors: [pubkey], authors: [pubkey],
since: now() - timedelta(1, 'days'),
limit: 100, limit: 100,
}, $notes => { }, $notes => {
notes = $notes if ($notes.length) {
notes = $notes
}
}) })
}) })
</script> </script>

View File

@ -1,7 +1,7 @@
import {prop, uniq, pluck, sortBy, uniqBy, find, last, groupBy} from 'ramda' import {prop, uniq, sortBy, uniqBy, find, last, groupBy} from 'ramda'
import {debounce} from 'throttle-debounce' import {debounce} from 'throttle-debounce'
import {writable, derived, get} from 'svelte/store' import {writable, derived, get} from 'svelte/store'
import {switcherFn, ensurePlural} from 'hurdak/lib/hurdak' import {switcherFn, noop, ensurePlural} from 'hurdak/lib/hurdak'
import {getLocalJson, setLocalJson, now, timedelta} from "src/util/misc" import {getLocalJson, setLocalJson, now, timedelta} from "src/util/misc"
import {user} from 'src/state/user' import {user} from 'src/state/user'
import {channels} from 'src/state/nostr' import {channels} from 'src/state/nostr'
@ -56,72 +56,105 @@ export const ensureAccounts = async pubkeys => {
}) })
} }
export const findNotes = (channel, queries, cb) => { export const findNotes = (filters, cb) => {
const notes = writable([]) const start = () => {
const reactions = writable([]) const notes = writable([])
const reactions = writable([])
let pubkeys = [] let pubkeys = []
const refreshAccounts = debounce(300, () => { const refreshAccounts = debounce(300, () => {
ensureAccounts(uniq(pubkeys)) ensureAccounts(uniq(pubkeys))
pubkeys = [] pubkeys = []
}) })
const closeRequest = channel.sub({ const closeRequest = channels.watcher.sub({
filter: ensurePlural(queries).map(q => ({kinds: [1, 5, 7], ...q})), filter: ensurePlural(filters).map(q => ({kinds: [1, 5, 7], ...q})),
cb: e => { cb: e => {
// Chunk requests to load accounts // Chunk requests to load accounts
pubkeys.push(e.pubkey) pubkeys.push(e.pubkey)
refreshAccounts() refreshAccounts()
switcherFn(e.kind, { switcherFn(e.kind, {
1: () => { 1: () => {
notes.update($xs => uniqBy(prop('id'), $xs.concat(e))) notes.update($xs => uniqBy(prop('id'), $xs.concat(e)))
}, },
5: () => { 5: () => {
const ids = e.tags.map(t => t[1]) const ids = e.tags.map(t => t[1])
notes.update($xs => $xs.filter(({id}) => !id.includes(ids))) notes.update($xs => $xs.filter(({id}) => !id.includes(ids)))
reactions.update($xs => $xs.filter(({id}) => !id.includes(ids))) reactions.update($xs => $xs.filter(({id}) => !id.includes(ids)))
}, },
7: () => { 7: () => {
reactions.update($xs => $xs.concat(e)) reactions.update($xs => $xs.concat(e))
}, },
}) })
}, },
}) })
const annotatedNotes = derived( const annotatedNotes = derived(
[notes, reactions, accounts], [notes, reactions, accounts],
([$notes, $reactions, $accounts]) => { ([$notes, $reactions, $accounts]) => {
const repliesById = groupBy( const repliesById = groupBy(
n => find(t => last(t) === 'reply', n.tags)[1], n => find(t => last(t) === 'reply', n.tags)[1],
$notes.filter(n => n.tags.map(last).includes('reply')) $notes.filter(n => n.tags.map(last).includes('reply'))
) )
const reactionsById = groupBy( const reactionsById = groupBy(
n => find(t => last(t) === 'reply', n.tags)[1], n => find(t => last(t) === 'reply', n.tags)[1],
$reactions.filter(n => n.tags.map(last).includes('reply')) $reactions.filter(n => n.tags.map(last).includes('reply'))
) )
const annotate = n => ({ const annotate = n => ({
...n, ...n,
user: $accounts[n.pubkey], user: $accounts[n.pubkey],
replies: (repliesById[n.id] || []).map(reply => annotate(reply)), replies: (repliesById[n.id] || []).map(reply => annotate(reply)),
reactions: (reactionsById[n.id] || []).map(reaction => annotate(reaction)), reactions: (reactionsById[n.id] || []).map(reaction => annotate(reaction)),
}) })
return sortBy(prop('created'), $notes.map(annotate)) return sortBy(prop('created'), $notes.map(annotate))
}
)
const unsubscribe = annotatedNotes.subscribe(debounce(100, cb))
return () => {
unsubscribe()
closeRequest()
} }
) }
const unsubscribe = annotatedNotes.subscribe(debounce(300, cb)) // Allow caller to suspend/restart the subscription
return start
}
export const findNotesAndWatchModal = (filters, cb) => {
const start = findNotes(filters, cb)
let stop = start()
// Suspend our subscription while we have note detail open
// so we can avoid exceeding our concurrent subscription limit
const unsub = modal.subscribe($modal => {
if ($modal) {
stop && stop()
stop = null
} else if (!stop) {
// Wait for animations to complete
setTimeout(
() => {
stop = start()
},
600
)
}
})
return () => { return () => {
unsubscribe() stop()
unsub()
closeRequest()
} }
} }

View File

@ -20,7 +20,7 @@ dispatch.addMethod("account/init", async (topic, privkey) => {
user.set({name: pubkey.slice(0, 8), privkey, pubkey}) user.set({name: pubkey.slice(0, 8), privkey, pubkey})
// Attempt to refresh user data from the network // Attempt to refresh user data from the network
const found = Boolean(await channels.getter.first({authors: [$user.pubkey]})) const found = Boolean(await channels.getter.first({authors: [pubkey]}))
// Tell the caller whether this user was found // Tell the caller whether this user was found
return {found} return {found}

View File

@ -1,6 +1,6 @@
import {writable, get} from "svelte/store" import {writable} from "svelte/store"
import {getLocalJson, setLocalJson} from "src/util/misc" import {getLocalJson, setLocalJson} from "src/util/misc"
import {nostr, channels} from 'src/state/nostr' import {nostr} from 'src/state/nostr'
export const user = writable(getLocalJson("coracle/user")) export const user = writable(getLocalJson("coracle/user"))
@ -10,3 +10,4 @@ user.subscribe($user => {
// Keep nostr in sync // Keep nostr in sync
nostr.login($user?.privkey) nostr.login($user?.privkey)
}) })