mirror of
https://github.com/coracle-social/coracle.git
synced 2024-10-06 11:43:30 +00:00
Split notesCursor into notesLoader and notesListener
This commit is contained in:
parent
bdc359441c
commit
3c60add04f
@ -1,31 +1,32 @@
|
||||
<script>
|
||||
import {onMount} from 'svelte'
|
||||
import {writable} from 'svelte/store'
|
||||
import {find, propEq} from 'ramda'
|
||||
import {notesCursor} from "src/state/app"
|
||||
import {notesLoader, notesListener} from "src/state/app"
|
||||
import {user} from "src/state/user"
|
||||
import Note from 'src/partials/Note.svelte'
|
||||
|
||||
export let note
|
||||
|
||||
const notes = writable([])
|
||||
let onScroll
|
||||
|
||||
onMount(async () => {
|
||||
const cursor = await notesCursor(
|
||||
[{ids: [note.id]},
|
||||
{'#e': [note.id]},
|
||||
// We can't target reaction deletes by e tag, so get them
|
||||
// all so we can support toggling like/flags for our user
|
||||
{kinds: [5], authors: $user ? [$user.pubkey] : []}],
|
||||
{isInModal: true}
|
||||
)
|
||||
const loader = await notesLoader(notes, {ids: [note.id]}, {isInModal: true})
|
||||
const listener = await notesListener(notes, [,
|
||||
{'#e': [note.id]},
|
||||
// We can't target reaction deletes by e tag, so get them
|
||||
// all so we can support toggling like/flags for our user
|
||||
{kinds: [5], authors: $user ? [$user.pubkey] : []}
|
||||
])
|
||||
|
||||
cursor.notes.subscribe($notes => {
|
||||
notes.subscribe($notes => {
|
||||
note = find(propEq('id', note.id), $notes) || note
|
||||
})
|
||||
|
||||
onScroll = cursor.onScroll
|
||||
onScroll = loader.onScroll
|
||||
|
||||
return cursor.unsub
|
||||
return loader.unsub
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -1,12 +1,13 @@
|
||||
<script>
|
||||
import {onMount} from 'svelte'
|
||||
import {writable} from 'svelte/store'
|
||||
import {navigate} from "svelte-routing"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Note from "src/partials/Note.svelte"
|
||||
import {relays} from "src/state/nostr"
|
||||
import {notesCursor} from "src/state/app"
|
||||
import {notesLoader, notesListener} from "src/state/app"
|
||||
|
||||
let notes
|
||||
const notes = writable([])
|
||||
let onScroll
|
||||
|
||||
const createNote = () => {
|
||||
@ -14,12 +15,15 @@
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
const cursor = await notesCursor({kinds: [1]}, {showParents: true})
|
||||
const loader = await notesLoader(notes, {kinds: [1]}, {showParents: true})
|
||||
const listener = await notesListener(notes, {kinds: [1]})
|
||||
|
||||
notes = cursor.notes
|
||||
onScroll = cursor.onScroll
|
||||
onScroll = loader.onScroll
|
||||
|
||||
return cursor.unsub
|
||||
return () => {
|
||||
loader.unsub()
|
||||
listener.unsub()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -1,25 +1,30 @@
|
||||
<script>
|
||||
import {onMount} from 'svelte'
|
||||
import {writable} from 'svelte/store'
|
||||
import {fly} from 'svelte/transition'
|
||||
import Note from "src/partials/Note.svelte"
|
||||
import {user as currentUser} from 'src/state/user'
|
||||
import {accounts, notesCursor} from "src/state/app"
|
||||
import {accounts, notesLoader, notesListener} from "src/state/app"
|
||||
|
||||
export let pubkey
|
||||
|
||||
let user
|
||||
let notes
|
||||
const notes = writable([])
|
||||
let onScroll
|
||||
|
||||
$: user = $accounts[pubkey]
|
||||
|
||||
onMount(async () => {
|
||||
const cursor = await notesCursor({authors: [pubkey]})
|
||||
const filter = {kinds: [1], authors: [pubkey]}
|
||||
const loader = await notesLoader(notes, filter, {showParents: true})
|
||||
const listener = await notesListener(notes, filter)
|
||||
|
||||
notes = cursor.notes
|
||||
onScroll = cursor.onScroll
|
||||
onScroll = loader.onScroll
|
||||
|
||||
return cursor.unsub
|
||||
return () => {
|
||||
loader.unsub()
|
||||
listener.unsub()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
191
src/state/app.js
191
src/state/app.js
@ -1,14 +1,25 @@
|
||||
import {prop, identity, whereEq, reverse, uniq, sortBy, uniqBy, find, last, pluck, groupBy} from 'ramda'
|
||||
import {when, prop, identity, whereEq, reverse, uniq, sortBy, uniqBy, find, last, pluck, groupBy} from 'ramda'
|
||||
import {debounce} from 'throttle-debounce'
|
||||
import {writable, get} from 'svelte/store'
|
||||
import {navigate} from "svelte-routing"
|
||||
import {switcherFn} from 'hurdak/lib/hurdak'
|
||||
import {getLocalJson, setLocalJson, now, timedelta, sleep} from "src/util/misc"
|
||||
import {user} from 'src/state/user'
|
||||
import {_channels, filterMatches, Cursor, channels, relays, findReply} from 'src/state/nostr'
|
||||
import {_channels, filterMatches, Cursor, channels, relays, findReplyTo} from 'src/state/nostr'
|
||||
|
||||
export const modal = writable(null)
|
||||
|
||||
export const logout = () => {
|
||||
// Give any animations a moment to finish
|
||||
setTimeout(() => {
|
||||
user.set(null)
|
||||
relays.set([])
|
||||
navigate("/login")
|
||||
}, 200)
|
||||
}
|
||||
|
||||
// Accounts
|
||||
|
||||
export const accounts = writable(getLocalJson("coracle/accounts") || {})
|
||||
|
||||
accounts.subscribe($accounts => {
|
||||
@ -21,17 +32,6 @@ user.subscribe($user => {
|
||||
}
|
||||
})
|
||||
|
||||
export const logout = () => {
|
||||
// Give any animations a moment to finish
|
||||
setTimeout(() => {
|
||||
user.set(null)
|
||||
relays.set([])
|
||||
navigate("/login")
|
||||
}, 200)
|
||||
}
|
||||
|
||||
// Accounts
|
||||
|
||||
export const ensureAccounts = async (pubkeys, {force = false} = {}) => {
|
||||
const $accounts = get(accounts)
|
||||
|
||||
@ -64,7 +64,7 @@ export const ensureAccounts = async (pubkeys, {force = false} = {}) => {
|
||||
// Notes
|
||||
|
||||
export const annotateNotesChunk = async (chunk, {showParents = false} = {}) => {
|
||||
const parentIds = chunk.map(findReply).filter(identity)
|
||||
const parentIds = chunk.map(findReplyTo).filter(identity)
|
||||
|
||||
if (showParents && parentIds.length) {
|
||||
// Find parents of replies to provide context
|
||||
@ -75,7 +75,7 @@ export const annotateNotesChunk = async (chunk, {showParents = false} = {}) => {
|
||||
|
||||
// Remove replies, show parents instead
|
||||
chunk = parents
|
||||
.concat(chunk.filter(e => !find(whereEq({id: findReply(e)}), parents)))
|
||||
.concat(chunk.filter(e => !find(whereEq({id: findReplyTo(e)}), parents)))
|
||||
}
|
||||
|
||||
if (chunk.length === 0) {
|
||||
@ -116,7 +116,8 @@ export const annotateNotesChunk = async (chunk, {showParents = false} = {}) => {
|
||||
return reverse(sortBy(prop('created'), chunk.map(annotate)))
|
||||
}
|
||||
|
||||
export const notesCursor = async (
|
||||
export const notesLoader = async (
|
||||
notes,
|
||||
filter,
|
||||
{
|
||||
showParents = false,
|
||||
@ -125,97 +126,6 @@ export const notesCursor = async (
|
||||
} = {}
|
||||
) => {
|
||||
const cursor = new Cursor(filter, delta)
|
||||
const notes = writable([])
|
||||
|
||||
const addChunk = chunk => {
|
||||
notes.update($notes => uniqBy(prop('id'), $notes.concat(chunk)))
|
||||
}
|
||||
|
||||
const unsub = await _channels.listener.sub(
|
||||
{kinds: [1, 5, 7], since: now()},
|
||||
e => switcherFn(e.kind, {
|
||||
1: async () => {
|
||||
const replyId = findReply(e)
|
||||
|
||||
if (replyId) {
|
||||
const [annotated] = await annotateNotesChunk([e])
|
||||
|
||||
notes.update($notes =>
|
||||
$notes
|
||||
.map(n => {
|
||||
if (n.id === replyId) {
|
||||
return {...n, replies: [...n.replies, annotated]}
|
||||
}
|
||||
|
||||
return {
|
||||
...n,
|
||||
replies: n.replies.map(r => {
|
||||
if (r.id === replyId) {
|
||||
return {...r, replies: [...r.replies, annotated]}
|
||||
}
|
||||
|
||||
return r
|
||||
}),
|
||||
}
|
||||
})
|
||||
)
|
||||
} else if (filterMatches(filter, e)) {
|
||||
addChunk(await annotateNotesChunk([e], {showParents}))
|
||||
}
|
||||
},
|
||||
5: () => {
|
||||
const ids = e.tags.map(t => t[1])
|
||||
|
||||
notes.update($notes =>
|
||||
$notes
|
||||
.filter(e => !ids.includes(e.id))
|
||||
.map(n => ({
|
||||
...n,
|
||||
replies: n.replies.filter(e => !ids.includes(e.id)),
|
||||
reactions: n.reactions.filter(e => !ids.includes(e.id)),
|
||||
}))
|
||||
)
|
||||
},
|
||||
7: () => {
|
||||
const replyId = findReply(e)
|
||||
|
||||
notes.update($notes =>
|
||||
$notes
|
||||
.map(n => {
|
||||
if (n.id === replyId) {
|
||||
return {...n, reactions: [...n.reactions, e]}
|
||||
}
|
||||
|
||||
return {
|
||||
...n,
|
||||
replies: n.replies.map(r => {
|
||||
if (r.id === replyId) {
|
||||
return {...r, reactions: [...r.reactions, e]}
|
||||
}
|
||||
|
||||
return r
|
||||
}),
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const loadChunk = async () => {
|
||||
const chunk = await cursor.chunk()
|
||||
const annotatedChunk = await annotateNotesChunk(chunk, {showParents})
|
||||
|
||||
addChunk(annotatedChunk)
|
||||
|
||||
// If we have an empty chunk, increase our step size so we can get back to where
|
||||
// we might have old events. Once we get a chunk, knock it down to the default again
|
||||
if (annotatedChunk.length === 0) {
|
||||
cursor.delta = Math.min(timedelta(30, 'days'), cursor.delta * 2)
|
||||
} else {
|
||||
cursor.delta = delta
|
||||
}
|
||||
}
|
||||
|
||||
const onScroll = debounce(1000, async () => {
|
||||
/* eslint no-constant-condition: 0 */
|
||||
@ -237,19 +147,80 @@ export const notesCursor = async (
|
||||
break
|
||||
}
|
||||
|
||||
await loadChunk()
|
||||
const chunk = await cursor.chunk()
|
||||
const annotated = await annotateNotesChunk(chunk, {showParents})
|
||||
|
||||
notes.update($notes => uniqBy(prop('id'), $notes.concat(annotated)))
|
||||
|
||||
// If we have an empty chunk, increase our step size so we can get back to where
|
||||
// we might have old events. Once we get a chunk, knock it down to the default again
|
||||
if (annotated.length === 0) {
|
||||
cursor.delta = Math.min(timedelta(30, 'days'), cursor.delta * 2)
|
||||
} else {
|
||||
cursor.delta = delta
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onScroll()
|
||||
|
||||
return {
|
||||
notes,
|
||||
onScroll,
|
||||
unsub: () => {
|
||||
cursor.stop()
|
||||
unsub()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const notesListener = async (notes, filter) => {
|
||||
const updateNote = (id, f) =>
|
||||
notes.update($notes =>
|
||||
$notes
|
||||
.map(n => {
|
||||
if (n.id === id) {
|
||||
return f(n)
|
||||
}
|
||||
|
||||
return {...n, replies: n.replies.map(when(whereEq({id}), f))}
|
||||
})
|
||||
)
|
||||
|
||||
const deleteNotes = ($notes, ids) =>
|
||||
$notes
|
||||
.filter(e => !ids.includes(e.id))
|
||||
.map(n => ({
|
||||
...n,
|
||||
replies: deleteNotes(n.replies, ids),
|
||||
reactions: n.reactions.filter(e => !ids.includes(e.id)),
|
||||
}))
|
||||
|
||||
return await _channels.listener.sub(
|
||||
{kinds: [1, 5, 7], since: now()},
|
||||
e => switcherFn(e.kind, {
|
||||
1: async () => {
|
||||
const id = findReplyTo(e)
|
||||
|
||||
if (id) {
|
||||
const [reply] = await annotateNotesChunk([e])
|
||||
|
||||
updateNote(id, n => ({...n, replies: n.replies.concat(reply)}))
|
||||
} else if (filterMatches(filter, e)) {
|
||||
const [note] = await annotateNotesChunk([e])
|
||||
|
||||
notes.update($notes => uniqBy(prop('id'), [note].concat($notes)))
|
||||
}
|
||||
},
|
||||
5: () => {
|
||||
const ids = e.tags.map(t => t[1])
|
||||
|
||||
notes.update($notes => deleteNotes($notes, ids))
|
||||
},
|
||||
7: () => {
|
||||
console.log(e)
|
||||
const id = findReplyTo(e)
|
||||
|
||||
updateNote(id, n => ({...n, reactions: n.reactions.concat(e)}))
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ export const filterTags = (where, events) =>
|
||||
export const findTag = (where, events) => first(filterTags(where, events))
|
||||
|
||||
// Support the deprecated version where tags are marked as replies
|
||||
export const findReply = e =>
|
||||
export const findReplyTo = e =>
|
||||
findTag({tag: "e", type: "reply"}, e) || findTag({tag: "e"}, e)
|
||||
|
||||
export const filterMatches = (filter, e) => {
|
||||
|
Loading…
Reference in New Issue
Block a user