diff --git a/src/partials/NoteDetail.svelte b/src/partials/NoteDetail.svelte index ff66116a..499452c4 100644 --- a/src/partials/NoteDetail.svelte +++ b/src/partials/NoteDetail.svelte @@ -9,8 +9,7 @@ export let note onMount(() => { - return findNotes( - channels.watcher, + const start = findNotes( [{ids: [note.id]}, {'#e': [note.id]}, // We can't target reaction deletes by e tag, so get them @@ -20,6 +19,8 @@ note = find(propEq('id', note.id), $notes) || note } ) + + return start() }) diff --git a/src/routes/Notes.svelte b/src/routes/Notes.svelte index 53e36043..1caf2e06 100644 --- a/src/routes/Notes.svelte +++ b/src/routes/Notes.svelte @@ -5,41 +5,22 @@ import Anchor from "src/partials/Anchor.svelte" import Note from "src/partials/Note.svelte" 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 const createNote = () => { navigate("/notes/new") } - const start = () => { - stop = findNotes(channels.watcher, { - since: now() - timedelta(1, 'days'), + onMount(() => { + return findNotesAndWatchModal({ limit: 100, }, $notes => { if ($notes.length) { notes = $notes } }) - } - - - onMount(() => { - const unsub = modal.subscribe($modal => { - console.log('modal', $modal) - if ($modal) { - stop && stop() - } else { - setTimeout(start, 600) - } - }) - - return () => { - stop() - unsub() - } }) diff --git a/src/routes/UserDetail.svelte b/src/routes/UserDetail.svelte index c377ff02..e292c6e9 100644 --- a/src/routes/UserDetail.svelte +++ b/src/routes/UserDetail.svelte @@ -6,7 +6,7 @@ import Note from "src/partials/Note.svelte" import {channels} from 'src/state/nostr' 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 @@ -15,13 +15,14 @@ $: user = $accounts[pubkey] - onMount(async () => { - return findNotes(channels.watcher, { + onMount(() => { + return findNotesAndWatchModal({ authors: [pubkey], - since: now() - timedelta(1, 'days'), limit: 100, }, $notes => { - notes = $notes + if ($notes.length) { + notes = $notes + } }) }) diff --git a/src/state/app.js b/src/state/app.js index dc299859..fd56bb5b 100644 --- a/src/state/app.js +++ b/src/state/app.js @@ -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 {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 {user} from 'src/state/user' import {channels} from 'src/state/nostr' @@ -56,72 +56,105 @@ export const ensureAccounts = async pubkeys => { }) } -export const findNotes = (channel, queries, cb) => { - const notes = writable([]) - const reactions = writable([]) +export const findNotes = (filters, cb) => { + const start = () => { + const notes = writable([]) + const reactions = writable([]) - let pubkeys = [] + let pubkeys = [] - const refreshAccounts = debounce(300, () => { - ensureAccounts(uniq(pubkeys)) + const refreshAccounts = debounce(300, () => { + ensureAccounts(uniq(pubkeys)) - pubkeys = [] - }) + pubkeys = [] + }) - const closeRequest = channel.sub({ - filter: ensurePlural(queries).map(q => ({kinds: [1, 5, 7], ...q})), - cb: e => { - // Chunk requests to load accounts - pubkeys.push(e.pubkey) - refreshAccounts() + const closeRequest = channels.watcher.sub({ + filter: ensurePlural(filters).map(q => ({kinds: [1, 5, 7], ...q})), + cb: e => { + // Chunk requests to load accounts + pubkeys.push(e.pubkey) + refreshAccounts() - switcherFn(e.kind, { - 1: () => { - notes.update($xs => uniqBy(prop('id'), $xs.concat(e))) - }, - 5: () => { - const ids = e.tags.map(t => t[1]) + switcherFn(e.kind, { + 1: () => { + notes.update($xs => uniqBy(prop('id'), $xs.concat(e))) + }, + 5: () => { + const ids = e.tags.map(t => t[1]) - notes.update($xs => $xs.filter(({id}) => !id.includes(ids))) - reactions.update($xs => $xs.filter(({id}) => !id.includes(ids))) - }, - 7: () => { - reactions.update($xs => $xs.concat(e)) - }, - }) - }, - }) + notes.update($xs => $xs.filter(({id}) => !id.includes(ids))) + reactions.update($xs => $xs.filter(({id}) => !id.includes(ids))) + }, + 7: () => { + reactions.update($xs => $xs.concat(e)) + }, + }) + }, + }) - const annotatedNotes = derived( - [notes, reactions, accounts], - ([$notes, $reactions, $accounts]) => { - const repliesById = groupBy( - n => find(t => last(t) === 'reply', n.tags)[1], - $notes.filter(n => n.tags.map(last).includes('reply')) - ) + const annotatedNotes = derived( + [notes, reactions, accounts], + ([$notes, $reactions, $accounts]) => { + const repliesById = groupBy( + n => find(t => last(t) === 'reply', n.tags)[1], + $notes.filter(n => n.tags.map(last).includes('reply')) + ) - const reactionsById = groupBy( - n => find(t => last(t) === 'reply', n.tags)[1], - $reactions.filter(n => n.tags.map(last).includes('reply')) - ) + const reactionsById = groupBy( + n => find(t => last(t) === 'reply', n.tags)[1], + $reactions.filter(n => n.tags.map(last).includes('reply')) + ) - const annotate = n => ({ - ...n, - user: $accounts[n.pubkey], - replies: (repliesById[n.id] || []).map(reply => annotate(reply)), - reactions: (reactionsById[n.id] || []).map(reaction => annotate(reaction)), - }) + const annotate = n => ({ + ...n, + user: $accounts[n.pubkey], + replies: (repliesById[n.id] || []).map(reply => annotate(reply)), + 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 () => { - unsubscribe() - - closeRequest() + stop() + unsub() } } diff --git a/src/state/dispatch.js b/src/state/dispatch.js index 90083838..bc84a25a 100644 --- a/src/state/dispatch.js +++ b/src/state/dispatch.js @@ -20,7 +20,7 @@ dispatch.addMethod("account/init", async (topic, privkey) => { user.set({name: pubkey.slice(0, 8), privkey, pubkey}) // 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 return {found} diff --git a/src/state/user.js b/src/state/user.js index 0dc82ca1..b3fe39f3 100644 --- a/src/state/user.js +++ b/src/state/user.js @@ -1,6 +1,6 @@ -import {writable, get} from "svelte/store" +import {writable} from "svelte/store" 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")) @@ -10,3 +10,4 @@ user.subscribe($user => { // Keep nostr in sync nostr.login($user?.privkey) }) +