Simplify profile notes loading
This commit is contained in:
parent
0b26eb8058
commit
10119aaa6d
7 changed files with 58 additions and 140 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
|
import {goto} from "$app/navigation"
|
||||||
import {displayProfileByPubkey, loadMessagingRelayList} from "@welshman/app"
|
import {displayProfileByPubkey, loadMessagingRelayList} from "@welshman/app"
|
||||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||||
import UserCircle from "@assets/icons/user-circle.svg?dataurl"
|
import UserCircle from "@assets/icons/user-circle.svg?dataurl"
|
||||||
|
|
@ -25,7 +26,7 @@
|
||||||
} from "@app/members"
|
} from "@app/members"
|
||||||
import {pushModal} from "@app/modal"
|
import {pushModal} from "@app/modal"
|
||||||
import {pushToast} from "@app/toast"
|
import {pushToast} from "@app/toast"
|
||||||
import {goToProfile} from "@app/routes"
|
import {makeProfilePath} from "@app/routes"
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
pubkey: string
|
pubkey: string
|
||||||
|
|
@ -42,7 +43,7 @@
|
||||||
|
|
||||||
const back = () => history.back()
|
const back = () => history.back()
|
||||||
|
|
||||||
const viewProfile = () => goToProfile(pubkey)
|
const viewProfile = () => goto(makeProfilePath(pubkey), {replaceState: true})
|
||||||
|
|
||||||
const toggleMenu = () => {
|
const toggleMenu = () => {
|
||||||
showMenu = !showMenu
|
showMenu = !showMenu
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
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"
|
||||||
import ProfileSharedSpaces from "@app/components/ProfileSharedSpaces.svelte"
|
import ProfileSharedSpaces from "@app/components/ProfileSharedSpaces.svelte"
|
||||||
import ProfilePinnedNotes from "@app/components/ProfilePinnedNotes.svelte"
|
|
||||||
import ProfilePageNotes from "@app/components/ProfilePageNotes.svelte"
|
import ProfilePageNotes from "@app/components/ProfilePageNotes.svelte"
|
||||||
import ProfileQrCode from "@app/components/ProfileQrCode.svelte"
|
import ProfileQrCode from "@app/components/ProfileQrCode.svelte"
|
||||||
import ProfileEdit from "@app/components/ProfileEdit.svelte"
|
import ProfileEdit from "@app/components/ProfileEdit.svelte"
|
||||||
|
|
@ -255,7 +254,6 @@
|
||||||
<ProfileTrust pubkey={target} {isSelf} />
|
<ProfileTrust pubkey={target} {isSelf} />
|
||||||
<ProfileSharedSpaces pubkey={target} {isSelf} />
|
<ProfileSharedSpaces pubkey={target} {isSelf} />
|
||||||
</div>
|
</div>
|
||||||
<ProfilePinnedNotes pubkey={target} />
|
|
||||||
<ProfilePageNotes pubkey={target} />
|
<ProfilePageNotes pubkey={target} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
import {derived, get, writable} from "svelte/store"
|
import {derived, writable} from "svelte/store"
|
||||||
import {sortBy, uniqBy} from "@welshman/lib"
|
import type {Writable} from "svelte/store"
|
||||||
import {feedFromFilter} from "@welshman/feeds"
|
import {sortBy, 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, makeFeedController} from "@welshman/app"
|
import {derivePinList} from "@welshman/app"
|
||||||
import {createScroller} from "@lib/html"
|
import {Router} from "@welshman/router"
|
||||||
|
import {load} from "@welshman/net"
|
||||||
import {fly} from "@lib/transition"
|
import {fly} from "@lib/transition"
|
||||||
import Spinner from "@lib/components/Spinner.svelte"
|
import Spinner from "@lib/components/Spinner.svelte"
|
||||||
import NoteItem from "@app/components/NoteItem.svelte"
|
import NoteItem from "@app/components/NoteItem.svelte"
|
||||||
|
import {makeFeed} from "@app/feeds"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
pubkey: string
|
pubkey: string
|
||||||
|
|
@ -18,51 +20,52 @@
|
||||||
const {pubkey}: Props = $props()
|
const {pubkey}: Props = $props()
|
||||||
|
|
||||||
const pinList = derivePinList(pubkey)
|
const pinList = derivePinList(pubkey)
|
||||||
const pinnedIds = derived(pinList, $pinList => new Set(getEventTagValues(getListTags($pinList))))
|
const pinnedIds = derived(pinList, $pinList => getEventTagValues(getListTags($pinList)))
|
||||||
|
|
||||||
let element: HTMLElement | undefined = $state()
|
$effect(() => {
|
||||||
const events = writable<TrustedEvent[]>([])
|
if ($pinnedIds.length > 0) {
|
||||||
let exhausted = $state(false)
|
const controller = new AbortController()
|
||||||
let buffer: TrustedEvent[] = []
|
|
||||||
|
|
||||||
const ctrl = makeFeedController({
|
load({
|
||||||
useWindowing: true,
|
relays: Router.get().FromPubkeys([pubkey]).getUrls(),
|
||||||
feed: feedFromFilter({kinds: [NOTE], authors: [pubkey]}),
|
filters: [{ids: $pinnedIds}],
|
||||||
onEvent: (event: TrustedEvent) => {
|
signal: controller.signal,
|
||||||
if (getReplyTags(event.tags).replies.length === 0 && !get(pinnedIds).has(event.id)) {
|
onEvent: e => events.update($events => $events.concat(e)),
|
||||||
buffer.push(event)
|
})
|
||||||
}
|
|
||||||
},
|
return () => controller.abort()
|
||||||
onExhausted: () => {
|
}
|
||||||
exhausted = true
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let element: HTMLElement | undefined = $state()
|
||||||
|
let exhausted = $state(false)
|
||||||
|
let events: Writable<TrustedEvent[]> = $state(writable([]))
|
||||||
|
|
||||||
|
const feedEvents = $derived(
|
||||||
|
sortBy(
|
||||||
|
e => ($pinnedIds.includes(e.id) ? -(now() + e.created_at) : -e.created_at),
|
||||||
|
$events.filter(e => getReplyTags(e.tags).replies.length === 0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const scroller = createScroller({
|
const feed = makeFeed({
|
||||||
|
url: Router.get().FromPubkeys([pubkey]).getUrls()[0],
|
||||||
element: element!,
|
element: element!,
|
||||||
delay: 300,
|
filters: [{kinds: [NOTE], authors: [pubkey]}],
|
||||||
threshold: 3000,
|
onBackwardExhausted: () => {
|
||||||
onScroll: () => {
|
exhausted = true
|
||||||
buffer = uniqBy(
|
|
||||||
e => e.id,
|
|
||||||
sortBy(e => -e.created_at, buffer),
|
|
||||||
)
|
|
||||||
|
|
||||||
events.update($events => uniqBy(e => e.id, [...$events, ...buffer.splice(0, 5)]))
|
|
||||||
|
|
||||||
if (buffer.length < 50) {
|
|
||||||
ctrl.load(50)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return () => scroller.stop()
|
events = feed.events
|
||||||
|
|
||||||
|
return () => feed.cleanup()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-4" bind:this={element}>
|
<div class="flex flex-col gap-4" bind:this={element}>
|
||||||
{#each $events as event (event.id)}
|
{#each feedEvents as event (event.id)}
|
||||||
<div in:fly>
|
<div in:fly>
|
||||||
<NoteItem {event} />
|
<NoteItem {event} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import {derived} from "svelte/store"
|
|
||||||
import {sortBy} from "@welshman/lib"
|
|
||||||
import {getListTags, getEventTagValues} from "@welshman/util"
|
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
|
||||||
import {derivePinList, repository} from "@welshman/app"
|
|
||||||
import {Router} from "@welshman/router"
|
|
||||||
import {load} from "@welshman/net"
|
|
||||||
import {deriveEventsById, deriveEventsDesc} from "@welshman/store"
|
|
||||||
import {fly} from "@lib/transition"
|
|
||||||
import Spinner from "@lib/components/Spinner.svelte"
|
|
||||||
import NoteItem from "@app/components/NoteItem.svelte"
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
pubkey: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const {pubkey}: Props = $props()
|
|
||||||
|
|
||||||
const pinList = derivePinList(pubkey)
|
|
||||||
|
|
||||||
const pinnedIds = derived(pinList, $pinList => getEventTagValues(getListTags($pinList)))
|
|
||||||
|
|
||||||
const pinnedEvents = derived(
|
|
||||||
pinnedIds,
|
|
||||||
($pinnedIds, set) => {
|
|
||||||
if ($pinnedIds.length === 0) {
|
|
||||||
set([])
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return deriveEventsDesc(
|
|
||||||
deriveEventsById({repository, filters: [{ids: $pinnedIds}]}),
|
|
||||||
).subscribe(events => {
|
|
||||||
set(sortBy(event => -$pinnedIds.indexOf(event.id), events))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[] as TrustedEvent[],
|
|
||||||
)
|
|
||||||
|
|
||||||
let fetching = $state(false)
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
const ids = $pinnedIds
|
|
||||||
|
|
||||||
if (ids.length === 0) {
|
|
||||||
fetching = false
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const missing = ids.filter(id => !repository.getEvent(id))
|
|
||||||
|
|
||||||
if (missing.length === 0) {
|
|
||||||
fetching = false
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fetching = true
|
|
||||||
|
|
||||||
const controller = new AbortController()
|
|
||||||
|
|
||||||
load({
|
|
||||||
relays: Router.get().FromPubkeys([pubkey]).getUrls(),
|
|
||||||
filters: [{ids: missing}],
|
|
||||||
signal: controller.signal,
|
|
||||||
onClose: () => {
|
|
||||||
fetching = false
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return () => controller.abort()
|
|
||||||
})
|
|
||||||
|
|
||||||
const loading = $derived(
|
|
||||||
fetching || ($pinnedIds.length > 0 && $pinnedEvents.length < $pinnedIds.length),
|
|
||||||
)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if $pinnedIds.length > 0 || loading}
|
|
||||||
{#if loading && $pinnedEvents.length === 0}
|
|
||||||
<p class="flex items-center justify-center py-8">
|
|
||||||
<Spinner loading />
|
|
||||||
</p>
|
|
||||||
{:else if $pinnedEvents.length > 0}
|
|
||||||
<div class="flex flex-col gap-4">
|
|
||||||
{#each $pinnedEvents as event (event.id)}
|
|
||||||
<div in:fly>
|
|
||||||
<NoteItem {event} />
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
{@const isMember = !isSelf && $userSpaceUrls.includes(url)}
|
{@const isMember = !isSelf && $userSpaceUrls.includes(url)}
|
||||||
<Link
|
<Link
|
||||||
href={makeSpacePath(url)}
|
href={makeSpacePath(url)}
|
||||||
class="card card-interactive flex flex-row items-center gap-3 p-3 sm:p-4">
|
class="card card-interactive flex flex-row items-center justify-between gap-3 p-3 sm:p-4">
|
||||||
<RelayIcon {url} size={8} />
|
<RelayIcon {url} size={8} />
|
||||||
<div class="min-w-0 flex-1">
|
<div class="min-w-0 flex-1">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,6 @@ export const setupHistory = () =>
|
||||||
|
|
||||||
export const makeProfilePath = (pubkey: string) => `/people/${nip19.npubEncode(pubkey)}`
|
export const makeProfilePath = (pubkey: string) => `/people/${nip19.npubEncode(pubkey)}`
|
||||||
|
|
||||||
export const goToProfile = (pubkey: string) => goto(makeProfilePath(pubkey))
|
|
||||||
|
|
||||||
// Chat
|
// Chat
|
||||||
|
|
||||||
export const makeChatPath = (pubkeys: string[]) => `/chat/${makeChatId(pubkeys)}`
|
export const makeChatPath = (pubkeys: string[]) => `/chat/${makeChatId(pubkeys)}`
|
||||||
|
|
|
||||||
14
src/routes/people/[npub]/+layout.svelte
Normal file
14
src/routes/people/[npub]/+layout.svelte
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type {Snippet} from "svelte"
|
||||||
|
import {page} from "$app/stores"
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children?: Snippet
|
||||||
|
}
|
||||||
|
|
||||||
|
const {children}: Props = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#key $page.params.npub}
|
||||||
|
{@render children?.()}
|
||||||
|
{/key}
|
||||||
Loading…
Reference in a new issue