Add deriveLatestEvent for ProfileBadges

This commit is contained in:
Jon Staab 2026-08-31 10:31:58 -07:00
parent 3bf3927896
commit 8cb89f362c
2 changed files with 83 additions and 19 deletions

View file

@ -1,11 +1,11 @@
<script lang="ts">
import {onMount} from "svelte"
import {formatTimestampRelative} from "@welshman/lib"
import {NOTE, ROOMS, COMMENT, MESSAGE, outbox} from "@welshman/util"
import {NOTE, COMMENT, MESSAGE} from "@welshman/util"
import Button from "@lib/components/Button.svelte"
import ProfileSpaces from "@app/components/ProfileSpaces.svelte"
import {network, relayLists, roomLists, router} from "@app/core"
import {deriveEvents} from "@app/repository"
import {network, roomLists} from "@app/core"
import {deriveLatestEvent} from "@app/repository"
import {goToEvent} from "@app/routes"
import {pushModal} from "@app/modal"
@ -16,31 +16,23 @@
const {pubkey, url}: Props = $props()
const events = deriveEvents([{authors: [pubkey], limit: 1}])
const latest = deriveLatestEvent(pubkey)
const spaceUrls = $roomLists.urls(pubkey).$
const viewEvent = () => goToEvent($events[0]!)
const viewEvent = () => goToEvent($latest!)
const openSpaces = () => pushModal(ProfileSpaces, {pubkey, url})
onMount(async () => {
await $relayLists.load(pubkey)
$network.load({
filters: [
{authors: [pubkey], kinds: [ROOMS]},
{authors: [pubkey], limit: 1, kinds: [NOTE, COMMENT, MESSAGE]},
],
relays: await $router.resolver.relays([outbox(pubkey)]),
})
onMount(() => {
$network.loadUsingOutbox(pubkey, {limit: 1, kinds: [NOTE, COMMENT, MESSAGE]})
})
</script>
<div class="flex flex-wrap gap-2">
{#if $events.length > 0}
{#if $latest}
<Button onclick={viewEvent} class="badge badge-neutral">
Last active {formatTimestampRelative($events[0].created_at)}
Last active {formatTimestampRelative($latest.created_at)}
</Button>
{/if}
{#if $spaceUrls.length > 0}

View file

@ -1,5 +1,8 @@
import {derived} from "svelte/store"
import {filter, spec} from "@welshman/lib"
import {derived, readable} from "svelte/store"
import type {Unsubscriber} from "svelte/store"
import {filter, first, on, spec} from "@welshman/lib"
import type {Maybe} from "@welshman/lib"
import {sortEventsDesc} from "@welshman/util"
import type {Filter, TrustedEvent} from "@welshman/util"
import * as store from "@welshman/store"
import {Network, Relays} from "@welshman/app"
@ -61,3 +64,72 @@ export const deriveRelaySignedEvents = (url: string, filters: Filter[] = [{}]) =
[fromApp($app => $app.use(Relays).one(url)), deriveEventsForUrl(url, filters)],
([$relay, $events]) => filter(spec({pubkey: $relay?.self}), $events as TrustedEvent[]),
)
// The most recent event held from one author.
const latestByPubkey = new Map<string, Maybe<TrustedEvent>>()
const latestSubscribers = new Map<string, Set<(event: Maybe<TrustedEvent>) => void>>()
let latestUnsubscriber: Maybe<Unsubscriber>
const readLatest = (pubkey: string) =>
first(sortEventsDesc(app.get().repository.query([{authors: [pubkey]}])))
export const deriveLatestEvent = (pubkey: string) =>
readable<Maybe<TrustedEvent>>(undefined, set => {
let subscribers = latestSubscribers.get(pubkey)
if (!subscribers) {
subscribers = new Set()
latestSubscribers.set(pubkey, subscribers)
latestByPubkey.set(pubkey, readLatest(pubkey))
}
subscribers.add(set)
set(latestByPubkey.get(pubkey))
latestUnsubscriber ??= on(app.get().repository, "update", ({added, removed}) => {
const touched = new Set<string>()
for (const event of added) {
if (latestSubscribers.has(event.pubkey)) {
const current = latestByPubkey.get(event.pubkey)
if (!current || event.created_at > current.created_at) {
latestByPubkey.set(event.pubkey, event)
touched.add(event.pubkey)
}
}
}
// A removal can take the very event a row is showing, so that author has to be looked up
// again rather than just dropped
for (const [author, event] of latestByPubkey) {
if (event && removed.has(event.id)) {
latestByPubkey.set(author, readLatest(author))
touched.add(author)
}
}
for (const author of touched) {
for (const subscriber of latestSubscribers.get(author) || []) {
subscriber(latestByPubkey.get(author))
}
}
})
return () => {
subscribers.delete(set)
if (subscribers.size === 0) {
latestSubscribers.delete(pubkey)
latestByPubkey.delete(pubkey)
if (latestSubscribers.size === 0) {
latestUnsubscriber?.()
latestUnsubscriber = undefined
}
}
}
})