diff --git a/src/app/components/ProfilePage.svelte b/src/app/components/ProfilePage.svelte index e1155854..05afdabc 100644 --- a/src/app/components/ProfilePage.svelte +++ b/src/app/components/ProfilePage.svelte @@ -10,6 +10,7 @@ follow, unfollow, tagPubkey, + loadRelayList, } from "@welshman/app" import {compressFile} from "@lib/html" import Copy from "@assets/icons/copy.svg?dataurl" @@ -24,6 +25,7 @@ import Icon from "@lib/components/Icon.svelte" import Link from "@lib/components/Link.svelte" import Button from "@lib/components/Button.svelte" + import Spinner from "@lib/components/Spinner.svelte" import Popover from "@lib/components/Popover.svelte" import ProfileCircle from "@app/components/ProfileCircle.svelte" import ProfileTrust from "@app/components/ProfileTrust.svelte" @@ -254,7 +256,14 @@ - + {#await loadRelayList(target)} +

+ + Loading notes... +

+ {:then} + + {/await} diff --git a/src/app/components/ProfilePageNotes.svelte b/src/app/components/ProfilePageNotes.svelte index 8664cae8..45106bee 100644 --- a/src/app/components/ProfilePageNotes.svelte +++ b/src/app/components/ProfilePageNotes.svelte @@ -2,7 +2,7 @@ import {onMount} from "svelte" import {derived, 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 type {TrustedEvent} from "@welshman/util" import {derivePinList} from "@welshman/app" @@ -30,7 +30,7 @@ relays: Router.get().FromPubkeys([pubkey]).getUrls(), filters: [{ids: $pinnedIds}], 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() @@ -50,7 +50,7 @@ onMount(() => { const feed = makeFeed({ - url: Router.get().FromPubkeys([pubkey]).getUrls()[0], + relays: Router.get().FromPubkeys([pubkey]).getUrls(), element: element!, filters: [{kinds: [NOTE], authors: [pubkey]}], onBackwardExhausted: () => { @@ -75,8 +75,9 @@ {/if} {/each} {#if !exhausted} -

+

+ Loading notes...

{/if} diff --git a/src/app/feeds.ts b/src/app/feeds.ts index 1f57f6eb..f42adc59 100644 --- a/src/app/feeds.ts +++ b/src/app/feeds.ts @@ -18,6 +18,7 @@ import { now, on, sortBy, + uniqBy, MONTH, YEAR, } from "@welshman/lib" @@ -105,14 +106,14 @@ export const userFeedFavorites = makeUserData(feedFavoritesByPubkey, loadFeedFav export const loadUserFeedFavorites = makeUserLoader(loadFeedFavorites) export const makeFeed = ({ - url, + relays, filters, element, onBackwardExhausted, onForwardExhausted, at = now(), }: { - url: string + relays: string[] filters: Filter[] element: HTMLElement onBackwardExhausted?: () => void @@ -121,9 +122,15 @@ export const makeFeed = ({ }) => { const controller = new AbortController() const events = writable([]) + const seen = new Set() 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 forwardWindow = [at, at + interval] @@ -142,6 +149,12 @@ export const makeFeed = ({ const visible: TrustedEvent[] = [] for (const event of newEvents) { + if (seen.has(event.id)) { + continue + } + + seen.add(event.id) + if (between([backwardWindow[0], forwardWindow[1]], event.created_at)) { visible.push(event) } else { @@ -164,6 +177,7 @@ export const makeFeed = ({ $events = [...$events, event] } } + return $events }) } @@ -182,7 +196,9 @@ export const makeFeed = ({ } 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) { @@ -190,8 +206,8 @@ export const makeFeed = ({ } }), ), - on(tracker, "add", (id: string, trackerUrl: string) => { - if (trackerUrl === url) { + on(tracker, "add", (id: string, url: string) => { + if (relays.includes(url)) { const event = repository.getEvent(id) if (event && matchFilters(filters, event)) { @@ -203,7 +219,7 @@ export const makeFeed = ({ const loadTimeframe = async (since: number, until: number) => { const events = await request({ - relays: [url], + relays, autoClose: true, signal: controller.signal, filters: filters.map(filter => ({...filter, since, until})), @@ -270,18 +286,19 @@ export const makeFeed = ({ } export const makeCalendarFeed = ({ - url, + relays, filters, element, onExhausted, }: { - url: string + relays: string[] filters: Filter[] element: HTMLElement onExhausted?: () => void }) => { const interval = int(5, MONTH) const controller = new AbortController() + const seen = new Set() let exhaustedScrollers = 0 let backwardWindow = [now() - interval, now()] @@ -291,15 +308,26 @@ export const makeCalendarFeed = ({ 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 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 events.update($events => { for (const event of valid) { + seen.add(event.id) + const start = getStart(event) const address = getAddress(event) @@ -320,6 +348,7 @@ export const makeCalendarFeed = ({ $events = [...$events.filter(e => getAddress(e) !== address), event] } } + return $events }) } @@ -342,8 +371,8 @@ export const makeCalendarFeed = ({ } }), ), - on(tracker, "add", (id: string, trackerUrl: string) => { - if (trackerUrl === url) { + on(tracker, "add", (id: string, url: string) => { + if (relays.includes(url)) { const event = repository.getEvent(id) if (event && matchFilters(filters, event)) { @@ -357,7 +386,7 @@ export const makeCalendarFeed = ({ const hashes = daysBetween(since, until).map(String) request({ - relays: [url], + relays, autoClose: true, signal: controller.signal, filters: [{kinds: [EVENT_TIME], "#D": hashes}], diff --git a/src/routes/people/[npub]/+page.svelte b/src/routes/people/[npub]/+page.svelte index 8377e6a8..ab90aaa2 100644 --- a/src/routes/people/[npub]/+page.svelte +++ b/src/routes/people/[npub]/+page.svelte @@ -19,26 +19,18 @@ import {decodePubkey} from "@lib/util" import Page from "@lib/components/Page.svelte" import PageContent from "@lib/components/PageContent.svelte" - import Spinner from "@lib/components/Spinner.svelte" import ProfilePage from "@app/components/ProfilePage.svelte" import {loadGroupList} from "@app/groups" const {npub} = $page.params as MakeNonOptional - let pubkey = $state() - let ready = $state(false) + const pubkey = decodePubkey(npub) onMount(async () => { - const decoded = decodePubkey(npub) - - if (!decoded) { - goto("/people", {replaceState: true}) - - return + if (!pubkey) { + return goto("/people", {replaceState: true}) } - pubkey = decoded - try { await loadProfile(pubkey) await loadRelayList(pubkey) @@ -66,8 +58,6 @@ relays: Router.get().FromPubkeys([pubkey]).getUrls(), filters, }) - - ready = true } catch { goto("/people", {replaceState: true}) } @@ -76,12 +66,8 @@ - {#if ready && pubkey} + {#if pubkey} - {:else} -

- -

{/if}
diff --git a/src/routes/spaces/[relay]/[h]/+page.svelte b/src/routes/spaces/[relay]/[h]/+page.svelte index 1877ad97..e2cb7c06 100644 --- a/src/routes/spaces/[relay]/[h]/+page.svelte +++ b/src/routes/spaces/[relay]/[h]/+page.svelte @@ -379,7 +379,7 @@ cleanup?.() const feed = makeFeed({ - url, + relays: [url], at: at || now(), element: element!, filters: [{kinds: [MESSAGE, ROOM_ADD_MEMBER], "#h": [h]}], diff --git a/src/routes/spaces/[relay]/calendar/+page.svelte b/src/routes/spaces/[relay]/calendar/+page.svelte index 0b04a925..894abbaa 100644 --- a/src/routes/spaces/[relay]/calendar/+page.svelte +++ b/src/routes/spaces/[relay]/calendar/+page.svelte @@ -95,7 +95,7 @@ onMount(() => { const feed = makeCalendarFeed({ - url, + relays: [url], element: element!, filters: [{kinds: [EVENT_TIME]}, makeCommentFilter([EVENT_TIME])], onExhausted: () => { diff --git a/src/routes/spaces/[relay]/chat/+page.svelte b/src/routes/spaces/[relay]/chat/+page.svelte index 4b796944..a86d9e12 100644 --- a/src/routes/spaces/[relay]/chat/+page.svelte +++ b/src/routes/spaces/[relay]/chat/+page.svelte @@ -260,7 +260,7 @@ cleanup?.() const feed = makeFeed({ - url, + relays: [url], at: at || now(), element: element!, filters: [{kinds: [MESSAGE, RELAY_ADD_MEMBER]}], diff --git a/src/routes/spaces/[relay]/classifieds/+page.svelte b/src/routes/spaces/[relay]/classifieds/+page.svelte index 7a6a8373..6292be45 100644 --- a/src/routes/spaces/[relay]/classifieds/+page.svelte +++ b/src/routes/spaces/[relay]/classifieds/+page.svelte @@ -46,7 +46,7 @@ onMount(() => { const feed = makeFeed({ - url, + relays: [url], element: element!, filters: [{kinds: [CLASSIFIED]}, makeCommentFilter([CLASSIFIED])], onBackwardExhausted: () => { diff --git a/src/routes/spaces/[relay]/goals/+page.svelte b/src/routes/spaces/[relay]/goals/+page.svelte index 9a1c5523..063823bf 100644 --- a/src/routes/spaces/[relay]/goals/+page.svelte +++ b/src/routes/spaces/[relay]/goals/+page.svelte @@ -46,7 +46,7 @@ onMount(() => { const feed = makeFeed({ - url, + relays: [url], element: element!, filters: [{kinds: [ZAP_GOAL]}, makeCommentFilter([ZAP_GOAL])], onBackwardExhausted: () => { diff --git a/src/routes/spaces/[relay]/polls/+page.svelte b/src/routes/spaces/[relay]/polls/+page.svelte index bcf2350b..13fffe84 100644 --- a/src/routes/spaces/[relay]/polls/+page.svelte +++ b/src/routes/spaces/[relay]/polls/+page.svelte @@ -46,7 +46,7 @@ onMount(() => { const feed = makeFeed({ - url, + relays: [url], element: element!, filters: [{kinds: [POLL]}, makeCommentFilter([POLL])], onBackwardExhausted: () => { diff --git a/src/routes/spaces/[relay]/threads/+page.svelte b/src/routes/spaces/[relay]/threads/+page.svelte index fdfb4b05..026c2f12 100644 --- a/src/routes/spaces/[relay]/threads/+page.svelte +++ b/src/routes/spaces/[relay]/threads/+page.svelte @@ -52,7 +52,7 @@ onMount(() => { const feed = makeFeed({ - url, + relays: [url], element: element!, filters: [{kinds: [THREAD]}, makeCommentFilter([THREAD])], onBackwardExhausted: () => {