fix some feed loading bugs
This commit is contained in:
parent
10119aaa6d
commit
77ac59008b
11 changed files with 69 additions and 44 deletions
|
|
@ -10,6 +10,7 @@
|
||||||
follow,
|
follow,
|
||||||
unfollow,
|
unfollow,
|
||||||
tagPubkey,
|
tagPubkey,
|
||||||
|
loadRelayList,
|
||||||
} from "@welshman/app"
|
} from "@welshman/app"
|
||||||
import {compressFile} from "@lib/html"
|
import {compressFile} from "@lib/html"
|
||||||
import Copy from "@assets/icons/copy.svg?dataurl"
|
import Copy from "@assets/icons/copy.svg?dataurl"
|
||||||
|
|
@ -24,6 +25,7 @@
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Link from "@lib/components/Link.svelte"
|
import Link from "@lib/components/Link.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
|
import Spinner from "@lib/components/Spinner.svelte"
|
||||||
import Popover from "@lib/components/Popover.svelte"
|
import Popover from "@lib/components/Popover.svelte"
|
||||||
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
||||||
import ProfileTrust from "@app/components/ProfileTrust.svelte"
|
import ProfileTrust from "@app/components/ProfileTrust.svelte"
|
||||||
|
|
@ -254,7 +256,14 @@
|
||||||
<ProfileTrust pubkey={target} {isSelf} />
|
<ProfileTrust pubkey={target} {isSelf} />
|
||||||
<ProfileSharedSpaces pubkey={target} {isSelf} />
|
<ProfileSharedSpaces pubkey={target} {isSelf} />
|
||||||
</div>
|
</div>
|
||||||
|
{#await loadRelayList(target)}
|
||||||
|
<p class="my-12 flex items-center justify-center gap-2">
|
||||||
|
<Spinner loading />
|
||||||
|
Loading notes...
|
||||||
|
</p>
|
||||||
|
{:then}
|
||||||
<ProfilePageNotes pubkey={target} />
|
<ProfilePageNotes pubkey={target} />
|
||||||
|
{/await}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
import {derived, writable} from "svelte/store"
|
import {derived, writable} from "svelte/store"
|
||||||
import type {Writable} from "svelte/store"
|
import type {Writable} from "svelte/store"
|
||||||
import {sortBy, now} from "@welshman/lib"
|
import {sortBy, uniqBy, now} from "@welshman/lib"
|
||||||
import {NOTE, getReplyTags, getListTags, getEventTagValues} from "@welshman/util"
|
import {NOTE, getReplyTags, getListTags, getEventTagValues} from "@welshman/util"
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
import type {TrustedEvent} from "@welshman/util"
|
||||||
import {derivePinList} from "@welshman/app"
|
import {derivePinList} from "@welshman/app"
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
relays: Router.get().FromPubkeys([pubkey]).getUrls(),
|
relays: Router.get().FromPubkeys([pubkey]).getUrls(),
|
||||||
filters: [{ids: $pinnedIds}],
|
filters: [{ids: $pinnedIds}],
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
onEvent: e => events.update($events => $events.concat(e)),
|
onEvent: e => events.update($events => uniqBy(e => e.id, $events.concat(e))),
|
||||||
})
|
})
|
||||||
|
|
||||||
return () => controller.abort()
|
return () => controller.abort()
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const feed = makeFeed({
|
const feed = makeFeed({
|
||||||
url: Router.get().FromPubkeys([pubkey]).getUrls()[0],
|
relays: Router.get().FromPubkeys([pubkey]).getUrls(),
|
||||||
element: element!,
|
element: element!,
|
||||||
filters: [{kinds: [NOTE], authors: [pubkey]}],
|
filters: [{kinds: [NOTE], authors: [pubkey]}],
|
||||||
onBackwardExhausted: () => {
|
onBackwardExhausted: () => {
|
||||||
|
|
@ -75,8 +75,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{#if !exhausted}
|
{#if !exhausted}
|
||||||
<p class="my-12 flex items-center justify-center">
|
<p class="my-12 flex items-center justify-center gap-2">
|
||||||
<Spinner loading />
|
<Spinner loading />
|
||||||
|
Loading notes...
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import {
|
||||||
now,
|
now,
|
||||||
on,
|
on,
|
||||||
sortBy,
|
sortBy,
|
||||||
|
uniqBy,
|
||||||
MONTH,
|
MONTH,
|
||||||
YEAR,
|
YEAR,
|
||||||
} from "@welshman/lib"
|
} from "@welshman/lib"
|
||||||
|
|
@ -105,14 +106,14 @@ export const userFeedFavorites = makeUserData(feedFavoritesByPubkey, loadFeedFav
|
||||||
export const loadUserFeedFavorites = makeUserLoader(loadFeedFavorites)
|
export const loadUserFeedFavorites = makeUserLoader(loadFeedFavorites)
|
||||||
|
|
||||||
export const makeFeed = ({
|
export const makeFeed = ({
|
||||||
url,
|
relays,
|
||||||
filters,
|
filters,
|
||||||
element,
|
element,
|
||||||
onBackwardExhausted,
|
onBackwardExhausted,
|
||||||
onForwardExhausted,
|
onForwardExhausted,
|
||||||
at = now(),
|
at = now(),
|
||||||
}: {
|
}: {
|
||||||
url: string
|
relays: string[]
|
||||||
filters: Filter[]
|
filters: Filter[]
|
||||||
element: HTMLElement
|
element: HTMLElement
|
||||||
onBackwardExhausted?: () => void
|
onBackwardExhausted?: () => void
|
||||||
|
|
@ -121,9 +122,15 @@ export const makeFeed = ({
|
||||||
}) => {
|
}) => {
|
||||||
const controller = new AbortController()
|
const controller = new AbortController()
|
||||||
const events = writable<TrustedEvent[]>([])
|
const events = writable<TrustedEvent[]>([])
|
||||||
|
const seen = new Set<string>()
|
||||||
|
|
||||||
let interval = int(MONTH)
|
let interval = int(MONTH)
|
||||||
let buffer = sortEventsDesc(getEventsForUrl(url, filters))
|
let buffer = sortEventsDesc(
|
||||||
|
uniqBy(
|
||||||
|
e => e.id,
|
||||||
|
relays.flatMap(url => Array.from(getEventsForUrl(url, filters))),
|
||||||
|
),
|
||||||
|
)
|
||||||
let backwardWindow = [at - interval, at]
|
let backwardWindow = [at - interval, at]
|
||||||
let forwardWindow = [at, at + interval]
|
let forwardWindow = [at, at + interval]
|
||||||
|
|
||||||
|
|
@ -142,6 +149,12 @@ export const makeFeed = ({
|
||||||
const visible: TrustedEvent[] = []
|
const visible: TrustedEvent[] = []
|
||||||
|
|
||||||
for (const event of newEvents) {
|
for (const event of newEvents) {
|
||||||
|
if (seen.has(event.id)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
seen.add(event.id)
|
||||||
|
|
||||||
if (between([backwardWindow[0], forwardWindow[1]], event.created_at)) {
|
if (between([backwardWindow[0], forwardWindow[1]], event.created_at)) {
|
||||||
visible.push(event)
|
visible.push(event)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -164,6 +177,7 @@ export const makeFeed = ({
|
||||||
$events = [...$events, event]
|
$events = [...$events, event]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $events
|
return $events
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +196,9 @@ export const makeFeed = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const matching = added.filter(
|
const matching = added.filter(
|
||||||
event => matchFilters(filters, event) && tracker.getRelays(event.id).has(url),
|
event =>
|
||||||
|
matchFilters(filters, event) &&
|
||||||
|
relays.some(url => tracker.getRelays(event.id).has(url)),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (matching.length > 0) {
|
if (matching.length > 0) {
|
||||||
|
|
@ -190,8 +206,8 @@ export const makeFeed = ({
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
on(tracker, "add", (id: string, trackerUrl: string) => {
|
on(tracker, "add", (id: string, url: string) => {
|
||||||
if (trackerUrl === url) {
|
if (relays.includes(url)) {
|
||||||
const event = repository.getEvent(id)
|
const event = repository.getEvent(id)
|
||||||
|
|
||||||
if (event && matchFilters(filters, event)) {
|
if (event && matchFilters(filters, event)) {
|
||||||
|
|
@ -203,7 +219,7 @@ export const makeFeed = ({
|
||||||
|
|
||||||
const loadTimeframe = async (since: number, until: number) => {
|
const loadTimeframe = async (since: number, until: number) => {
|
||||||
const events = await request({
|
const events = await request({
|
||||||
relays: [url],
|
relays,
|
||||||
autoClose: true,
|
autoClose: true,
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
filters: filters.map(filter => ({...filter, since, until})),
|
filters: filters.map(filter => ({...filter, since, until})),
|
||||||
|
|
@ -270,18 +286,19 @@ export const makeFeed = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
export const makeCalendarFeed = ({
|
export const makeCalendarFeed = ({
|
||||||
url,
|
relays,
|
||||||
filters,
|
filters,
|
||||||
element,
|
element,
|
||||||
onExhausted,
|
onExhausted,
|
||||||
}: {
|
}: {
|
||||||
url: string
|
relays: string[]
|
||||||
filters: Filter[]
|
filters: Filter[]
|
||||||
element: HTMLElement
|
element: HTMLElement
|
||||||
onExhausted?: () => void
|
onExhausted?: () => void
|
||||||
}) => {
|
}) => {
|
||||||
const interval = int(5, MONTH)
|
const interval = int(5, MONTH)
|
||||||
const controller = new AbortController()
|
const controller = new AbortController()
|
||||||
|
const seen = new Set<string>()
|
||||||
|
|
||||||
let exhaustedScrollers = 0
|
let exhaustedScrollers = 0
|
||||||
let backwardWindow = [now() - interval, now()]
|
let backwardWindow = [now() - interval, now()]
|
||||||
|
|
@ -291,15 +308,26 @@ export const makeCalendarFeed = ({
|
||||||
|
|
||||||
const getEnd = (event: TrustedEvent) => parseInt(getTagValue("end", event.tags) || "")
|
const getEnd = (event: TrustedEvent) => parseInt(getTagValue("end", event.tags) || "")
|
||||||
|
|
||||||
const events = writable(sortBy(getStart, getEventsForUrl(url, filters)))
|
const events = writable(
|
||||||
|
sortBy(
|
||||||
|
getStart,
|
||||||
|
uniqBy(
|
||||||
|
e => e.id,
|
||||||
|
relays.flatMap(url => Array.from(getEventsForUrl(url, filters))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// Batch-insert calendar events into the store with a single update
|
// Batch-insert calendar events into the store with a single update
|
||||||
const insertEvents = (newEvents: TrustedEvent[]) => {
|
const insertEvents = (newEvents: TrustedEvent[]) => {
|
||||||
const valid = newEvents.filter(e => !isNaN(getStart(e)) && !isNaN(getEnd(e)))
|
const valid = newEvents.filter(e => !isNaN(getStart(e)) && !isNaN(getEnd(e)) && !seen.has(e.id))
|
||||||
|
|
||||||
if (valid.length === 0) return
|
if (valid.length === 0) return
|
||||||
|
|
||||||
events.update($events => {
|
events.update($events => {
|
||||||
for (const event of valid) {
|
for (const event of valid) {
|
||||||
|
seen.add(event.id)
|
||||||
|
|
||||||
const start = getStart(event)
|
const start = getStart(event)
|
||||||
const address = getAddress(event)
|
const address = getAddress(event)
|
||||||
|
|
||||||
|
|
@ -320,6 +348,7 @@ export const makeCalendarFeed = ({
|
||||||
$events = [...$events.filter(e => getAddress(e) !== address), event]
|
$events = [...$events.filter(e => getAddress(e) !== address), event]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $events
|
return $events
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -342,8 +371,8 @@ export const makeCalendarFeed = ({
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
on(tracker, "add", (id: string, trackerUrl: string) => {
|
on(tracker, "add", (id: string, url: string) => {
|
||||||
if (trackerUrl === url) {
|
if (relays.includes(url)) {
|
||||||
const event = repository.getEvent(id)
|
const event = repository.getEvent(id)
|
||||||
|
|
||||||
if (event && matchFilters(filters, event)) {
|
if (event && matchFilters(filters, event)) {
|
||||||
|
|
@ -357,7 +386,7 @@ export const makeCalendarFeed = ({
|
||||||
const hashes = daysBetween(since, until).map(String)
|
const hashes = daysBetween(since, until).map(String)
|
||||||
|
|
||||||
request({
|
request({
|
||||||
relays: [url],
|
relays,
|
||||||
autoClose: true,
|
autoClose: true,
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
filters: [{kinds: [EVENT_TIME], "#D": hashes}],
|
filters: [{kinds: [EVENT_TIME], "#D": hashes}],
|
||||||
|
|
|
||||||
|
|
@ -19,26 +19,18 @@
|
||||||
import {decodePubkey} from "@lib/util"
|
import {decodePubkey} from "@lib/util"
|
||||||
import Page from "@lib/components/Page.svelte"
|
import Page from "@lib/components/Page.svelte"
|
||||||
import PageContent from "@lib/components/PageContent.svelte"
|
import PageContent from "@lib/components/PageContent.svelte"
|
||||||
import Spinner from "@lib/components/Spinner.svelte"
|
|
||||||
import ProfilePage from "@app/components/ProfilePage.svelte"
|
import ProfilePage from "@app/components/ProfilePage.svelte"
|
||||||
import {loadGroupList} from "@app/groups"
|
import {loadGroupList} from "@app/groups"
|
||||||
|
|
||||||
const {npub} = $page.params as MakeNonOptional<typeof $page.params>
|
const {npub} = $page.params as MakeNonOptional<typeof $page.params>
|
||||||
|
|
||||||
let pubkey = $state<string | undefined>()
|
const pubkey = decodePubkey(npub)
|
||||||
let ready = $state(false)
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const decoded = decodePubkey(npub)
|
if (!pubkey) {
|
||||||
|
return goto("/people", {replaceState: true})
|
||||||
if (!decoded) {
|
|
||||||
goto("/people", {replaceState: true})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pubkey = decoded
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await loadProfile(pubkey)
|
await loadProfile(pubkey)
|
||||||
await loadRelayList(pubkey)
|
await loadRelayList(pubkey)
|
||||||
|
|
@ -66,8 +58,6 @@
|
||||||
relays: Router.get().FromPubkeys([pubkey]).getUrls(),
|
relays: Router.get().FromPubkeys([pubkey]).getUrls(),
|
||||||
filters,
|
filters,
|
||||||
})
|
})
|
||||||
|
|
||||||
ready = true
|
|
||||||
} catch {
|
} catch {
|
||||||
goto("/people", {replaceState: true})
|
goto("/people", {replaceState: true})
|
||||||
}
|
}
|
||||||
|
|
@ -76,12 +66,8 @@
|
||||||
|
|
||||||
<Page>
|
<Page>
|
||||||
<PageContent class="p-0 md:p-4">
|
<PageContent class="p-0 md:p-4">
|
||||||
{#if ready && pubkey}
|
{#if pubkey}
|
||||||
<ProfilePage {pubkey} />
|
<ProfilePage {pubkey} />
|
||||||
{:else}
|
|
||||||
<p class="flex items-center justify-center py-20">
|
|
||||||
<Spinner loading />
|
|
||||||
</p>
|
|
||||||
{/if}
|
{/if}
|
||||||
</PageContent>
|
</PageContent>
|
||||||
</Page>
|
</Page>
|
||||||
|
|
|
||||||
|
|
@ -379,7 +379,7 @@
|
||||||
cleanup?.()
|
cleanup?.()
|
||||||
|
|
||||||
const feed = makeFeed({
|
const feed = makeFeed({
|
||||||
url,
|
relays: [url],
|
||||||
at: at || now(),
|
at: at || now(),
|
||||||
element: element!,
|
element: element!,
|
||||||
filters: [{kinds: [MESSAGE, ROOM_ADD_MEMBER], "#h": [h]}],
|
filters: [{kinds: [MESSAGE, ROOM_ADD_MEMBER], "#h": [h]}],
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const feed = makeCalendarFeed({
|
const feed = makeCalendarFeed({
|
||||||
url,
|
relays: [url],
|
||||||
element: element!,
|
element: element!,
|
||||||
filters: [{kinds: [EVENT_TIME]}, makeCommentFilter([EVENT_TIME])],
|
filters: [{kinds: [EVENT_TIME]}, makeCommentFilter([EVENT_TIME])],
|
||||||
onExhausted: () => {
|
onExhausted: () => {
|
||||||
|
|
|
||||||
|
|
@ -260,7 +260,7 @@
|
||||||
cleanup?.()
|
cleanup?.()
|
||||||
|
|
||||||
const feed = makeFeed({
|
const feed = makeFeed({
|
||||||
url,
|
relays: [url],
|
||||||
at: at || now(),
|
at: at || now(),
|
||||||
element: element!,
|
element: element!,
|
||||||
filters: [{kinds: [MESSAGE, RELAY_ADD_MEMBER]}],
|
filters: [{kinds: [MESSAGE, RELAY_ADD_MEMBER]}],
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const feed = makeFeed({
|
const feed = makeFeed({
|
||||||
url,
|
relays: [url],
|
||||||
element: element!,
|
element: element!,
|
||||||
filters: [{kinds: [CLASSIFIED]}, makeCommentFilter([CLASSIFIED])],
|
filters: [{kinds: [CLASSIFIED]}, makeCommentFilter([CLASSIFIED])],
|
||||||
onBackwardExhausted: () => {
|
onBackwardExhausted: () => {
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const feed = makeFeed({
|
const feed = makeFeed({
|
||||||
url,
|
relays: [url],
|
||||||
element: element!,
|
element: element!,
|
||||||
filters: [{kinds: [ZAP_GOAL]}, makeCommentFilter([ZAP_GOAL])],
|
filters: [{kinds: [ZAP_GOAL]}, makeCommentFilter([ZAP_GOAL])],
|
||||||
onBackwardExhausted: () => {
|
onBackwardExhausted: () => {
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const feed = makeFeed({
|
const feed = makeFeed({
|
||||||
url,
|
relays: [url],
|
||||||
element: element!,
|
element: element!,
|
||||||
filters: [{kinds: [POLL]}, makeCommentFilter([POLL])],
|
filters: [{kinds: [POLL]}, makeCommentFilter([POLL])],
|
||||||
onBackwardExhausted: () => {
|
onBackwardExhausted: () => {
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const feed = makeFeed({
|
const feed = makeFeed({
|
||||||
url,
|
relays: [url],
|
||||||
element: element!,
|
element: element!,
|
||||||
filters: [{kinds: [THREAD]}, makeCommentFilter([THREAD])],
|
filters: [{kinds: [THREAD]}, makeCommentFilter([THREAD])],
|
||||||
onBackwardExhausted: () => {
|
onBackwardExhausted: () => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue