mirror of
https://github.com/coracle-social/coracle.git
synced 2024-10-06 11:43:30 +00:00
Clean up pool so we're not waiting for slow connections
This commit is contained in:
parent
dcd8de7912
commit
fc984cafa6
@ -1,26 +1,32 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
"env": {
|
root: true,
|
||||||
"browser": true,
|
env: {
|
||||||
"es2021": true
|
browser: true,
|
||||||
|
es2021: true
|
||||||
},
|
},
|
||||||
"plugins": [
|
plugins: ['svelte3', '@typescript-eslint'],
|
||||||
'svelte3'
|
|
||||||
],
|
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: ['*.svelte'],
|
files: ['*.svelte'],
|
||||||
processor: 'svelte3/svelte3'
|
processor: 'svelte3/svelte3'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"extends": "eslint:recommended",
|
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
|
||||||
"parserOptions": {
|
parser: '@typescript-eslint/parser',
|
||||||
"ecmaVersion": "latest",
|
parserOptions: {
|
||||||
"sourceType": "module"
|
ecmaVersion: "latest",
|
||||||
|
sourceType: "module",
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: ['./tsconfig.json'],
|
||||||
|
extraFileExtensions: ['.svelte']
|
||||||
},
|
},
|
||||||
"rules": {
|
settings: {
|
||||||
|
'svelte3/typescript': require('typescript'),
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
"a11y-click-events-have-key-events": "off",
|
"a11y-click-events-have-key-events": "off",
|
||||||
"no-unused-vars": ["error", {args: "none"}],
|
"no-unused-vars": ["error", {args: "none"}],
|
||||||
"no-async-promise-executor": "off",
|
"no-async-promise-executor": "off",
|
||||||
},
|
},
|
||||||
"ignorePatterns": ["*.svg"]
|
ignorePatterns: ["*.svg"]
|
||||||
}
|
}
|
||||||
|
BIN
package-lock.json
generated
BIN
package-lock.json
generated
Binary file not shown.
@ -12,12 +12,15 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/vite-plugin-svelte": "^1.1.0",
|
"@sveltejs/vite-plugin-svelte": "^1.1.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.50.0",
|
||||||
|
"@typescript-eslint/parser": "^5.50.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"eslint": "^8.28.0",
|
"eslint": "^8.33.0",
|
||||||
"eslint-plugin-svelte3": "^4.0.0",
|
"eslint-plugin-svelte3": "^4.0.0",
|
||||||
"postcss": "^8.4.19",
|
"postcss": "^8.4.19",
|
||||||
"svelte": "^3.52.0",
|
"svelte": "^3.52.0",
|
||||||
"tailwindcss": "^3.2.4",
|
"tailwindcss": "^3.2.4",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
"vite": "^3.2.3"
|
"vite": "^3.2.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -71,7 +71,7 @@ export const publish = async (relays, event) => {
|
|||||||
return signedEvent
|
return signedEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
export const load = async (relays, filter, opts?) => {
|
export const load = async (relays, filter, opts?): Promise<Record<string, unknown>[]> => {
|
||||||
const events = await pool.request(relays, filter, opts)
|
const events = await pool.request(relays, filter, opts)
|
||||||
|
|
||||||
await processEvents(events)
|
await processEvents(events)
|
||||||
@ -79,18 +79,16 @@ export const load = async (relays, filter, opts?) => {
|
|||||||
return events
|
return events
|
||||||
}
|
}
|
||||||
|
|
||||||
export const listen = async (relays, filter, onEvent, {shouldProcess = true}: any = {}) => {
|
export const listen = (relays, filter, onEvent, {shouldProcess = true}: any = {}) => {
|
||||||
const sub = await pool.subscribe(relays, filter)
|
return pool.subscribe(relays, filter, {
|
||||||
|
onEvent: e => {
|
||||||
|
if (shouldProcess) {
|
||||||
|
processEvents(e)
|
||||||
|
}
|
||||||
|
|
||||||
sub.onEvent(e => {
|
if (onEvent) {
|
||||||
if (shouldProcess) {
|
onEvent(e)
|
||||||
processEvents(e)
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
if (onEvent) {
|
|
||||||
onEvent(e)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return sub
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
import type {Relay} from 'nostr-tools'
|
||||||
import {relayInit} from 'nostr-tools'
|
import {relayInit} from 'nostr-tools'
|
||||||
import {uniqBy, reject, prop, find, whereEq, is, filter, identity} from 'ramda'
|
import {uniqBy, reject, prop, find, whereEq, is} from 'ramda'
|
||||||
import {ensurePlural} from 'hurdak/lib/hurdak'
|
import {ensurePlural} from 'hurdak/lib/hurdak'
|
||||||
import {isRelay} from 'src/util/nostr'
|
import {isRelay} from 'src/util/nostr'
|
||||||
import {sleep} from 'src/util/misc'
|
import {sleep} from 'src/util/misc'
|
||||||
@ -8,6 +9,12 @@ import {db} from 'src/agent/data'
|
|||||||
let connections = []
|
let connections = []
|
||||||
|
|
||||||
class Connection {
|
class Connection {
|
||||||
|
promise: Promise<void>
|
||||||
|
nostr: Relay
|
||||||
|
status: string
|
||||||
|
url: string
|
||||||
|
stats: Record<string, number>
|
||||||
|
lastRequest: number
|
||||||
constructor(url) {
|
constructor(url) {
|
||||||
this.promise = null
|
this.promise = null
|
||||||
this.nostr = this.init(url)
|
this.nostr = this.init(url)
|
||||||
@ -107,7 +114,7 @@ const describeFilter = ({kinds = [], ...filter}) => {
|
|||||||
return '(' + parts.join(',') + ')'
|
return '(' + parts.join(',') + ')'
|
||||||
}
|
}
|
||||||
|
|
||||||
const subscribe = async (relays, filters) => {
|
const subscribe = async (relays, filters, {onEvent, onEose}: Record<string, (e: any) => void>) => {
|
||||||
relays = uniqBy(prop('url'), relays.filter(r => isRelay(r.url)))
|
relays = uniqBy(prop('url'), relays.filter(r => isRelay(r.url)))
|
||||||
filters = ensurePlural(filters)
|
filters = ensurePlural(filters)
|
||||||
|
|
||||||
@ -117,151 +124,101 @@ const subscribe = async (relays, filters) => {
|
|||||||
filters.map(describeFilter).join(':'),
|
filters.map(describeFilter).join(':'),
|
||||||
].join('-')
|
].join('-')
|
||||||
|
|
||||||
|
// Deduplicate events
|
||||||
const seen = new Set()
|
const seen = new Set()
|
||||||
const eose = []
|
|
||||||
const events = []
|
|
||||||
let onEvent
|
|
||||||
let onEose
|
|
||||||
|
|
||||||
const subs = filter(identity, await Promise.all(
|
// Don't await before returning so we're not blocking on slow connects
|
||||||
relays.map(async relay => {
|
const promises = relays.map(async relay => {
|
||||||
const conn = await connect(relay.url)
|
const conn = await connect(relay.url)
|
||||||
|
|
||||||
// If the relay failed to connect, give up
|
// If the relay failed to connect, give up
|
||||||
if (!conn) {
|
if (!conn) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const sub = conn.nostr.sub(filters, {id})
|
const sub = conn.nostr.sub(filters, {id})
|
||||||
|
|
||||||
// Subscribe to events immediately so we don't miss any while we
|
if (onEvent) {
|
||||||
// wait for slow relays to connect
|
|
||||||
sub.on('event', e => {
|
sub.on('event', e => {
|
||||||
if (!seen.has(e.id)) {
|
if (!seen.has(e.id)) {
|
||||||
e.seen_on = sub.conn.url
|
|
||||||
seen.add(e.id)
|
seen.add(e.id)
|
||||||
|
|
||||||
if (onEvent) {
|
onEvent(Object.assign(e, {seen_on: conn.url}))
|
||||||
onEvent(e)
|
|
||||||
} else {
|
|
||||||
events.push(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Same thing for eose
|
if (onEose) {
|
||||||
sub.on('eose', () => {
|
sub.on('eose', () => onEose(conn.url))
|
||||||
if (onEose) {
|
}
|
||||||
onEose(conn.url)
|
|
||||||
} else {
|
|
||||||
eose.push(conn.url)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
sub.conn = conn
|
conn.stats.activeCount += 1
|
||||||
sub.conn.stats.activeCount += 1
|
|
||||||
|
|
||||||
if (sub.conn.stats.activeCount > 10) {
|
if (conn.stats.activeCount > 10) {
|
||||||
console.warn(`Relay ${sub.conn.url} has >10 active subscriptions`)
|
console.warn(`Relay ${conn.url} has >10 active subscriptions`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sub
|
return Object.assign(sub, {conn})
|
||||||
})
|
})
|
||||||
))
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subs,
|
|
||||||
unsub: () => {
|
unsub: () => {
|
||||||
subs.forEach(sub => {
|
promises.forEach(async promise => {
|
||||||
if (sub.conn.status === 'ready') {
|
const sub = await promise
|
||||||
sub.unsub()
|
|
||||||
|
if (sub) {
|
||||||
|
if (sub.conn.status === 'ready') {
|
||||||
|
sub.unsub()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub.conn.stats.activeCount -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
sub.conn.stats.activeCount -= 1
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onEvent: cb => {
|
|
||||||
// Report our buffered events
|
|
||||||
events.splice(0).forEach(e => cb(e))
|
|
||||||
|
|
||||||
// Add our listener for future ones
|
|
||||||
onEvent = cb
|
|
||||||
},
|
|
||||||
onEose: cb => {
|
|
||||||
// Report our buffered eoses
|
|
||||||
eose.splice(0).forEach(e => cb(e))
|
|
||||||
|
|
||||||
// Add our listener for future ones
|
|
||||||
onEose = cb
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const request = (relays, filters, {threshold = 1} = {}) => {
|
const request = (relays, filters, {threshold = 1} = {}): Promise<Record<string, unknown>[]> => {
|
||||||
relays = uniqBy(prop('url'), relays.filter(r => isRelay(r.url)))
|
|
||||||
|
|
||||||
return new Promise(async resolve => {
|
return new Promise(async resolve => {
|
||||||
const agg = await subscribe(relays, filters)
|
relays = uniqBy(prop('url'), relays.filter(r => isRelay(r.url)))
|
||||||
|
threshold = Math.min(relays.length, threshold)
|
||||||
|
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
const relaysWithEvents = new Set()
|
const relaysWithEvents = new Set()
|
||||||
const events = []
|
const events = []
|
||||||
const eose = []
|
const eose = []
|
||||||
|
|
||||||
const attemptToComplete = () => {
|
const attemptToComplete = () => {
|
||||||
// If we have all relays, more than `threshold` reporting events, most after
|
if (eose.length >= threshold || Date.now() - now >= 5000) {
|
||||||
// a short timeout, or all after a long timeout, go ahead and unsubscribe.
|
|
||||||
const done = (
|
|
||||||
eose.length === agg.subs.length
|
|
||||||
|| eose.filter(url => relaysWithEvents.has(url)).length >= threshold
|
|
||||||
|| (
|
|
||||||
Date.now() - now >= 1000
|
|
||||||
&& eose.length > agg.subs.length - Math.round(agg.subs.length / 10)
|
|
||||||
)
|
|
||||||
|| Date.now() - now >= 5000
|
|
||||||
)
|
|
||||||
|
|
||||||
if (done) {
|
|
||||||
agg.unsub()
|
agg.unsub()
|
||||||
resolve(events)
|
resolve(events)
|
||||||
|
|
||||||
// Keep track of relay timeouts
|
|
||||||
agg.subs.forEach(async sub => {
|
|
||||||
if (!eose.includes(sub.conn.url)) {
|
|
||||||
const conn = findConnection(sub.conn.url)
|
|
||||||
|
|
||||||
if (conn) {
|
|
||||||
conn.stats.count += 1
|
|
||||||
conn.stats.timer += Date.now() - now
|
|
||||||
conn.stats.timeouts += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
agg.onEvent(e => {
|
|
||||||
relaysWithEvents.add(e.seen_on)
|
|
||||||
events.push(e)
|
|
||||||
})
|
|
||||||
|
|
||||||
agg.onEose(async url => {
|
|
||||||
if (!eose.includes(url)) {
|
|
||||||
eose.push(url)
|
|
||||||
|
|
||||||
const conn = findConnection(url)
|
|
||||||
|
|
||||||
// Keep track of relay timing stats
|
|
||||||
if (conn) {
|
|
||||||
conn.stats.count += 1
|
|
||||||
conn.stats.timer += Date.now() - now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
attemptToComplete()
|
|
||||||
})
|
|
||||||
|
|
||||||
// If a relay takes too long, give up
|
// If a relay takes too long, give up
|
||||||
setTimeout(attemptToComplete, 5000)
|
setTimeout(attemptToComplete, 5000)
|
||||||
|
|
||||||
|
const agg = await subscribe(relays, filters, {
|
||||||
|
onEvent: e => {
|
||||||
|
relaysWithEvents.add(e.seen_on)
|
||||||
|
events.push(e)
|
||||||
|
},
|
||||||
|
onEose: async url => {
|
||||||
|
if (!eose.includes(url)) {
|
||||||
|
eose.push(url)
|
||||||
|
|
||||||
|
const conn = findConnection(url)
|
||||||
|
|
||||||
|
// Keep track of relay timing stats
|
||||||
|
if (conn) {
|
||||||
|
conn.stats.count += 1
|
||||||
|
conn.stats.timer += Date.now() - now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attemptToComplete()
|
||||||
|
},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -30,7 +30,7 @@ const loadPeople = (relays, pubkeys, {kinds = personKinds, force = false, ...opt
|
|||||||
const loadNetwork = async (relays, pubkey) => {
|
const loadNetwork = async (relays, pubkey) => {
|
||||||
// Get this user's profile to start with. This may update what relays
|
// Get this user's profile to start with. This may update what relays
|
||||||
// are available, so don't assign relays to a variable here.
|
// are available, so don't assign relays to a variable here.
|
||||||
let events = pubkey ? await loadPeople(relays, [pubkey], {force: true}) : []
|
const events = pubkey ? await loadPeople(relays, [pubkey], {force: true}) : []
|
||||||
let petnames = Tags.from(events.filter(e => e.kind === 3)).type("p").all()
|
let petnames = Tags.from(events.filter(e => e.kind === 3)).type("p").all()
|
||||||
|
|
||||||
// Default to some cool guys we know
|
// Default to some cool guys we know
|
||||||
@ -58,7 +58,7 @@ const loadContext = async (relays, notes, {loadParents = false, depth = 0, ...op
|
|||||||
const parentTags = uniq(chunk.map(findReply).filter(identity))
|
const parentTags = uniq(chunk.map(findReply).filter(identity))
|
||||||
const parentIds = Tags.wrap(parentTags).values().all()
|
const parentIds = Tags.wrap(parentTags).values().all()
|
||||||
const combinedRelays = uniq(relays.concat(Tags.wrap(parentTags).relays()))
|
const combinedRelays = uniq(relays.concat(Tags.wrap(parentTags).relays()))
|
||||||
const filter = [{kinds: [1, 7], '#e': chunkIds} as {}]
|
const filter = [{kinds: [1, 7], '#e': chunkIds} as object]
|
||||||
|
|
||||||
if (authors.length > 0) {
|
if (authors.length > 0) {
|
||||||
filter.push({kinds: personKinds, authors})
|
filter.push({kinds: personKinds, authors})
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
switcher(type, {
|
switcher(type, {
|
||||||
anchor: "underline",
|
anchor: "underline",
|
||||||
button: "py-2 px-4 rounded bg-white text-accent",
|
button: "py-2 px-4 rounded bg-white text-accent",
|
||||||
|
'button-circle': "w-10 h-10 flex justify-center items-center rounded-full bg-white text-accent",
|
||||||
'button-accent': "py-2 px-4 rounded bg-accent text-white",
|
'button-accent': "py-2 px-4 rounded bg-accent text-white",
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
import Compose from "src/partials/Compose.svelte"
|
import Compose from "src/partials/Compose.svelte"
|
||||||
import Card from "src/partials/Card.svelte"
|
import Card from "src/partials/Card.svelte"
|
||||||
import {user, people, getPerson, getRelays, getEventRelays} from 'src/agent'
|
import {user, people, getPerson, getRelays, getEventRelays} from 'src/agent'
|
||||||
import {addRelay, removeRelay} from "src/app"
|
|
||||||
import cmd from 'src/app/cmd'
|
import cmd from 'src/app/cmd'
|
||||||
|
|
||||||
export let note
|
export let note
|
||||||
|
@ -44,11 +44,6 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
const loadMessages = async ({until, limit}) => {
|
const loadMessages = async ({until, limit}) => {
|
||||||
const a = db.table('messages')
|
|
||||||
const b = a.where('pubkey')
|
|
||||||
const c = b.equals(pubkey)
|
|
||||||
const d = c.toArray()
|
|
||||||
const e = await d
|
|
||||||
const fromThem = await db.table('messages').where('pubkey').equals(pubkey).toArray()
|
const fromThem = await db.table('messages').where('pubkey').equals(pubkey).toArray()
|
||||||
const toThem = await db.table('messages').where('recipient').equals(pubkey).toArray()
|
const toThem = await db.table('messages').where('recipient').equals(pubkey).toArray()
|
||||||
const events = fromThem.concat(toThem).filter(e => e.created_at < until)
|
const events = fromThem.concat(toThem).filter(e => e.created_at < until)
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
import Tabs from "src/partials/Tabs.svelte"
|
import Tabs from "src/partials/Tabs.svelte"
|
||||||
import Content from "src/partials/Content.svelte"
|
import Content from "src/partials/Content.svelte"
|
||||||
import Anchor from "src/partials/Anchor.svelte"
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
import Button from "src/partials/Button.svelte"
|
|
||||||
import Spinner from "src/partials/Spinner.svelte"
|
import Spinner from "src/partials/Spinner.svelte"
|
||||||
import Notes from "src/views/person/Notes.svelte"
|
import Notes from "src/views/person/Notes.svelte"
|
||||||
import Likes from "src/views/person/Likes.svelte"
|
import Likes from "src/views/person/Likes.svelte"
|
||||||
@ -92,7 +91,7 @@
|
|||||||
<Content>
|
<Content>
|
||||||
<div class="flex gap-4" in:fly={{y: 20}}>
|
<div class="flex gap-4" in:fly={{y: 20}}>
|
||||||
<div
|
<div
|
||||||
class="overflow-hidden w-32 h-32 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
|
class="overflow-hidden w-16 h-16 sm:w-32 sm:h-32 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
|
||||||
style="background-image: url({person.picture})" />
|
style="background-image: url({person.picture})" />
|
||||||
<div class="flex flex-col gap-4 flex-grow">
|
<div class="flex flex-col gap-4 flex-grow">
|
||||||
<div class="flex justify-between items-center gap-4">
|
<div class="flex justify-between items-center gap-4">
|
||||||
@ -107,22 +106,24 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="whitespace-nowrap flex gap-4 items-center">
|
<div class="whitespace-nowrap flex gap-3 items-center flex-wrap">
|
||||||
{#if $user?.pubkey === pubkey && keys.canSign()}
|
{#if $user?.pubkey === pubkey && keys.canSign()}
|
||||||
<Anchor type="button" href="/profile">
|
<Anchor href="/profile"><i class="fa-solid fa-edit" /> Edit profile</Anchor>
|
||||||
<i class="fa-solid fa-edit" /> Edit profile
|
|
||||||
</Anchor>
|
|
||||||
{:else if $user && keys.canSign()}
|
{:else if $user && keys.canSign()}
|
||||||
<Anchor type="button" on:click={openAdvanced}>
|
<Anchor type="button-circle" on:click={openAdvanced}>
|
||||||
<i class="fa-solid fa-sliders" />
|
<i class="fa fa-sliders" />
|
||||||
</Anchor>
|
</Anchor>
|
||||||
<Anchor type="button" href={`/messages/${npub}`}>
|
<Anchor type="button-circle" href={`/messages/${npub}`}>
|
||||||
<i class="fa-solid fa-envelope" />
|
<i class="fa fa-envelope" />
|
||||||
</Anchor>
|
</Anchor>
|
||||||
{#if following}
|
{#if following}
|
||||||
<Button on:click={unfollow}>Unfollow</Button>
|
<Anchor type="button-circle" on:click={unfollow}>
|
||||||
|
<i class="fa fa-user-minus" />
|
||||||
|
</Anchor>
|
||||||
{:else}
|
{:else}
|
||||||
<Button on:click={follow}>Follow</Button>
|
<Anchor type="button-circle" on:click={follow}>
|
||||||
|
<i class="fa fa-user-plus" />
|
||||||
|
</Anchor>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -100,7 +100,6 @@ export const createScroller = (loadMore, {reverse = false} = {}) => {
|
|||||||
// loadMore function is not properly awaiting all the work necessary.
|
// loadMore function is not properly awaiting all the work necessary.
|
||||||
// That is the problem, but see commit 8371fde for another strategy
|
// That is the problem, but see commit 8371fde for another strategy
|
||||||
let done = false
|
let done = false
|
||||||
let timeout = null
|
|
||||||
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
|
||||||
@ -111,8 +110,6 @@ export const createScroller = (loadMore, {reverse = false} = {}) => {
|
|||||||
|
|
||||||
// Only trigger loading the first time we reach the threshold
|
// Only trigger loading the first time we reach the threshold
|
||||||
if (shouldLoad) {
|
if (shouldLoad) {
|
||||||
clearTimeout(timeout)
|
|
||||||
|
|
||||||
await loadMore()
|
await loadMore()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user