Add better muting, add EventReducer

This commit is contained in:
Jon Staab
2026-02-18 10:22:23 -08:00
parent cbafcf6939
commit 56dddbdd86
4 changed files with 181 additions and 3 deletions
+1 -1
View File
@@ -26,7 +26,7 @@
<Icon icon={Reply} />
<span>{$replies.length} {$replies.length === 1 ? "reply" : "replies"}</span>
</div>
<div class="btn btn-neutral btn-xs relative hidden rounded-full sm:flex">
<div class="btn btn-neutral btn-xs relative rounded-full">
{#if gt(lastActive, $checked)}
<div class="h-2 w-2 rounded-full bg-primary"></div>
{/if}
+131
View File
@@ -0,0 +1,131 @@
<script lang="ts">
import {insertAt, lt, addToMapKey, parseJson} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {Router, addMaximalFallbacks} from "@welshman/router"
import {load} from "@welshman/net"
import {
getIdOrAddress,
getIdFilters,
getParentIdsAndAddrs,
getParentIdOrAddr,
verifyEvent,
ZAP_RESPONSE,
} from "@welshman/util"
import {repository, getValidZap} from "@welshman/app"
import {REPOST_KINDS, REACTION_KINDS, isEventMuted} from '@app/core/state'
export let events: TrustedEvent[]
export let depth = 0
export let showMuted = false
export let hideReplies = false
export let showDeleted = false
export let shouldSort = false
export let shouldAwait = false
export let items: TrustedEvent[] = []
const timestamps = new Map<string, number>()
const context = new Map<string, Set<TrustedEvent>>()
const shouldSkip = (event: TrustedEvent) => {
if (!showMuted && $isEventMuted(event)) return true
if (!showDeleted && repository.isDeleted(event)) return true
if (hideReplies && getParentIdOrAddr(event)) return true
if (timestamps.has(getIdOrAddress(event))) return true
return false
}
const getParent = async (event: TrustedEvent) => {
if (REPOST_KINDS.includes(event.kind)) {
const parent = parseJson(event.content)
if (parent && verifyEvent(parent)) {
return parent
}
}
const parentIds = getParentIdsAndAddrs(event)
if (parentIds.length > 0) {
const filters = getIdFilters(parentIds)
const [cached] = repository.query(filters)
if (cached) return cached
const relays = Router.get().EventParents(event).policy(addMaximalFallbacks).getUrls()
const [parent] = await load({filters, relays})
return parent
}
}
const addEvent = async (event: TrustedEvent) => {
const original = event
let currentDepth = depth
timestamps.set(getIdOrAddress(event), original.created_at)
while (currentDepth > 0) {
const parent = await getParent(event)
// Unable to get the parent? we're done traversing parents
if (!parent) {
break
}
// Skip zaps that fail our zapper check
if (event.kind === ZAP_RESPONSE && !(await getValidZap(event, parent))) {
return
}
// Link the events, even if we end up skipping this one (since we deduplicate)
addToMapKey(context, getIdOrAddress(parent), event)
// Hide replies to deleted/muted parents, or parents we've already seen
if (shouldSkip(parent)) {
return
}
timestamps.set(getIdOrAddress(parent), original.created_at)
currentDepth--
event = parent
}
// If it's not displayable, skip it
if ([...REPOST_KINDS, ...REACTION_KINDS].includes(event.kind)) return
let inserted = false
if (shouldSort) {
for (let i = 0; i < items.length; i++) {
if (lt(timestamps.get(getIdOrAddress(items[i])), original.created_at)) {
items = insertAt(i, event, items)
inserted = true
break
}
}
}
if (!inserted) {
items = [...items, event]
}
}
const addEvents = async (events: TrustedEvent[]) => {
for (const event of events) {
if (!shouldSkip(event)) {
const promise = addEvent(event)
if (shouldAwait) {
await promise
}
}
}
}
$: addEvents(events)
</script>
{#each items as event, i (event.id)}
{@render children({i, event})}
{/each}
+2 -2
View File
@@ -4,13 +4,13 @@
import {formatTimestamp} from "@welshman/lib"
import {getListTags, getPubkeyTagValues} from "@welshman/util"
import type {TrustedEvent} from "@welshman/util"
import {userMuteList} from "@welshman/app"
import Danger from "@assets/icons/danger-triangle.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import Profile from "@app/components/Profile.svelte"
import ProfileName from "@app/components/ProfileName.svelte"
import {goToEvent} from "@app/util/routes"
import {isEventMuted} from "@app/core/state"
const {
event,
@@ -32,7 +32,7 @@
muted = false
}
let muted = $state(getPubkeyTagValues(getListTags($userMuteList)).includes(event.pubkey))
let muted = $state($isEventMuted(event))
</script>
<div class="flex flex-col gap-2 shadow-md {restProps.class}">