From 30e7cde4937756fbc8a349ff1333131384d84f0c Mon Sep 17 00:00:00 2001 From: Silberengel Date: Fri, 27 Mar 2026 21:06:45 +0100 Subject: [PATCH] bug-fixes --- src/components/KindFilter/index.tsx | 17 ++- src/components/NormalFeed/index.tsx | 43 ++++++- src/components/Note/UnknownNote.tsx | 82 ++++++++++---- src/components/NoteList/index.tsx | 105 ++++++++++++------ src/components/Relay/index.tsx | 2 + src/i18n/locales/de.ts | 1 + src/i18n/locales/en.ts | 1 + src/pages/primary/NoteListPage/RelaysFeed.tsx | 11 +- 8 files changed, 197 insertions(+), 65 deletions(-) diff --git a/src/components/KindFilter/index.tsx b/src/components/KindFilter/index.tsx index 6a761260..24a0e3fc 100644 --- a/src/components/KindFilter/index.tsx +++ b/src/components/KindFilter/index.tsx @@ -308,11 +308,13 @@ export default function KindFilter({ {trigger} - + Filter - {content} +
+ {content} +
@@ -322,8 +324,15 @@ export default function KindFilter({ return ( {trigger} - - {content} + +
{content}
) diff --git a/src/components/NormalFeed/index.tsx b/src/components/NormalFeed/index.tsx index beea684f..9bb37d10 100644 --- a/src/components/NormalFeed/index.tsx +++ b/src/components/NormalFeed/index.tsx @@ -26,6 +26,10 @@ const NormalFeed = forwardRef(function NormalFeed( { subRequests, @@ -36,7 +40,10 @@ const NormalFeed = forwardRef JSON.stringify(showKinds), [showKinds]) + const subHeaderFilterDepsKey = allowKindlessRelayExplore + ? 'kindless-relay-explore' + : `${showKindsKey}|${feedKindFilterBypass}` + const tabsElement = ( {onSubHeaderRefresh != null && } - + {!allowKindlessRelayExplore && ( + + )} } /> @@ -100,11 +113,28 @@ const NormalFeed = forwardRef { if (!isMainFeed || !setSubHeader) return + if (allowKindlessRelayExplore) { + setSubHeader( + onSubHeaderRefresh != null ? ( +
+ +
+ ) : null + ) + return () => setSubHeader(null) + } setSubHeader(tabsElement) return () => setSubHeader(null) - }, [isMainFeed, setSubHeader, listMode, showKindsKey, feedKindFilterBypass, onSubHeaderRefresh]) + }, [ + isMainFeed, + setSubHeader, + listMode, + subHeaderFilterDepsKey, + onSubHeaderRefresh, + allowKindlessRelayExplore + ]) - const renderTabsInFeed = !(isMainFeed && setSubHeader) + const renderTabsInFeed = !(isMainFeed && setSubHeader) && !allowKindlessRelayExplore return ( <> @@ -118,13 +148,16 @@ const NormalFeed = forwardRef diff --git a/src/components/Note/UnknownNote.tsx b/src/components/Note/UnknownNote.tsx index 2fb211fe..e6d75fe3 100644 --- a/src/components/Note/UnknownNote.tsx +++ b/src/components/Note/UnknownNote.tsx @@ -34,6 +34,9 @@ const ELEVATED_TAG_NAMES = new Set([ 'pubkey' ]) +/** e / p / q / a: thread & pubkey refs — noisy in preview; show under Technical details only. */ +const TECHNICAL_ONLY_TAG_NAMES = new Set(['e', 'p', 'q', 'a']) + function truncatePreview(text: string, max: number): string { const t = text.trim() if (t.length <= max) return t @@ -166,7 +169,17 @@ export default function UnknownNote({ const elevated = useMemo(() => extractElevatedTags(event.tags), [event.tags]) const remainderTags = useMemo( - () => event.tags.filter(tag => tag[0] && !ELEVATED_TAG_NAMES.has(tag[0])), + () => event.tags.filter((tag) => tag[0] && !ELEVATED_TAG_NAMES.has(tag[0])), + [event.tags] + ) + + const mainCardTags = useMemo( + () => remainderTags.filter((tag) => !TECHNICAL_ONLY_TAG_NAMES.has(tag[0])), + [remainderTags] + ) + + const technicalReferenceTags = useMemo( + () => event.tags.filter((tag) => tag[0] && TECHNICAL_ONLY_TAG_NAMES.has(tag[0])), [event.tags] ) @@ -199,22 +212,23 @@ export default function UnknownNote({ const showNoTextPlaceholder = !contentRaw && !hasAnyElevatedCopy && !isBookstrEvent - const proseClass = 'text-sm leading-relaxed whitespace-pre-wrap break-words text-foreground/95' + const proseClass = + 'text-xs leading-snug whitespace-pre-wrap break-words text-foreground/95' return (
-
-

+

+

{t('Unsupported event preview')}

{showAuthorSummary && isValidPubkey(event.pubkey) ? ( -
+
-

{headline}

+

{headline}

{!omitKindLabel ? ( - + ) : null} {elevated.title?.trim() && !omitKindLabel ? ( -

+

{kindLabel.description} · {t('Event kind label', { kind: event.kind })} @@ -252,12 +266,12 @@ export default function UnknownNote({ {elevated.topics.length > 0 ? (

-

+

{t('Topics')}

-
+
{elevated.topics.map((topic, i) => ( - + {topic} ))} @@ -272,7 +286,7 @@ export default function UnknownNote({ key={`${url}-${i}`} src={url} alt="" - className="max-h-52 w-full rounded-md border border-border object-cover bg-muted" + className="max-h-40 w-full rounded-md border border-border object-cover bg-muted" loading="lazy" referrerPolicy="no-referrer" /> @@ -282,7 +296,7 @@ export default function UnknownNote({ {elevated.summary ? (
-

+

{t('Summary')}

{truncatePreview(elevated.summary, CONTENT_PREVIEW_MAX)}

@@ -291,7 +305,7 @@ export default function UnknownNote({ {elevated.description ? (
-

+

{t('Description')}

{truncatePreview(elevated.description, CONTENT_PREVIEW_MAX)}

@@ -300,7 +314,7 @@ export default function UnknownNote({ {elevated.tagContent && normText(elevated.tagContent) !== contentNorm ? (
-

+

{t('Unknown note tagged content')}

{truncatePreview(elevated.tagContent, CONTENT_PREVIEW_MAX)}

@@ -322,17 +336,17 @@ export default function UnknownNote({ ) : null} {showNoTextPlaceholder ? ( -

{t('No text content in event')}

+

{t('No text content in event')}

) : null} - {remainderTags.length > 0 ? ( -
-

+ {mainCardTags.length > 0 ? ( +

+

{t('Tags')}

-
    - {remainderTags.map((tag, i) => ( -
  • +
      + {mainCardTags.map((tag, i) => ( +
    • {tag[0]} {tag.length > 1 ? tag.slice(1).join(' · ') : '—'} @@ -362,7 +376,27 @@ export default function UnknownNote({ )} - + + {technicalReferenceTags.length > 0 ? ( +
      +

      + {t('Unknown note reference tags')} +

      +
        + {technicalReferenceTags.map((tag, i) => ( +
      • + {tag[0]} + {tag.length > 1 ? ( + <> + {' '} + {tag.slice(1).join(' · ')} + + ) : null} +
      • + ))} +
      +
      + ) : null}
      diff --git a/src/components/NoteList/index.tsx b/src/components/NoteList/index.tsx index 54fc67d8..a2624d7e 100644 --- a/src/components/NoteList/index.tsx +++ b/src/components/NoteList/index.tsx @@ -61,6 +61,8 @@ import NoteCard, { NoteCardLoadingSkeleton } from '../NoteCard' const LIMIT = 100 // Increased from 200 to load more events per request const ALGO_LIMIT = 200 // Increased from 500 for algorithm feeds +/** Single-relay explore: kindless REQ cap (relay returns whatever it has, up to this many). */ +const RELAY_EXPLORE_LIMIT = 200 /** * Vite HMR replaces this module and remounts NoteList; timeline refs reset while the subscription can briefly look @@ -97,11 +99,13 @@ function mergeEventBatchesById(prev: Event[], incoming: Event[], cap: number): E /** When omitting `kinds` from a live REQ, require another scope so we never subscribe to a whole relay. */ function timelineFilterHasNonKindScope(f: Filter): boolean { + const search = f.search return ( (Array.isArray(f.authors) && f.authors.length > 0) || (Array.isArray(f.ids) && f.ids.length > 0) || (Array.isArray(f['#p']) && f['#p']!.length > 0) || - (Array.isArray(f['#e']) && f['#e']!.length > 0) + (Array.isArray(f['#e']) && f['#e']!.length > 0) || + (typeof search === 'string' && search.trim().length > 0) ) } @@ -114,6 +118,10 @@ const NoteList = forwardRef( showKind1Replies = true, showKind1111 = true, seeAllFeedEvents = false, + /** + * Single-relay Explore / home chip: REQ omits `kinds`, limit 200, no feed kind filter (relay decides what to send). + */ + allowKindlessRelayExplore = false, filterMutedNotes = true, hideReplies = false, hideUntrustedNotes = false, @@ -184,6 +192,7 @@ const NoteList = forwardRef( showKind1111?: boolean /** Omit REQ kinds and skip client-side kind filtering (main feed testing). Ignored when useFilterAsIs. */ seeAllFeedEvents?: boolean + allowKindlessRelayExplore?: boolean filterMutedNotes?: boolean hideReplies?: boolean hideUntrustedNotes?: boolean @@ -376,11 +385,15 @@ const NoteList = forwardRef( () => JSON.stringify({ feed: timelineSubscriptionKey, - kinds: showKindsKey, - op: showKind1OPs, - rep: showKind1Replies, - c1111: showKind1111, - seeAll: seeAllFeedEvents + ...(allowKindlessRelayExplore + ? { relayKindless: true } + : { + kinds: showKindsKey, + op: showKind1OPs, + rep: showKind1Replies, + c1111: showKind1111, + seeAll: seeAllFeedEvents + }) }), [ timelineSubscriptionKey, @@ -388,14 +401,22 @@ const NoteList = forwardRef( showKind1OPs, showKind1Replies, showKind1111, - seeAllFeedEvents + seeAllFeedEvents, + allowKindlessRelayExplore ] ) + /** Kindless relay explore ignores the feed kind picker; avoid re-subscribing when it changes. */ + const timelineResubscribeKindKey = allowKindlessRelayExplore + ? 'kindless-relay-explore' + : `${showKindsKey}|${showKind1OPs}|${showKind1Replies}|${showKind1111}` + const showKindsRef = useRef(showKinds) showKindsRef.current = showKinds const seeAllFeedEventsRef = useRef(seeAllFeedEvents) seeAllFeedEventsRef.current = seeAllFeedEvents + const allowKindlessRelayExploreRef = useRef(allowKindlessRelayExplore) + allowKindlessRelayExploreRef.current = allowKindlessRelayExplore const useFilterAsIsRef = useRef(useFilterAsIs) useFilterAsIsRef.current = useFilterAsIs const clientSideKindFilterRef = useRef(clientSideKindFilter) @@ -464,7 +485,7 @@ const NoteList = forwardRef( const idSet = new Set() return events.slice(0, showCount).filter((evt) => { - if (!seeAllFeedEvents) { + if (!seeAllFeedEvents && !allowKindlessRelayExplore) { if (!showKinds.includes(evt.kind)) return false // Kind 1: show only OPs if showKind1OPs, only replies if showKind1Replies if (evt.kind === kinds.ShortTextNote) { @@ -492,7 +513,8 @@ const NoteList = forwardRef( showKind1OPs, showKind1Replies, showKind1111, - seeAllFeedEvents + seeAllFeedEvents, + allowKindlessRelayExplore ]) useLayoutEffect(() => { @@ -538,7 +560,7 @@ const NoteList = forwardRef( const idSet = new Set() return newEvents.filter((event: Event) => { - if (!seeAllFeedEvents) { + if (!seeAllFeedEvents && !allowKindlessRelayExplore) { if (!showKinds.includes(event.kind)) return false if (event.kind === kinds.ShortTextNote) { const isReply = isReplyNoteEvent(event) @@ -565,7 +587,8 @@ const NoteList = forwardRef( showKind1OPs, showKind1Replies, showKind1111, - seeAllFeedEvents + seeAllFeedEvents, + allowKindlessRelayExplore ]) useLayoutEffect(() => { @@ -792,8 +815,16 @@ const NoteList = forwardRef( const mappedSubRequests = subRequestsRef.current.map(({ urls, filter }) => { const baseLimit = filter.limit ?? (areAlgoRelays ? ALGO_LIMIT : LIMIT) if (useFilterAsIs) { - const finalFilter: Filter = { ...filter, limit: baseLimit } const hasKindsInRequest = Array.isArray(filter.kinds) && filter.kinds.length > 0 + if (allowKindlessRelayExplore && urls.length === 1 && !hasKindsInRequest) { + const finalFilter: Filter = { + ...filter, + limit: filter.limit ?? RELAY_EXPLORE_LIMIT + } + delete finalFilter.kinds + return { urls, filter: finalFilter } + } + const finalFilter: Filter = { ...filter, limit: baseLimit } if (clientSideKindFilter) { if (hasKindsInRequest) { finalFilter.kinds = filter.kinds @@ -828,10 +859,13 @@ const NoteList = forwardRef( }) const filterMissingKinds = (f: Filter) => !f.kinds || f.kinds.length === 0 - const invalidFilters = mappedSubRequests.filter(({ filter: f }) => { + const invalidFilters = mappedSubRequests.filter(({ urls, filter: f }) => { if (seeAllNoSpell) return false if (!filterMissingKinds(f)) return false if (useFilterAsIs && clientSideKindFilter && timelineFilterHasNonKindScope(f)) return false + if (useFilterAsIs && allowKindlessRelayExplore && urls.length === 1) { + return false + } return true }) if (invalidFilters.length > 0) { @@ -849,7 +883,7 @@ const NoteList = forwardRef( } const narrowLiveBatch = (evs: Event[]) => { - if (seeAllNoSpell) return evs + if (seeAllFeedEventsRef.current || allowKindlessRelayExploreRef.current) return evs if (!useFilterAsIs || !clientSideKindFilter) return evs return evs.filter((e) => showKinds.includes(e.kind)) } @@ -886,7 +920,12 @@ const NoteList = forwardRef( let merged = [...byId.values()] .sort((a, b) => b.created_at - a.created_at) .slice(0, cap) - if (useFilterAsIs && clientSideKindFilter) { + if ( + useFilterAsIs && + clientSideKindFilter && + !seeAllFeedEventsRef.current && + !allowKindlessRelayExploreRef.current + ) { merged = merged.filter((e) => showKinds.includes(e.kind)) } if (sessionSnap?.length && !userPulledRefresh) { @@ -970,7 +1009,11 @@ const NoteList = forwardRef( }, subscribeSetupRaceMs) }) - const eventCap = areAlgoRelays ? ALGO_LIMIT : LIMIT + const eventCap = allowKindlessRelayExplore + ? RELAY_EXPLORE_LIMIT + : areAlgoRelays + ? ALGO_LIMIT + : LIMIT timelineSubscribePromise = client.subscribeTimeline( mappedSubRequests as Array<{ urls: string[]; filter: TSubRequestFilter }>, @@ -1084,10 +1127,9 @@ const NoteList = forwardRef( onNew: (event: Event) => { if (!effectActive) return feedRelayReturnedAnyEventRef.current = true - const seeAll = seeAllFeedEventsRef.current && !useFilterAsIs - if (!seeAll && !useFilterAsIs && !showKinds.includes(event.kind)) return - if (clientSideKindFilter && useFilterAsIs && !showKinds.includes(event.kind)) return - if (!seeAll) { + if (!seeAllFeedEventsRef.current && !allowKindlessRelayExploreRef.current) { + if (!useFilterAsIs && !showKinds.includes(event.kind)) return + if (clientSideKindFilter && useFilterAsIs && !showKinds.includes(event.kind)) return if (event.kind === kinds.ShortTextNote) { const isReply = isReplyNoteEvent(event) if (isReply && !showKind1Replies) return @@ -1179,10 +1221,7 @@ const NoteList = forwardRef( mergeTimelineWhenSubRequestFiltersMatch, feedTimelineScopeKey, refreshCount, - showKindsKey, - showKind1OPs, - showKind1Replies, - showKind1111, + timelineResubscribeKindKey, seeAllFeedEvents, useFilterAsIs, areAlgoRelays, @@ -1194,7 +1233,8 @@ const NoteList = forwardRef( oneShotGlobalTimeoutMs, oneShotEoseTimeoutMs, oneShotFirstRelayGraceMs, - clientSideKindFilter + clientSideKindFilter, + allowKindlessRelayExplore ]) const oneShotDebugPrevLoadingRef = useRef(false) @@ -1458,14 +1498,17 @@ const NoteList = forwardRef( } let fetchBatch = newEvents - let toAppend = - useFilterAsIsRef.current && clientSideKindFilterRef.current - ? fetchBatch.filter((e) => showKindsRef.current.includes(e.kind)) - : fetchBatch - - if ( + const narrowLoadMore = useFilterAsIsRef.current && clientSideKindFilterRef.current && + !seeAllFeedEventsRef.current && + !allowKindlessRelayExploreRef.current + let toAppend = narrowLoadMore + ? fetchBatch.filter((e) => showKindsRef.current.includes(e.kind)) + : fetchBatch + + if ( + narrowLoadMore && toAppend.length === 0 && fetchBatch.length > 0 ) { diff --git a/src/components/Relay/index.tsx b/src/components/Relay/index.tsx index 49cfa518..ee77d26a 100644 --- a/src/components/Relay/index.tsx +++ b/src/components/Relay/index.tsx @@ -82,6 +82,8 @@ const Relay = forwardRef(fun subRequests={[ { urls: [normalizedUrl], filter: debouncedInput ? { search: debouncedInput } : {} } ]} + useFilterAsIs + allowKindlessRelayExplore />
) diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index a2be4872..cf6bb8c0 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -1437,6 +1437,7 @@ export default { 'Subscribed to topic (local)': 'Subscribed to topic (local)', 'Subscribing...': 'Subscribing...', Summary: 'Summary', + 'Unknown note reference tags': 'Referenz-Tags (e, p, q, a)', 'Supported Event Types': 'Supported Event Types', 'Take a note': 'Take a note', 'The full prompt conversation (optional)': 'The full prompt conversation (optional)', diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 34e5c4cc..fe8cb8a6 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -412,6 +412,7 @@ export default { 'Unknown note declared kind tag': 'Tagged kind: {{value}}', 'Unknown note tagged pubkey': 'Tagged pubkey', 'Unknown note tagged content': 'Content', + 'Unknown note reference tags': 'Reference tags (e, p, q, a)', 'Copy JSON': 'Copy JSON', Verse: 'Verse', 'Notification reaction summary': 'reacted to this note.', diff --git a/src/pages/primary/NoteListPage/RelaysFeed.tsx b/src/pages/primary/NoteListPage/RelaysFeed.tsx index 49f662c7..21086db7 100644 --- a/src/pages/primary/NoteListPage/RelaysFeed.tsx +++ b/src/pages/primary/NoteListPage/RelaysFeed.tsx @@ -75,6 +75,10 @@ const RelaysFeed = forwardRef< ? showKinds : [kinds.ShortTextNote] + /** One relay + user kind filter: avoid huge `kinds` REQ (many relays error with "too many kinds"). */ + const singleRelayKindlessExplore = + feedInfo.feedType === 'relay' && relayUrls.length === 1 && !kindsOverride?.length + const canRenderFeed = (feedInfo.feedType === 'relay' || feedInfo.feedType === 'relays' || @@ -95,6 +99,9 @@ const RelaysFeed = forwardRef< // Hooks must run every render — never place useMemo after conditional returns. const subRequests = useMemo(() => { if (!canRenderFeed) return [] + if (singleRelayKindlessExplore) { + return [{ urls: relayUrls, filter: {} }] + } return [ { urls: relayUrls, @@ -103,7 +110,7 @@ const RelaysFeed = forwardRef< } } ] - }, [canRenderFeed, relayUrls, defaultKinds, kindsOverride]) + }, [canRenderFeed, relayUrls, defaultKinds, kindsOverride, singleRelayKindlessExplore]) if (!canRenderFeed) { return null @@ -123,6 +130,8 @@ const RelaysFeed = forwardRef< onSubHeaderRefresh={onSubHeaderRefresh} preserveTimelineOnSubRequestsChange feedTimelineScopeKey={feedTimelineScopeKey} + useFilterAsIs={singleRelayKindlessExplore} + allowKindlessRelayExplore={singleRelayKindlessExplore} /> ) })