Show a person's most recent pinned note on their profile dialog
This commit is contained in:
parent
e8a1f0bba7
commit
fc86977a47
5 changed files with 77 additions and 27 deletions
|
|
@ -17,6 +17,7 @@
|
|||
import ProfileAbout from "@app/components/ProfileAbout.svelte"
|
||||
import ProfileBadges from "@app/components/ProfileBadges.svelte"
|
||||
import ProfileMenu from "@app/components/ProfileMenu.svelte"
|
||||
import ProfilePinnedNote from "@app/components/ProfilePinnedNote.svelte"
|
||||
import ProfileStatus from "@app/components/ProfileStatus.svelte"
|
||||
import {messagingRelayLists, profiles, relayManagement, user} from "@app/core"
|
||||
import {deriveUserIsSpaceAdmin} from "@app/management"
|
||||
|
|
@ -138,6 +139,7 @@
|
|||
<ProfileStatus {pubkey} {url} />
|
||||
<ProfileAbout {pubkey} {url} />
|
||||
<ProfileBadges {pubkey} {url} />
|
||||
<ProfilePinnedNote {pubkey} {url} />
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
<script lang="ts">
|
||||
import {onDestroy, onMount} from "svelte"
|
||||
import {derived, writable} from "svelte/store"
|
||||
import {writable} from "svelte/store"
|
||||
import type {Readable} from "svelte/store"
|
||||
import {sortBy, uniqBy, now} from "@welshman/lib"
|
||||
import type {Maybe} from "@welshman/lib"
|
||||
import {NOTE, outbox} from "@welshman/util"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {getReplyTags} from "@welshman/domain"
|
||||
import {PinLists} from "@welshman/app"
|
||||
import {fly} from "@lib/transition"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import NoteItem from "@app/components/NoteItem.svelte"
|
||||
import {app, network, router} from "@app/core"
|
||||
import {router} from "@app/core"
|
||||
import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds"
|
||||
import {derivePinnedEvents} from "@app/pins"
|
||||
|
||||
type Props = {
|
||||
pubkey: string
|
||||
|
|
@ -25,30 +25,9 @@
|
|||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
const pinnedIds = derived($app.use(PinLists).one(pubkey), $pinList => $pinList?.ids() ?? [])
|
||||
const pinnedEvents = derivePinnedEvents(pubkey)
|
||||
|
||||
// Pinned notes are asked for by id instead of by the feed's filters, so they get a store of
|
||||
// their own. A feed dedupes and sorts what it holds as it loads it, and writing to its store
|
||||
// from out here bypasses both.
|
||||
const pinnedEvents = writable<TrustedEvent[]>([])
|
||||
|
||||
$effect(() => {
|
||||
if ($pinnedIds.length > 0) {
|
||||
const controller = new AbortController()
|
||||
|
||||
relays.then($relays =>
|
||||
$network.load({
|
||||
relays: $relays,
|
||||
filters: [{ids: $pinnedIds}],
|
||||
signal: controller.signal,
|
||||
onEvent: event =>
|
||||
pinnedEvents.update($pinned => uniqBy(e => e.id, $pinned.concat(event))),
|
||||
}),
|
||||
)
|
||||
|
||||
return () => controller.abort()
|
||||
}
|
||||
})
|
||||
const pinnedIds = $derived($pinnedEvents.map(e => e.id))
|
||||
|
||||
let element: HTMLElement | undefined = $state()
|
||||
let older: Maybe<ReturnType<typeof makeScrollLoader>> = $state()
|
||||
|
|
@ -59,7 +38,7 @@
|
|||
|
||||
const feedEvents = $derived(
|
||||
sortBy(
|
||||
e => ($pinnedIds.includes(e.id) ? -(now() + e.created_at) : -e.created_at),
|
||||
e => (pinnedIds.includes(e.id) ? -(now() + e.created_at) : -e.created_at),
|
||||
uniqBy(e => e.id, [...$pinnedEvents, ...$events]).filter(
|
||||
e => getReplyTags(e.tags).replies.length === 0,
|
||||
),
|
||||
|
|
|
|||
31
src/app/components/ProfilePinnedNote.svelte
Normal file
31
src/app/components/ProfilePinnedNote.svelte
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<script lang="ts">
|
||||
import {NOTE} from "@welshman/util"
|
||||
import Pin from "@assets/icons/pin.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import NoteCard from "@app/components/NoteCard.svelte"
|
||||
import NoteContent from "@app/components/NoteContent.svelte"
|
||||
import {derivePinnedEvents} from "@app/pins"
|
||||
|
||||
type Props = {
|
||||
pubkey: string
|
||||
url?: string
|
||||
}
|
||||
|
||||
const {pubkey, url}: Props = $props()
|
||||
|
||||
const pinnedEvents = derivePinnedEvents(pubkey)
|
||||
|
||||
const event = $derived($pinnedEvents.find(e => e.kind === NOTE))
|
||||
</script>
|
||||
|
||||
{#if event}
|
||||
<div class="flex flex-col gap-2">
|
||||
<p class="text-content-muted flex items-center gap-2 text-sm">
|
||||
<Icon icon={Pin} size={4} />
|
||||
Pinned note
|
||||
</p>
|
||||
<NoteCard {event} {url} hideProfile class="card">
|
||||
<NoteContent {event} {url} minLength={120} maxLength={240} expandMode="inline" />
|
||||
</NoteCard>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -16,6 +16,7 @@ import {
|
|||
MessagingRelayLists,
|
||||
MuteLists,
|
||||
Network,
|
||||
PinLists,
|
||||
Profiles,
|
||||
Reactions,
|
||||
RelayLists,
|
||||
|
|
@ -143,6 +144,7 @@ export const logger = usePlugin(Logger)
|
|||
export const messagingRelayLists = usePlugin(MessagingRelayLists)
|
||||
export const muteLists = usePlugin(MuteLists)
|
||||
export const network = usePlugin(Network)
|
||||
export const pinLists = usePlugin(PinLists)
|
||||
export const profiles = usePlugin(Profiles)
|
||||
export const reactions = usePlugin(Reactions)
|
||||
export const relayLists = usePlugin(RelayLists)
|
||||
|
|
|
|||
36
src/app/pins.ts
Normal file
36
src/app/pins.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import {derived} from "svelte/store"
|
||||
import type {Readable} from "svelte/store"
|
||||
import {getIdFilters, outbox} from "@welshman/util"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {network, pinLists, router} from "@app/core"
|
||||
import {deriveEvents} from "@app/repository"
|
||||
|
||||
export const derivePinnedEvents = (pubkey: string): Readable<TrustedEvent[]> =>
|
||||
derived(
|
||||
pinLists.get().one(pubkey),
|
||||
($pinList, set: (events: TrustedEvent[]) => void) => {
|
||||
const ids = $pinList?.ids() || []
|
||||
|
||||
if (ids.length > 0) {
|
||||
const controller = new AbortController()
|
||||
const filters = getIdFilters(ids)
|
||||
|
||||
router
|
||||
.get()
|
||||
.resolver.relays([outbox(pubkey)])
|
||||
.then($relays =>
|
||||
network.get().load({relays: $relays, filters, signal: controller.signal}),
|
||||
)
|
||||
|
||||
const unsubscribe = deriveEvents(filters).subscribe(set)
|
||||
|
||||
return () => {
|
||||
controller.abort()
|
||||
unsubscribe()
|
||||
}
|
||||
}
|
||||
|
||||
set([])
|
||||
},
|
||||
[] as TrustedEvent[],
|
||||
)
|
||||
Loading…
Reference in a new issue