Browse Source

fix streams not responding to filter

imwald
Silberengel 21 hours ago
parent
commit
3ac645ca59
  1. 23
      src/components/Embedded/EmbeddedNote.tsx
  2. 8
      src/components/NormalFeed/index.tsx
  3. 195
      src/components/NoteList/index.tsx
  4. 6
      src/components/ZapStreamLiveEventEmbed/index.tsx
  5. 5
      src/lib/live-activities.ts
  6. 2
      src/providers/KindFilterProvider.tsx

23
src/components/Embedded/EmbeddedNote.tsx

@ -2,7 +2,7 @@ import { Skeleton } from '@/components/ui/skeleton'
import ExternalLink from '@/components/ExternalLink' import ExternalLink from '@/components/ExternalLink'
import { FAST_READ_RELAY_URLS, SEARCHABLE_RELAY_URLS, ExtendedKind } from '@/constants' import { FAST_READ_RELAY_URLS, SEARCHABLE_RELAY_URLS, ExtendedKind } from '@/constants'
import { getFavoritesFeedRelayUrls } from '@/lib/favorites-feed-relays' import { getFavoritesFeedRelayUrls } from '@/lib/favorites-feed-relays'
import { LIVE_ACTIVITY_KINDS, liveActivityKindsEnabledInPicker } from '@/lib/live-activities' import { LIVE_ACTIVITY_KINDS } from '@/lib/live-activities'
import { isRenderableNoteKind } from '@/lib/note-renderable-kinds' import { isRenderableNoteKind } from '@/lib/note-renderable-kinds'
import { useFetchEvent } from '@/hooks' import { useFetchEvent } from '@/hooks'
import { normalizeUrl } from '@/lib/url' import { normalizeUrl } from '@/lib/url'
@ -10,7 +10,6 @@ import { cn } from '@/lib/utils'
import client from '@/services/client.service' import client from '@/services/client.service'
import indexedDb from '@/services/indexed-db.service' import indexedDb from '@/services/indexed-db.service'
import { useFavoriteRelays } from '@/providers/favorite-relays-context' import { useFavoriteRelays } from '@/providers/favorite-relays-context'
import { useKindFilterOrDefaults } from '@/providers/KindFilterProvider'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useEffect, useMemo, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import { Event, nip19 } from 'nostr-tools' import { Event, nip19 } from 'nostr-tools'
@ -327,24 +326,8 @@ function EmbeddedNoteContent({
containingEvent?: Event containingEvent?: Event
showFull?: boolean showFull?: boolean
}) { }) {
const { showKinds, feedKindFilterBypass } = useKindFilterOrDefaults() /** Embeds are contextual to the parent note; home kind picker must not hide NIP-53 live cards here. */
const allowLiveEmbeds = liveActivityKindsEnabledInPicker(showKinds, feedKindFilterBypass) const allowLiveEmbeds = true
const naddrTargetsLiveActivityOnly = useMemo(() => {
try {
const dec = nip19.decode(noteId.trim())
if (dec.type !== 'naddr') return false
return LIVE_ACTIVITY_KINDS.includes(dec.data.kind as (typeof LIVE_ACTIVITY_KINDS)[number])
} catch {
return false
}
}, [noteId])
const skipLiveActivityFetch = naddrTargetsLiveActivityOnly && !allowLiveEmbeds
if (skipLiveActivityFetch) {
return <SuppressedLiveStreamEmbed noteId={noteId} className={className} />
}
return ( return (
<EmbeddedNoteFetched <EmbeddedNoteFetched

8
src/components/NormalFeed/index.tsx

@ -174,10 +174,14 @@ const NormalFeed = forwardRef<TNoteListRef, {
[showFeedClientFilterProp, isMainFeed, allowKindlessRelayExplore, useFilterAsIs] [showFeedClientFilterProp, isMainFeed, allowKindlessRelayExplore, useFilterAsIs]
) )
const listShowAllKinds = showAllKindsProp ?? feedKindFilterBypass /**
* Relay explorer passes {@link showAllKinds} explicitly. Home feeds must not tie this to
* {@link feedKindFilterBypass}: bypass widens REQ only; the kind picker still narrows visible rows.
*/
const listShowAllKinds = showAllKindsProp ?? false
/** Include kind picker deps for single-relay chips (kindless REQ + client-side kinds). */ /** Include kind picker deps for single-relay chips (kindless REQ + client-side kinds). */
const subHeaderFilterDepsKey = `${allowKindlessRelayExplore ? 'kle' : 'std'}|${showKindsKey}|${feedKindFilterBypass}|${listShowAllKinds ? 'all' : 'k'}` const subHeaderFilterDepsKey = `${allowKindlessRelayExplore ? 'kle' : 'std'}|${showKindsKey}|${feedKindFilterBypass}|${showAllKindsProp ? 'allProp' : 'k'}`
const tabsElement = useMemo( const tabsElement = useMemo(
() => ( () => (

195
src/components/NoteList/index.tsx

@ -118,6 +118,25 @@ const LOAD_MORE_IO_ROOT_MARGIN_BOTTOM_PX = 3200
* start load-more (uses viewport height of that container, with a floor). * start load-more (uses viewport height of that container, with a floor).
*/ */
const LOAD_MORE_SCROLL_PREFETCH_VIEWPORT_MULT = 2.35 const LOAD_MORE_SCROLL_PREFETCH_VIEWPORT_MULT = 2.35
/** Same rules as visible-row filtering when the home kind picker applies (not {@link shouldHideEvent}). */
function eventPassesNoteListKindPicker(
event: Event,
effectiveShowKinds: readonly number[],
showKind1OPs: boolean,
showKind1Replies: boolean,
showKind1111: boolean
): boolean {
if (!effectiveShowKinds.includes(event.kind)) return false
if (event.kind === kinds.ShortTextNote) {
const isReply = isReplyNoteEvent(event)
if (isReply && !showKind1Replies) return false
if (!isReply && !showKind1OPs) return false
}
if (event.kind === ExtendedKind.COMMENT && !showKind1111) return false
if (event.kind === ExtendedKind.GIT_RELEASE && !showKind1OPs) return false
return true
}
const LOAD_MORE_SCROLL_PREFETCH_MIN_PX = 960 const LOAD_MORE_SCROLL_PREFETCH_MIN_PX = 960
/** Min ms between scroll-driven load-more attempts (loadMore also throttles internally). */ /** Min ms between scroll-driven load-more attempts (loadMore also throttles internally). */
const LOAD_MORE_SCROLL_PREFETCH_COOLDOWN_MS = 180 const LOAD_MORE_SCROLL_PREFETCH_COOLDOWN_MS = 180
@ -937,6 +956,12 @@ const NoteList = forwardRef(
showKindsRef.current = showKinds showKindsRef.current = showKinds
const effectiveShowKindsRef = useRef(effectiveShowKinds) const effectiveShowKindsRef = useRef(effectiveShowKinds)
effectiveShowKindsRef.current = effectiveShowKinds effectiveShowKindsRef.current = effectiveShowKinds
const showKind1OPsRef = useRef(showKind1OPs)
showKind1OPsRef.current = showKind1OPs
const showKind1RepliesRef = useRef(showKind1Replies)
showKind1RepliesRef.current = showKind1Replies
const showKind1111Ref = useRef(showKind1111)
showKind1111Ref.current = showKind1111
const progressiveDocumentKindsRef = useRef(progressiveDocumentKinds) const progressiveDocumentKindsRef = useRef(progressiveDocumentKinds)
progressiveDocumentKindsRef.current = progressiveDocumentKinds progressiveDocumentKindsRef.current = progressiveDocumentKinds
const progressiveWarmupQueryRef = useRef(progressiveWarmupQuery) const progressiveWarmupQueryRef = useRef(progressiveWarmupQuery)
@ -960,11 +985,13 @@ const NoteList = forwardRef(
/** /**
* When to apply kind picker + kind-1 OP|reply / 1111 / GitRelease splits to visible rows. * When to apply kind picker + kind-1 OP|reply / 1111 / GitRelease splits to visible rows.
* Home feeds default to {@link withKindFilter}; relay explorer and KindFilter "All Events" use {@link showAllKinds}. * Home feeds default to {@link withKindFilter}. Relay explorer sets {@link showAllKinds} explicitly (kindless
* firehose). {@link seeAllFeedEvents} widens REQ when applicable; merged batches and live rows still respect the
* picker unless {@link showAllKinds} is true with kindless explore.
*/ */
const applyKindPickerInUi = useMemo( const applyKindPickerInUi = useMemo(
() => withKindFilter && !showAllKinds && !seeAllFeedEvents, () => withKindFilter && !showAllKinds,
[withKindFilter, showAllKinds, seeAllFeedEvents] [withKindFilter, showAllKinds]
) )
const shouldHideEvent = useCallback( const shouldHideEvent = useCallback(
@ -1037,14 +1064,9 @@ const NoteList = forwardRef(
for (; i < maxScan && i < timelineEventsForFilter.length && out.length < target; i++) { for (; i < maxScan && i < timelineEventsForFilter.length && out.length < target; i++) {
const evt = timelineEventsForFilter[i]! const evt = timelineEventsForFilter[i]!
if (applyKindPickerInUi) { if (applyKindPickerInUi) {
if (!effectiveShowKinds.includes(evt.kind)) continue if (!eventPassesNoteListKindPicker(evt, effectiveShowKinds, showKind1OPs, showKind1Replies, showKind1111)) {
if (evt.kind === kinds.ShortTextNote) { continue
const isReply = isReplyNoteEvent(evt)
if (isReply && !showKind1Replies) continue
if (!isReply && !showKind1OPs) continue
} }
if (evt.kind === ExtendedKind.COMMENT && !showKind1111) continue
if (evt.kind === ExtendedKind.GIT_RELEASE && !showKind1OPs) continue
} }
if (shouldHideEvent(evt)) continue if (shouldHideEvent(evt)) continue
@ -1118,14 +1140,17 @@ const NoteList = forwardRef(
return newEvents.filter((event: Event) => { return newEvents.filter((event: Event) => {
if (applyKindPickerInUi) { if (applyKindPickerInUi) {
if (!effectiveShowKinds.includes(event.kind)) return false if (
if (event.kind === kinds.ShortTextNote) { !eventPassesNoteListKindPicker(
const isReply = isReplyNoteEvent(event) event,
if (isReply && !showKind1Replies) return false effectiveShowKinds,
if (!isReply && !showKind1OPs) return false showKind1OPs,
showKind1Replies,
showKind1111
)
) {
return false
} }
if (event.kind === ExtendedKind.COMMENT && !showKind1111) return false
if (event.kind === ExtendedKind.GIT_RELEASE && !showKind1OPs) return false
} }
if (shouldHideEvent(event)) return false if (shouldHideEvent(event)) return false
@ -1676,15 +1701,25 @@ const NoteList = forwardRef(
} }
/** /**
* Kindless relay REQ: when {@link showAllKinds} is true (explorer / "All Events"), keep the full batch; * Relay kindless firehose: keep the full batch. Else when the kind picker applies, narrow like
* otherwise narrow to effectiveShowKinds so the merged timeline matches {@link applyKindPickerInUi}. * {@link applyKindPickerInUi}. Remaining spell paths use kinds-only narrowing when client-side kind filter runs.
*/ */
const narrowLiveBatch = (evs: Event[]) => { const narrowLiveBatch = (evs: Event[]) => {
if (seeAllFeedEventsRef.current) return evs
if (allowKindlessRelayExploreRef.current && showAllKindsRef.current) return evs if (allowKindlessRelayExploreRef.current && showAllKindsRef.current) return evs
if (withKindFilterRef.current && !showAllKindsRef.current) {
return evs.filter((e) =>
eventPassesNoteListKindPicker(
e,
effectiveShowKindsRef.current,
showKind1OPsRef.current,
showKind1RepliesRef.current,
showKind1111Ref.current
)
)
}
if (!useFilterAsIsRef.current || !clientSideKindFilterRef.current) return evs if (!useFilterAsIsRef.current || !clientSideKindFilterRef.current) return evs
if (!withKindFilterRef.current) return evs if (!withKindFilterRef.current) return evs
return evs.filter((e) => effectiveShowKinds.includes(e.kind)) return evs.filter((e) => effectiveShowKindsRef.current.includes(e.kind))
} }
if (oneShotFetch) { if (oneShotFetch) {
@ -1746,7 +1781,6 @@ const NoteList = forwardRef(
useFilterAsIs && useFilterAsIs &&
clientSideKindFilter && clientSideKindFilter &&
withKindFilter && withKindFilter &&
!seeAllFeedEventsRef.current &&
(!allowKindlessRelayExplore || !showAllKinds) (!allowKindlessRelayExplore || !showAllKinds)
) { ) {
relayOnly = relayOnly.filter((e) => effectiveShowKinds.includes(e.kind)) relayOnly = relayOnly.filter((e) => effectiveShowKinds.includes(e.kind))
@ -2084,25 +2118,39 @@ const NoteList = forwardRef(
onNew: (event: Event) => { onNew: (event: Event) => {
if (!effectActive) return if (!effectActive) return
feedRelayReturnedAnyEventRef.current = true feedRelayReturnedAnyEventRef.current = true
if (!seeAllFeedEventsRef.current && withKindFilterRef.current) { if (withKindFilterRef.current) {
const kindlessFirehose = const kindlessFirehose =
allowKindlessRelayExploreRef.current && showAllKindsRef.current allowKindlessRelayExploreRef.current && showAllKindsRef.current
if (!kindlessFirehose) { if (!kindlessFirehose) {
if (!useFilterAsIsRef.current && !effectiveShowKindsRef.current.includes(event.kind)) if (!showAllKindsRef.current) {
return if (
if ( !eventPassesNoteListKindPicker(
clientSideKindFilterRef.current && event,
useFilterAsIsRef.current && effectiveShowKindsRef.current,
!effectiveShowKindsRef.current.includes(event.kind) showKind1OPsRef.current,
) showKind1RepliesRef.current,
return showKind1111Ref.current
if (event.kind === kinds.ShortTextNote) { )
const isReply = isReplyNoteEvent(event) ) {
if (isReply && !showKind1Replies) return return
if (!isReply && !showKind1OPs) return }
} else {
if (!useFilterAsIsRef.current && !effectiveShowKindsRef.current.includes(event.kind))
return
if (
clientSideKindFilterRef.current &&
useFilterAsIsRef.current &&
!effectiveShowKindsRef.current.includes(event.kind)
)
return
if (event.kind === kinds.ShortTextNote) {
const isReply = isReplyNoteEvent(event)
if (isReply && !showKind1RepliesRef.current) return
if (!isReply && !showKind1OPsRef.current) return
}
if (event.kind === ExtendedKind.COMMENT && !showKind1111Ref.current) return
if (event.kind === ExtendedKind.GIT_RELEASE && !showKind1OPsRef.current) return
} }
if (event.kind === ExtendedKind.COMMENT && !showKind1111) return
if (event.kind === ExtendedKind.GIT_RELEASE && !showKind1OPs) return
} }
} }
if (shouldHideEventRef.current(event)) return if (shouldHideEventRef.current(event)) return
@ -2261,8 +2309,18 @@ const NoteList = forwardRef(
: LIMIT : LIMIT
const narrowDeltaBatch = (evs: Event[]) => { const narrowDeltaBatch = (evs: Event[]) => {
if (seeAllFeedEventsRef.current) return evs
if (allowKindlessRelayExploreRef.current && showAllKindsRef.current) return evs if (allowKindlessRelayExploreRef.current && showAllKindsRef.current) return evs
if (withKindFilterRef.current && !showAllKindsRef.current) {
return evs.filter((e) =>
eventPassesNoteListKindPicker(
e,
effectiveShowKindsRef.current,
showKind1OPsRef.current,
showKind1RepliesRef.current,
showKind1111Ref.current
)
)
}
if (!useFilterAsIsRef.current || !clientSideKindFilterRef.current) return evs if (!useFilterAsIsRef.current || !clientSideKindFilterRef.current) return evs
if (!withKindFilterRef.current) return evs if (!withKindFilterRef.current) return evs
return evs.filter((e) => effectiveShowKindsRef.current.includes(e.kind)) return evs.filter((e) => effectiveShowKindsRef.current.includes(e.kind))
@ -2336,25 +2394,39 @@ const NoteList = forwardRef(
onNew: (event: Event) => { onNew: (event: Event) => {
if (!deltaActive) return if (!deltaActive) return
feedRelayReturnedAnyEventRef.current = true feedRelayReturnedAnyEventRef.current = true
if (!seeAllFeedEventsRef.current && withKindFilterRef.current) { if (withKindFilterRef.current) {
const kindlessFirehose = const kindlessFirehose =
allowKindlessRelayExploreRef.current && showAllKindsRef.current allowKindlessRelayExploreRef.current && showAllKindsRef.current
if (!kindlessFirehose) { if (!kindlessFirehose) {
if (!useFilterAsIsRef.current && !effectiveShowKindsRef.current.includes(event.kind)) if (!showAllKindsRef.current) {
return if (
if ( !eventPassesNoteListKindPicker(
clientSideKindFilterRef.current && event,
useFilterAsIsRef.current && effectiveShowKindsRef.current,
!effectiveShowKindsRef.current.includes(event.kind) showKind1OPsRef.current,
) showKind1RepliesRef.current,
return showKind1111Ref.current
if (event.kind === kinds.ShortTextNote) { )
const isReply = isReplyNoteEvent(event) ) {
if (isReply && !showKind1Replies) return return
if (!isReply && !showKind1OPs) return }
} else {
if (!useFilterAsIsRef.current && !effectiveShowKindsRef.current.includes(event.kind))
return
if (
clientSideKindFilterRef.current &&
useFilterAsIsRef.current &&
!effectiveShowKindsRef.current.includes(event.kind)
)
return
if (event.kind === kinds.ShortTextNote) {
const isReply = isReplyNoteEvent(event)
if (isReply && !showKind1RepliesRef.current) return
if (!isReply && !showKind1OPsRef.current) return
}
if (event.kind === ExtendedKind.COMMENT && !showKind1111Ref.current) return
if (event.kind === ExtendedKind.GIT_RELEASE && !showKind1OPsRef.current) return
} }
if (event.kind === ExtendedKind.COMMENT && !showKind1111) return
if (event.kind === ExtendedKind.GIT_RELEASE && !showKind1OPs) return
} }
} }
if (shouldHideEventRef.current(event)) return if (shouldHideEventRef.current(event)) return
@ -2694,12 +2766,21 @@ const NoteList = forwardRef(
useFilterAsIsRef.current && useFilterAsIsRef.current &&
clientSideKindFilterRef.current && clientSideKindFilterRef.current &&
withKindFilterRef.current && withKindFilterRef.current &&
!seeAllFeedEventsRef.current &&
(!allowKindlessRelayExploreRef.current || !showAllKindsRef.current) (!allowKindlessRelayExploreRef.current || !showAllKindsRef.current)
const existingIds = new Set(latestEvents.map((e) => e.id)) const existingIds = new Set(latestEvents.map((e) => e.id))
const kindPasses = (e: Event) => const kindPasses = (e: Event) => {
!narrowLoadMore || effectiveShowKindsRef.current.includes(e.kind) if (withKindFilterRef.current && !showAllKindsRef.current) {
return eventPassesNoteListKindPicker(
e,
effectiveShowKindsRef.current,
showKind1OPsRef.current,
showKind1RepliesRef.current,
showKind1111Ref.current
)
}
return !narrowLoadMore || effectiveShowKindsRef.current.includes(e.kind)
}
const noveltyFromBatch = (batch: Event[]) => { const noveltyFromBatch = (batch: Event[]) => {
const out: Event[] = [] const out: Event[] = []

6
src/components/ZapStreamLiveEventEmbed/index.tsx

@ -1,9 +1,7 @@
import { EmbeddedNote } from '@/components/Embedded/EmbeddedNote' import { EmbeddedNote } from '@/components/Embedded/EmbeddedNote'
import ExternalLink from '@/components/ExternalLink' import ExternalLink from '@/components/ExternalLink'
import { liveActivityKindsEnabledInPicker } from '@/lib/live-activities'
import { naddrFromZapStreamWatchUrl } from '@/lib/zap-stream-url' import { naddrFromZapStreamWatchUrl } from '@/lib/zap-stream-url'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { useKindFilterOrDefaults } from '@/providers/KindFilterProvider'
import type { Event } from 'nostr-tools' import type { Event } from 'nostr-tools'
/** zap.stream `/naddr1…` → fetch kind 30311 and render as embedded note (LiveEvent), not a full-site iframe. */ /** zap.stream `/naddr1…` → fetch kind 30311 and render as embedded note (LiveEvent), not a full-site iframe. */
@ -18,10 +16,6 @@ export default function ZapStreamLiveEventEmbed({
containingEvent?: Event containingEvent?: Event
showFull?: boolean showFull?: boolean
}) { }) {
const { showKinds, feedKindFilterBypass } = useKindFilterOrDefaults()
if (!liveActivityKindsEnabledInPicker(showKinds, feedKindFilterBypass)) {
return <ExternalLink url={url} className={cn('not-prose', className)} />
}
const naddr = naddrFromZapStreamWatchUrl(url) const naddr = naddrFromZapStreamWatchUrl(url)
if (!naddr) { if (!naddr) {
return <ExternalLink url={url} className={cn('not-prose', className)} /> return <ExternalLink url={url} className={cn('not-prose', className)} />

5
src/lib/live-activities.ts

@ -36,7 +36,10 @@ export type LiveActivitiesFetchEventsFn = (
/** NIP-53 live streaming (30311), meeting space (30312), meeting (30313). */ /** NIP-53 live streaming (30311), meeting space (30312), meeting (30313). */
export const LIVE_ACTIVITY_KINDS = [30311, 30312, 30313] as const export const LIVE_ACTIVITY_KINDS = [30311, 30312, 30313] as const
/** True when the home kind picker (or “see all events”) allows NIP-53 live activity rows and inline embeds. */ /**
* @deprecated Home embeds no longer consult the kind picker. Kept for callers that still want
* is live activity in the users selected kinds? (e.g. optional UI); prefer inlining that check.
*/
export function liveActivityKindsEnabledInPicker( export function liveActivityKindsEnabledInPicker(
showKinds: readonly number[], showKinds: readonly number[],
feedKindFilterBypass: boolean feedKindFilterBypass: boolean

2
src/providers/KindFilterProvider.tsx

@ -26,7 +26,7 @@ type TKindFilterContext = {
showKind1OPs: boolean showKind1OPs: boolean
showKind1Replies: boolean showKind1Replies: boolean
showKind1111: boolean showKind1111: boolean
/** When true, main feed omits REQ `kinds` and skips client-side kind filtering (testing). */ /** When true, main feed uses wider REQ / merge paths ("see all events"); visible rows still follow the kind picker. */
feedKindFilterBypass: boolean feedKindFilterBypass: boolean
updateShowKinds: ( updateShowKinds: (
kinds: number[], kinds: number[],

Loading…
Cancel
Save