mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-30 00:41:12 +00:00
Working on scrolling stuff
This commit is contained in:
parent
eaf2e45e46
commit
447c112d21
@ -22,6 +22,7 @@ Coracle is currently in _alpha_ - expect bugs, slow loading times, and rough edg
|
|||||||
- [ ] Optimistically load events the user publishes (e.g. to reduce reflow for reactions/replies).
|
- [ ] Optimistically load events the user publishes (e.g. to reduce reflow for reactions/replies).
|
||||||
- Essentially, we can pretend to be our own in-memory relay.
|
- Essentially, we can pretend to be our own in-memory relay.
|
||||||
- This allows us to keep a copy of all user data, and possibly user likes/reply parents
|
- This allows us to keep a copy of all user data, and possibly user likes/reply parents
|
||||||
|
- [ ] Support invoices https://twitter.com/jb55/status/1604131336247476224
|
||||||
|
|
||||||
# Bugs
|
# Bugs
|
||||||
|
|
||||||
@ -44,6 +45,7 @@ Coracle is currently in _alpha_ - expect bugs, slow loading times, and rough edg
|
|||||||
- [ ] Make user a livequery instead of a store
|
- [ ] Make user a livequery instead of a store
|
||||||
- [ ] Figure out if multiple relays congest response times because we wait for all eose
|
- [ ] Figure out if multiple relays congest response times because we wait for all eose
|
||||||
- [ ] Set default relay when storage is empty
|
- [ ] Set default relay when storage is empty
|
||||||
|
- [ ] Are connections closed when a relay is removed?
|
||||||
- https://vitejs.dev/guide/features.html#web-workers
|
- https://vitejs.dev/guide/features.html#web-workers
|
||||||
- https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
|
- https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
|
||||||
- https://web.dev/module-workers/
|
- https://web.dev/module-workers/
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import Dexie from 'dexie'
|
import Dexie from 'dexie'
|
||||||
import {groupBy, prop, flatten, pick} from 'ramda'
|
import {groupBy, prop, flatten, pick} from 'ramda'
|
||||||
import {ensurePlural, switcherFn} from 'hurdak/lib/hurdak'
|
import {ensurePlural, switcherFn} from 'hurdak/lib/hurdak'
|
||||||
|
import {now} from 'src/util/misc'
|
||||||
import {filterTags, findReply, findRoot} from 'src/util/nostr'
|
import {filterTags, findReply, findRoot} from 'src/util/nostr'
|
||||||
|
|
||||||
export const db = new Dexie('coracle/relay')
|
export const db = new Dexie('coracle/relay')
|
||||||
@ -59,11 +60,12 @@ db.events.process = async events => {
|
|||||||
for (const event of profileUpdates) {
|
for (const event of profileUpdates) {
|
||||||
const {pubkey, kind, content, tags} = event
|
const {pubkey, kind, content, tags} = event
|
||||||
const user = await db.users.where('pubkey').equals(pubkey).first()
|
const user = await db.users.where('pubkey').equals(pubkey).first()
|
||||||
|
const putUser = data => db.users.put({...user, ...data, pubkey, updated_at: now()})
|
||||||
|
|
||||||
await switcherFn(kind, {
|
await switcherFn(kind, {
|
||||||
0: () => db.users.put({...user, ...JSON.parse(content), pubkey}),
|
0: () => putUser(JSON.parse(content)),
|
||||||
3: () => db.users.put({...user, petnames: tags, pubkey}),
|
3: () => putUser({petnames: tags}),
|
||||||
12165: () => db.users.put({...user, muffle: tags, pubkey}),
|
12165: () => putUser({muffle: tags}),
|
||||||
default: () => {
|
default: () => {
|
||||||
console.log(`Received unsupported event type ${event.kind}`)
|
console.log(`Received unsupported event type ${event.kind}`)
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {liveQuery} from 'dexie'
|
import {liveQuery} from 'dexie'
|
||||||
import {pluck, isNil} from 'ramda'
|
import {pluck, uniq, objOf, isNil} from 'ramda'
|
||||||
import {ensurePlural, createMap, ellipsize, first} from 'hurdak/lib/hurdak'
|
import {ensurePlural, createMap, ellipsize, first} from 'hurdak/lib/hurdak'
|
||||||
import {now, timedelta} from 'src/util/misc'
|
import {now, timedelta} from 'src/util/misc'
|
||||||
import {escapeHtml} from 'src/util/html'
|
import {escapeHtml} from 'src/util/html'
|
||||||
@ -16,16 +16,26 @@ const lq = f => liveQuery(async () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const ensureContext = async e => {
|
const ensurePerson = async ({pubkey}) => {
|
||||||
const user = await db.users.where('pubkey').equals(e.pubkey).first()
|
const user = await db.users.where('pubkey').equals(pubkey).first()
|
||||||
|
|
||||||
// Throttle updates for users
|
// Throttle updates for users
|
||||||
if (!user || user.updated_at < now() - timedelta(1, 'hours')) {
|
if (!user || user.updated_at < now() - timedelta(1, 'hours')) {
|
||||||
await pool.syncUserInfo({pubkey: e.pubkey, ...user})
|
await pool.syncUserInfo({pubkey, ...user})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO optimize this like user above so we're not double-fetching
|
const ensureContext = async events => {
|
||||||
await pool.fetchContext(e)
|
const ids = events.flatMap(e => filterTags({tag: "e"}, e).concat(e.id))
|
||||||
|
const people = uniq(pluck('pubkey', events)).map(objOf('pubkey'))
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
people.map(ensurePerson),
|
||||||
|
pool.fetchEvents([
|
||||||
|
{kinds: [1, 5, 7], '#e': ids},
|
||||||
|
{kinds: [1, 5], ids},
|
||||||
|
]),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
const prefilterEvents = filter => {
|
const prefilterEvents = filter => {
|
||||||
@ -50,6 +60,8 @@ const filterEvents = filter => {
|
|||||||
if (filter.ids && !filter.ids.includes(e.id)) return false
|
if (filter.ids && !filter.ids.includes(e.id)) return false
|
||||||
if (filter.authors && !filter.authors.includes(e.pubkey)) return false
|
if (filter.authors && !filter.authors.includes(e.pubkey)) return false
|
||||||
if (filter.kinds && !filter.kinds.includes(e.kind)) return false
|
if (filter.kinds && !filter.kinds.includes(e.kind)) return false
|
||||||
|
if (filter.since && filter.since > e.created_at) return false
|
||||||
|
if (filter.until && filter.until < e.created_at) return false
|
||||||
if (!isNil(filter.content) && filter.content !== e.content) return false
|
if (!isNil(filter.content) && filter.content !== e.content) return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -78,12 +90,27 @@ const findReaction = async (id, filter) =>
|
|||||||
const countReactions = async (id, filter) =>
|
const countReactions = async (id, filter) =>
|
||||||
(await filterReactions(id, filter)).length
|
(await filterReactions(id, filter)).length
|
||||||
|
|
||||||
const findNote = async id => {
|
const findNote = async (id, giveUp = false) => {
|
||||||
const [note, children] = await Promise.all([
|
const [note, children] = await Promise.all([
|
||||||
db.events.get(id),
|
db.events.get(id),
|
||||||
db.events.where('reply').equals(id),
|
db.events.where('reply').equals(id),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// If we don't have it, try to retrieve it
|
||||||
|
if (!note) {
|
||||||
|
console.warning(`Failed to find context for note ${id}`)
|
||||||
|
|
||||||
|
if (giveUp) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
await ensureContext([
|
||||||
|
await pool.fetchEvents({ids: [id]}),
|
||||||
|
])
|
||||||
|
|
||||||
|
return findNote(id, true)
|
||||||
|
}
|
||||||
|
|
||||||
const [replies, reactions, user, html] = await Promise.all([
|
const [replies, reactions, user, html] = await Promise.all([
|
||||||
children.clone().filter(e => e.kind === 1).toArray(),
|
children.clone().filter(e => e.kind === 1).toArray(),
|
||||||
children.clone().filter(e => e.kind === 7).toArray(),
|
children.clone().filter(e => e.kind === 7).toArray(),
|
||||||
@ -130,6 +157,6 @@ const filterAlerts = async (user, filter) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
db, pool, lq, ensureContext, filterEvents, filterReactions, countReactions,
|
db, pool, lq, ensurePerson, ensureContext, filterEvents, filterReactions,
|
||||||
findReaction, filterReplies, findNote, renderNote, filterAlerts,
|
countReactions, findReaction, filterReplies, findNote, renderNote, filterAlerts,
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {uniqBy, prop} from 'ramda'
|
import {uniqBy, prop} from 'ramda'
|
||||||
import {relayPool, getPublicKey} from 'nostr-tools'
|
import {relayPool, getPublicKey} from 'nostr-tools'
|
||||||
import {noop} from 'hurdak/lib/hurdak'
|
import {noop, range} from 'hurdak/lib/hurdak'
|
||||||
import {now, randomChoice, timedelta, getLocalJson, setLocalJson} from "src/util/misc"
|
import {now, randomChoice, timedelta, getLocalJson, setLocalJson} from "src/util/misc"
|
||||||
import {filterTags, getTagValues} from "src/util/nostr"
|
import {getTagValues} from "src/util/nostr"
|
||||||
import {db} from 'src/relay/db'
|
import {db} from 'src/relay/db'
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -73,11 +73,7 @@ class Channel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const channels = [
|
export const channels = range(0, 10).map(i => new Channel(i.toString()))
|
||||||
new Channel('a'),
|
|
||||||
new Channel('b'),
|
|
||||||
new Channel('c'),
|
|
||||||
]
|
|
||||||
|
|
||||||
const req = filter => randomChoice(channels).all(filter)
|
const req = filter => randomChoice(channels).all(filter)
|
||||||
|
|
||||||
@ -117,7 +113,9 @@ const publishEvent = event => {
|
|||||||
const loadEvents = async filter => {
|
const loadEvents = async filter => {
|
||||||
const events = await req(filter)
|
const events = await req(filter)
|
||||||
|
|
||||||
db.events.process(events)
|
await db.events.process(events)
|
||||||
|
|
||||||
|
return events
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncUserInfo = async user => {
|
const syncUserInfo = async user => {
|
||||||
@ -137,16 +135,12 @@ const syncUserInfo = async user => {
|
|||||||
return person
|
return person
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchContext = async event => {
|
const fetchEvents = async filter => {
|
||||||
const events = await req([
|
db.events.process(await req(filter))
|
||||||
{kinds: [5, 7], '#e': [event.id]},
|
|
||||||
{kinds: [5], 'ids': filterTags({tag: "e"}, event)},
|
|
||||||
])
|
|
||||||
|
|
||||||
db.events.process(events)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let syncSub = null
|
let syncSub = null
|
||||||
|
let syncChan = new Channel('sync')
|
||||||
|
|
||||||
const sync = async user => {
|
const sync = async user => {
|
||||||
if (syncSub) {
|
if (syncSub) {
|
||||||
@ -155,7 +149,10 @@ const sync = async user => {
|
|||||||
|
|
||||||
if (!user) return
|
if (!user) return
|
||||||
|
|
||||||
|
// Get user info right away
|
||||||
const {petnames, pubkey} = await syncUserInfo(user)
|
const {petnames, pubkey} = await syncUserInfo(user)
|
||||||
|
|
||||||
|
// Don't grab nothing, but don't grab everything either
|
||||||
const since = Math.max(
|
const since = Math.max(
|
||||||
now() - timedelta(3, 'days'),
|
now() - timedelta(3, 'days'),
|
||||||
Math.min(
|
Math.min(
|
||||||
@ -167,7 +164,7 @@ const sync = async user => {
|
|||||||
setLocalJson('pool/lastSync', now())
|
setLocalJson('pool/lastSync', now())
|
||||||
|
|
||||||
// Populate recent activity in network so the user has something to look at right away
|
// Populate recent activity in network so the user has something to look at right away
|
||||||
syncSub = randomChoice(channels).sub(
|
syncSub = syncChan.sub(
|
||||||
[{since, authors: getTagValues(petnames).concat(pubkey)},
|
[{since, authors: getTagValues(petnames).concat(pubkey)},
|
||||||
{since, '#p': [pubkey]}],
|
{since, '#p': [pubkey]}],
|
||||||
db.events.process
|
db.events.process
|
||||||
@ -176,5 +173,5 @@ const sync = async user => {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
getPubkey, addRelay, removeRelay, setPrivateKey, setPublicKey,
|
getPubkey, addRelay, removeRelay, setPrivateKey, setPublicKey,
|
||||||
publishEvent, loadEvents, syncUserInfo, fetchContext, sync,
|
publishEvent, loadEvents, syncUserInfo, fetchEvents, sync,
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import {find} from 'ramda'
|
import {find} from 'ramda'
|
||||||
import {fly} from 'svelte/transition'
|
import {fly} from 'svelte/transition'
|
||||||
import {navigate} from 'svelte-routing'
|
import {navigate} from 'svelte-routing'
|
||||||
|
import {timedelta} from 'src/util/misc'
|
||||||
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/views/Notes.svelte"
|
import Notes from "src/views/Notes.svelte"
|
||||||
@ -13,6 +14,8 @@
|
|||||||
export let pubkey
|
export let pubkey
|
||||||
export let activeTab
|
export let activeTab
|
||||||
|
|
||||||
|
relay.ensurePerson({pubkey})
|
||||||
|
|
||||||
const user = relay.lq(() => relay.db.users.get(pubkey))
|
const user = relay.lq(() => relay.db.users.get(pubkey))
|
||||||
|
|
||||||
let following = $currentUser && find(t => t[1] === pubkey, $currentUser.petnames)
|
let following = $currentUser && find(t => t[1] === pubkey, $currentUser.petnames)
|
||||||
@ -78,7 +81,7 @@
|
|||||||
|
|
||||||
<Tabs tabs={['notes', 'likes', 'network']} {activeTab} {setActiveTab} />
|
<Tabs tabs={['notes', 'likes', 'network']} {activeTab} {setActiveTab} />
|
||||||
{#if activeTab === 'notes'}
|
{#if activeTab === 'notes'}
|
||||||
<Notes filter={{kinds: [1], authors: [pubkey]}} />
|
<Notes showParent delta={timedelta(1, 'days')} filter={{kinds: [1], authors: [pubkey]}} />
|
||||||
{:else if activeTab === 'likes'}
|
{:else if activeTab === 'likes'}
|
||||||
<Likes author={pubkey} />
|
<Likes author={pubkey} />
|
||||||
{:else if activeTab === 'network'}
|
{:else if activeTab === 'network'}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import {pluck} from "ramda"
|
import {pluck} from "ramda"
|
||||||
import {debounce} from 'throttle-debounce'
|
|
||||||
import Fuse from "fuse.js/dist/fuse.min.js"
|
import Fuse from "fuse.js/dist/fuse.min.js"
|
||||||
|
|
||||||
export const fuzzy = (data, opts = {}) => {
|
export const fuzzy = (data, opts = {}) => {
|
||||||
@ -52,27 +51,31 @@ 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 => {
|
||||||
const onScroll = debounce(1000, async () => {
|
/* eslint no-constant-condition: 0 */
|
||||||
/* eslint no-constant-condition: 0 */
|
|
||||||
|
|
||||||
while (true) {
|
let done = false
|
||||||
// While we have empty space, fill it
|
|
||||||
const {scrollY, innerHeight} = window
|
|
||||||
const {scrollHeight} = document.body
|
|
||||||
|
|
||||||
if (scrollY + innerHeight + 600 < scrollHeight) {
|
const check = async () => {
|
||||||
break
|
// While we have empty space, fill it
|
||||||
}
|
const {scrollY, innerHeight} = window
|
||||||
|
const {scrollHeight} = document.body
|
||||||
|
|
||||||
loadMore()
|
if (scrollY + innerHeight + 600 > scrollHeight) {
|
||||||
|
await loadMore()
|
||||||
await sleep(1000)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
onScroll()
|
await sleep(300)
|
||||||
|
|
||||||
return onScroll
|
if (!done) {
|
||||||
|
requestAnimationFrame(check)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(check)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
done = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const randomChoice = xs => xs[Math.floor(Math.random() * xs.length)]
|
export const randomChoice = xs => xs[Math.floor(Math.random() * xs.length)]
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
import {findReply} from "src/util/nostr"
|
import {findReply} from "src/util/nostr"
|
||||||
import Preview from 'src/partials/Preview.svelte'
|
import Preview from 'src/partials/Preview.svelte'
|
||||||
import Anchor from 'src/partials/Anchor.svelte'
|
import Anchor from 'src/partials/Anchor.svelte'
|
||||||
import relay from 'src/relay'
|
|
||||||
import {dispatch} from "src/state/dispatch"
|
import {dispatch} from "src/state/dispatch"
|
||||||
import {settings, user, modal} from "src/state/app"
|
import {settings, user, modal} from "src/state/app"
|
||||||
import {formatTimestamp} from 'src/util/misc'
|
import {formatTimestamp} from 'src/util/misc'
|
||||||
@ -34,8 +33,6 @@
|
|||||||
flag = find(whereEq({pubkey: $user?.pubkey}), flags)
|
flag = find(whereEq({pubkey: $user?.pubkey}), flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
relay.ensureContext(note)
|
|
||||||
|
|
||||||
const onClick = e => {
|
const onClick = e => {
|
||||||
if (!['I'].includes(e.target.tagName) && !hasParent('a', e.target)) {
|
if (!['I'].includes(e.target.tagName) && !hasParent('a', e.target)) {
|
||||||
modal.set({note})
|
modal.set({note})
|
||||||
@ -112,14 +109,16 @@
|
|||||||
<Anchor on:click={() => deleteReaction(flag)}>Unflag</Anchor>
|
<Anchor on:click={() => deleteReaction(flag)}>Unflag</Anchor>
|
||||||
</p>
|
</p>
|
||||||
{:else}
|
{:else}
|
||||||
<p class="text-ellipsis overflow-hidden">
|
<div class="text-ellipsis overflow-hidden flex flex-col gap-2">
|
||||||
{@html note.html}
|
<p>{@html note.html}</p>
|
||||||
{#if link}
|
{#if link}
|
||||||
<div class="mt-2" on:click={e => e.stopPropagation()}>
|
<div>
|
||||||
<Preview endpoint={`${$settings.dufflepudUrl}/link/preview`} url={link} />
|
<div class="inline-block" on:click={e => e.stopPropagation()}>
|
||||||
|
<Preview endpoint={`${$settings.dufflepudUrl}/link/preview`} url={link} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
</div>
|
||||||
<div class="flex gap-6 text-light">
|
<div class="flex gap-6 text-light">
|
||||||
<div>
|
<div>
|
||||||
<i
|
<i
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
export let note
|
export let note
|
||||||
|
|
||||||
const observable = relay.lq(() => relay.findNote(note, {showEntire: true}))
|
const observable = relay.lq(() => relay.findNote(note.id, {showEntire: true}))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $observable}
|
{#if $observable}
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import {onDestroy} from 'svelte'
|
||||||
import {prop, identity, concat, uniqBy, groupBy} from 'ramda'
|
import {prop, identity, concat, uniqBy, groupBy} from 'ramda'
|
||||||
import {createMap} from 'hurdak/lib/hurdak'
|
import {createMap} from 'hurdak/lib/hurdak'
|
||||||
import {now, timedelta} from 'src/util/misc'
|
import {now, timedelta} from 'src/util/misc'
|
||||||
import {findReply, findRoot} from 'src/util/nostr'
|
import {findReply, findRoot} from 'src/util/nostr'
|
||||||
import {fly} from 'svelte/transition'
|
|
||||||
import {createScroller} from 'src/util/misc'
|
import {createScroller} from 'src/util/misc'
|
||||||
import Spinner from 'src/partials/Spinner.svelte'
|
import Spinner from 'src/partials/Spinner.svelte'
|
||||||
import Note from "src/views/Note.svelte"
|
import Note from "src/views/Note.svelte"
|
||||||
import relay from 'src/relay'
|
import relay from 'src/relay'
|
||||||
|
|
||||||
export let filter
|
export let filter
|
||||||
|
export let showParent = false
|
||||||
export let shouldMuffle = false
|
export let shouldMuffle = false
|
||||||
|
export let delta = timedelta(10, 'minutes')
|
||||||
|
|
||||||
let limit = 10, init = now(), offset = 0, notes
|
let since = now() - delta, until = now(), notes
|
||||||
|
|
||||||
const onScroll = createScroller(async () => {
|
const done = createScroller(async () => {
|
||||||
limit += 10
|
since -= delta
|
||||||
offset += 1
|
until -= delta
|
||||||
|
|
||||||
const delta = timedelta(1, 'minutes')
|
await relay.ensureContext(
|
||||||
const since = init - delta * offset
|
await relay.pool.loadEvents({...filter, since, until})
|
||||||
const until = init - delta * (offset - 1)
|
)
|
||||||
|
|
||||||
await relay.pool.loadEvents({...filter, since, until})
|
|
||||||
|
|
||||||
createNotesObservable()
|
createNotesObservable()
|
||||||
})
|
})
|
||||||
|
|
||||||
const createNotesObservable = () => {
|
const createNotesObservable = () => {
|
||||||
notes = relay.lq(async () => {
|
notes = relay.lq(async () => {
|
||||||
const notes = await relay.filterEvents(filter).limit(limit).reverse().sortBy('created_at')
|
const notes = await relay.filterEvents({...filter, since}).reverse().sortBy('created_at')
|
||||||
const ancestorIds = concat(notes.map(findRoot), notes.map(findReply)).filter(identity)
|
const ancestorIds = concat(notes.map(findRoot), notes.map(findReply)).filter(identity)
|
||||||
const ancestors = await relay.filterEvents({kinds: [1], ids: ancestorIds}).toArray()
|
const ancestors = await relay.filterEvents({kinds: [1], ids: ancestorIds}).toArray()
|
||||||
|
|
||||||
@ -58,23 +58,13 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
createNotesObservable()
|
onDestroy(done)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:scroll={onScroll} />
|
|
||||||
|
|
||||||
<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)}
|
{#each ($notes || []) as n (n.id)}
|
||||||
<li><Note interactive note={n} depth={2} /></li>
|
<li><Note interactive note={n} depth={2} {showParent} /></li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{#if $notes?.length === 0}
|
|
||||||
<div in:fly={{y: 20}} class="flex w-full justify-center items-center py-16">
|
|
||||||
<div class="text-center max-w-md">
|
|
||||||
No notes found.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{/if}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user