diff --git a/src/components/Embedded/EmbeddedNote.tsx b/src/components/Embedded/EmbeddedNote.tsx index fe94ed3a..79d14ead 100644 --- a/src/components/Embedded/EmbeddedNote.tsx +++ b/src/components/Embedded/EmbeddedNote.tsx @@ -2,7 +2,7 @@ import { Skeleton } from '@/components/ui/skeleton' import ExternalLink from '@/components/ExternalLink' import { FAST_READ_RELAY_URLS, SEARCHABLE_RELAY_URLS, ExtendedKind } from '@/constants' 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 { useFetchEvent } from '@/hooks' import { normalizeUrl } from '@/lib/url' @@ -10,7 +10,6 @@ import { cn } from '@/lib/utils' import client from '@/services/client.service' import indexedDb from '@/services/indexed-db.service' import { useFavoriteRelays } from '@/providers/favorite-relays-context' -import { useKindFilterOrDefaults } from '@/providers/KindFilterProvider' import { useTranslation } from 'react-i18next' import { useEffect, useMemo, useState } from 'react' import { Event, nip19 } from 'nostr-tools' @@ -327,24 +326,8 @@ function EmbeddedNoteContent({ containingEvent?: Event showFull?: boolean }) { - const { showKinds, feedKindFilterBypass } = useKindFilterOrDefaults() - const allowLiveEmbeds = liveActivityKindsEnabledInPicker(showKinds, feedKindFilterBypass) - - 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 - } + /** Embeds are contextual to the parent note; home kind picker must not hide NIP-53 live cards here. */ + const allowLiveEmbeds = true return ( ( diff --git a/src/components/NoteList/index.tsx b/src/components/NoteList/index.tsx index 8af05127..b3ad7335 100644 --- a/src/components/NoteList/index.tsx +++ b/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). */ 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 /** Min ms between scroll-driven load-more attempts (loadMore also throttles internally). */ const LOAD_MORE_SCROLL_PREFETCH_COOLDOWN_MS = 180 @@ -937,6 +956,12 @@ const NoteList = forwardRef( showKindsRef.current = showKinds const effectiveShowKindsRef = useRef(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) progressiveDocumentKindsRef.current = progressiveDocumentKinds 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. - * 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( - () => withKindFilter && !showAllKinds && !seeAllFeedEvents, - [withKindFilter, showAllKinds, seeAllFeedEvents] + () => withKindFilter && !showAllKinds, + [withKindFilter, showAllKinds] ) const shouldHideEvent = useCallback( @@ -1037,14 +1064,9 @@ const NoteList = forwardRef( for (; i < maxScan && i < timelineEventsForFilter.length && out.length < target; i++) { const evt = timelineEventsForFilter[i]! if (applyKindPickerInUi) { - if (!effectiveShowKinds.includes(evt.kind)) continue - if (evt.kind === kinds.ShortTextNote) { - const isReply = isReplyNoteEvent(evt) - if (isReply && !showKind1Replies) continue - if (!isReply && !showKind1OPs) continue + if (!eventPassesNoteListKindPicker(evt, effectiveShowKinds, showKind1OPs, showKind1Replies, showKind1111)) { + continue } - if (evt.kind === ExtendedKind.COMMENT && !showKind1111) continue - if (evt.kind === ExtendedKind.GIT_RELEASE && !showKind1OPs) continue } if (shouldHideEvent(evt)) continue @@ -1118,14 +1140,17 @@ const NoteList = forwardRef( return newEvents.filter((event: Event) => { if (applyKindPickerInUi) { - 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 ( + !eventPassesNoteListKindPicker( + event, + effectiveShowKinds, + 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 @@ -1676,15 +1701,25 @@ const NoteList = forwardRef( } /** - * Kindless relay REQ: when {@link showAllKinds} is true (explorer / "All Events"), keep the full batch; - * otherwise narrow to effectiveShowKinds so the merged timeline matches {@link applyKindPickerInUi}. + * Relay kindless firehose: keep the full batch. Else when the kind picker applies, narrow like + * {@link applyKindPickerInUi}. Remaining spell paths use kinds-only narrowing when client-side kind filter runs. */ const narrowLiveBatch = (evs: Event[]) => { - if (seeAllFeedEventsRef.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 (!withKindFilterRef.current) return evs - return evs.filter((e) => effectiveShowKinds.includes(e.kind)) + return evs.filter((e) => effectiveShowKindsRef.current.includes(e.kind)) } if (oneShotFetch) { @@ -1746,7 +1781,6 @@ const NoteList = forwardRef( useFilterAsIs && clientSideKindFilter && withKindFilter && - !seeAllFeedEventsRef.current && (!allowKindlessRelayExplore || !showAllKinds) ) { relayOnly = relayOnly.filter((e) => effectiveShowKinds.includes(e.kind)) @@ -2084,25 +2118,39 @@ const NoteList = forwardRef( onNew: (event: Event) => { if (!effectActive) return feedRelayReturnedAnyEventRef.current = true - if (!seeAllFeedEventsRef.current && withKindFilterRef.current) { + if (withKindFilterRef.current) { const kindlessFirehose = allowKindlessRelayExploreRef.current && showAllKindsRef.current - if (!kindlessFirehose) { - 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 && !showKind1Replies) return - if (!isReply && !showKind1OPs) return + if (!kindlessFirehose) { + if (!showAllKindsRef.current) { + if ( + !eventPassesNoteListKindPicker( + event, + effectiveShowKindsRef.current, + showKind1OPsRef.current, + showKind1RepliesRef.current, + showKind1111Ref.current + ) + ) { + 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 @@ -2261,8 +2309,18 @@ const NoteList = forwardRef( : LIMIT const narrowDeltaBatch = (evs: Event[]) => { - if (seeAllFeedEventsRef.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 (!withKindFilterRef.current) return evs return evs.filter((e) => effectiveShowKindsRef.current.includes(e.kind)) @@ -2336,25 +2394,39 @@ const NoteList = forwardRef( onNew: (event: Event) => { if (!deltaActive) return feedRelayReturnedAnyEventRef.current = true - if (!seeAllFeedEventsRef.current && withKindFilterRef.current) { + if (withKindFilterRef.current) { const kindlessFirehose = allowKindlessRelayExploreRef.current && showAllKindsRef.current if (!kindlessFirehose) { - 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 && !showKind1Replies) return - if (!isReply && !showKind1OPs) return + if (!showAllKindsRef.current) { + if ( + !eventPassesNoteListKindPicker( + event, + effectiveShowKindsRef.current, + showKind1OPsRef.current, + showKind1RepliesRef.current, + showKind1111Ref.current + ) + ) { + 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 @@ -2694,12 +2766,21 @@ const NoteList = forwardRef( useFilterAsIsRef.current && clientSideKindFilterRef.current && withKindFilterRef.current && - !seeAllFeedEventsRef.current && (!allowKindlessRelayExploreRef.current || !showAllKindsRef.current) const existingIds = new Set(latestEvents.map((e) => e.id)) - const kindPasses = (e: Event) => - !narrowLoadMore || effectiveShowKindsRef.current.includes(e.kind) + const kindPasses = (e: Event) => { + 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 out: Event[] = [] diff --git a/src/components/ZapStreamLiveEventEmbed/index.tsx b/src/components/ZapStreamLiveEventEmbed/index.tsx index 133c1c4b..ced00ba6 100644 --- a/src/components/ZapStreamLiveEventEmbed/index.tsx +++ b/src/components/ZapStreamLiveEventEmbed/index.tsx @@ -1,9 +1,7 @@ import { EmbeddedNote } from '@/components/Embedded/EmbeddedNote' import ExternalLink from '@/components/ExternalLink' -import { liveActivityKindsEnabledInPicker } from '@/lib/live-activities' import { naddrFromZapStreamWatchUrl } from '@/lib/zap-stream-url' import { cn } from '@/lib/utils' -import { useKindFilterOrDefaults } from '@/providers/KindFilterProvider' import type { Event } from 'nostr-tools' /** 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 showFull?: boolean }) { - const { showKinds, feedKindFilterBypass } = useKindFilterOrDefaults() - if (!liveActivityKindsEnabledInPicker(showKinds, feedKindFilterBypass)) { - return - } const naddr = naddrFromZapStreamWatchUrl(url) if (!naddr) { return diff --git a/src/lib/live-activities.ts b/src/lib/live-activities.ts index 1cbd0abf..fb7e5984 100644 --- a/src/lib/live-activities.ts +++ b/src/lib/live-activities.ts @@ -36,7 +36,10 @@ export type LiveActivitiesFetchEventsFn = ( /** NIP-53 live streaming (30311), meeting space (30312), meeting (30313). */ 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 user’s selected kinds?” (e.g. optional UI); prefer inlining that check. + */ export function liveActivityKindsEnabledInPicker( showKinds: readonly number[], feedKindFilterBypass: boolean diff --git a/src/providers/KindFilterProvider.tsx b/src/providers/KindFilterProvider.tsx index eb8bde50..fc728c30 100644 --- a/src/providers/KindFilterProvider.tsx +++ b/src/providers/KindFilterProvider.tsx @@ -26,7 +26,7 @@ type TKindFilterContext = { showKind1OPs: boolean showKind1Replies: 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 updateShowKinds: ( kinds: number[],