Add deriveLatestEvent for ProfileBadges
This commit is contained in:
parent
3bf3927896
commit
8cb89f362c
2 changed files with 83 additions and 19 deletions
|
|
@ -1,11 +1,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
import {formatTimestampRelative} from "@welshman/lib"
|
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 Button from "@lib/components/Button.svelte"
|
||||||
import ProfileSpaces from "@app/components/ProfileSpaces.svelte"
|
import ProfileSpaces from "@app/components/ProfileSpaces.svelte"
|
||||||
import {network, relayLists, roomLists, router} from "@app/core"
|
import {network, roomLists} from "@app/core"
|
||||||
import {deriveEvents} from "@app/repository"
|
import {deriveLatestEvent} from "@app/repository"
|
||||||
import {goToEvent} from "@app/routes"
|
import {goToEvent} from "@app/routes"
|
||||||
import {pushModal} from "@app/modal"
|
import {pushModal} from "@app/modal"
|
||||||
|
|
||||||
|
|
@ -16,31 +16,23 @@
|
||||||
|
|
||||||
const {pubkey, url}: Props = $props()
|
const {pubkey, url}: Props = $props()
|
||||||
|
|
||||||
const events = deriveEvents([{authors: [pubkey], limit: 1}])
|
const latest = deriveLatestEvent(pubkey)
|
||||||
|
|
||||||
const spaceUrls = $roomLists.urls(pubkey).$
|
const spaceUrls = $roomLists.urls(pubkey).$
|
||||||
|
|
||||||
const viewEvent = () => goToEvent($events[0]!)
|
const viewEvent = () => goToEvent($latest!)
|
||||||
|
|
||||||
const openSpaces = () => pushModal(ProfileSpaces, {pubkey, url})
|
const openSpaces = () => pushModal(ProfileSpaces, {pubkey, url})
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(() => {
|
||||||
await $relayLists.load(pubkey)
|
$network.loadUsingOutbox(pubkey, {limit: 1, kinds: [NOTE, COMMENT, MESSAGE]})
|
||||||
|
|
||||||
$network.load({
|
|
||||||
filters: [
|
|
||||||
{authors: [pubkey], kinds: [ROOMS]},
|
|
||||||
{authors: [pubkey], limit: 1, kinds: [NOTE, COMMENT, MESSAGE]},
|
|
||||||
],
|
|
||||||
relays: await $router.resolver.relays([outbox(pubkey)]),
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
{#if $events.length > 0}
|
{#if $latest}
|
||||||
<Button onclick={viewEvent} class="badge badge-neutral">
|
<Button onclick={viewEvent} class="badge badge-neutral">
|
||||||
Last active {formatTimestampRelative($events[0].created_at)}
|
Last active {formatTimestampRelative($latest.created_at)}
|
||||||
</Button>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if $spaceUrls.length > 0}
|
{#if $spaceUrls.length > 0}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import {derived} from "svelte/store"
|
import {derived, readable} from "svelte/store"
|
||||||
import {filter, spec} from "@welshman/lib"
|
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 type {Filter, TrustedEvent} from "@welshman/util"
|
||||||
import * as store from "@welshman/store"
|
import * as store from "@welshman/store"
|
||||||
import {Network, Relays} from "@welshman/app"
|
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)],
|
[fromApp($app => $app.use(Relays).one(url)), deriveEventsForUrl(url, filters)],
|
||||||
([$relay, $events]) => filter(spec({pubkey: $relay?.self}), $events as TrustedEvent[]),
|
([$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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue