diff --git a/src/views/catalog/__tests__/catalog.test.tsx b/src/views/catalog/__tests__/catalog.test.tsx index b38059eb..282eea5c 100644 --- a/src/views/catalog/__tests__/catalog.test.tsx +++ b/src/views/catalog/__tests__/catalog.test.tsx @@ -45,7 +45,7 @@ const testState = vi.hoisted(() => ({ } as Record }>, directories: [{ address: 'music-posting.eth', title: '/mu/ - Music' }] as Array<{ address: string; title?: string }>, feed: [] as TestComment[], - feedOptionsCalls: [] as Array<{ communitiesLength?: number; newerThan?: number; postsPerPage?: number; sortType?: string }>, + feedOptionsCalls: [] as Array<{ communitiesLength?: number; filterKey?: string; newerThan?: number; postsPerPage?: number; sortType?: string }>, filterItems: [] as FilterItem[], filteredDirectoryAddresses: ['music-posting.eth'] as string[], hasMore: false, @@ -121,7 +121,12 @@ const getScopedAccountComments = (options?: { commentIndices?: number[]; communi return scopedComments; }; -const getScopedFeed = (options?: { filter?: { filter: (comment: TestComment) => boolean }; newerThan?: number; postsPerPage?: number }) => { +type FeedFilter = { + filter: (comment: TestComment) => boolean; + key?: string; +}; + +const getScopedFeed = (options?: { filter?: FeedFilter; newerThan?: number; postsPerPage?: number }) => { let scopedFeed = [...testState.feed]; if (typeof options?.newerThan === 'number') { @@ -146,15 +151,10 @@ vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({ testState.accountCommentsCalls.push(options); return { accountComments: getScopedAccountComments(options) }; }, - useFeed: (options: { - communities?: unknown[]; - filter?: { filter: (comment: TestComment) => boolean }; - newerThan?: number; - postsPerPage?: number; - sortType?: string; - }) => { + useFeed: (options: { communities?: unknown[]; filter?: FeedFilter; newerThan?: number; postsPerPage?: number; sortType?: string }) => { testState.feedOptionsCalls.push({ communitiesLength: options.communities?.length, + filterKey: options.filter?.key, newerThan: options.newerThan, postsPerPage: options.postsPerPage, sortType: options.sortType, @@ -679,6 +679,15 @@ describe('Catalog', () => { expect(Array.from(container.querySelectorAll('[data-testid="catalog-row"]')).map((element) => element.textContent)).toEqual(['row:local-cats-post,network-post']); }); + it('reuses the board feed identity when catalog search and filters are inactive', async () => { + testState.sortType = 'active'; + testState.feed = [{ cid: 'network-post', title: 'cats on stage', communityAddress: 'music-posting.eth' }]; + + await renderCatalog({ initialEntry: '/mu/catalog', routePath: '/:boardIdentifier/catalog' }); + + expect(testState.feedOptionsCalls).toEqual(expect.arrayContaining([expect.objectContaining({ filterKey: 'exclude-archived' })])); + }); + it('chunks catalog rows safely even when the viewport is narrower than one card', async () => { testState.windowWidth = 0; testState.feed = [ diff --git a/src/views/catalog/catalog.tsx b/src/views/catalog/catalog.tsx index ee0cbc22..2de3958b 100644 --- a/src/views/catalog/catalog.tsx +++ b/src/views/catalog/catalog.tsx @@ -278,6 +278,17 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, const subscriptions = account?.subscriptions; const accountCommunityAddresses = useAccountCommunityAddresses(); const filteredDirectoryAddresses = useFilteredDirectoryAddresses(); + const excludeArchivedFilter = useMemo( + () => ({ + filter: (comment: Comment) => !isCommentArchived(comment), + key: 'exclude-archived', + }), + [], + ); + const hasActiveCatalogFiltering = useMemo( + () => searchText.trim().length > 0 || filterItems.some((item) => item.enabled && item.text.trim() !== ''), + [filterItems, searchText], + ); const communityAddresses = useMemo(() => { if (isInAllView) { @@ -337,12 +348,13 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, communities, sortType: feedSortType, postsPerPage: isMultiboard ? multiboardCatalogPostsPerPage : paginationFeedPostsPerPage, - filter: createCombinedFilter(filterItems, searchText, communityAddress || 'all', handleFilterMatch), + filter: hasActiveCatalogFiltering ? createCombinedFilter(filterItems, searchText, communityAddress || 'all', handleFilterMatch) : excludeArchivedFilter, newerThan: multiboardTimeFilterSeconds, }; }, [ communities, feedSortType, + hasActiveCatalogFiltering, isMultiboard, paginationFeedPostsPerPage, multiboardCatalogPostsPerPage, @@ -351,6 +363,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, communityAddress, handleFilterMatch, multiboardTimeFilterSeconds, + excludeArchivedFilter, ]); const { feed, hasMore, loadMore, reset, expandTimeWindow } = useFeed(feedOptions); @@ -364,8 +377,8 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, const shouldProbeMonthlyFeed = shouldProbeSuggestionFeeds && currentTimeFilterSeconds < MONTH_IN_SECONDS; const shouldProbeYearlyFeed = shouldProbeSuggestionFeeds && currentTimeFilterSeconds < YEAR_IN_SECONDS; const suggestionFilter = useMemo( - () => createCombinedFilter(filterItems, searchText, communityAddress || 'all', undefined, false), - [communityAddress, filterItems, searchText], + () => (hasActiveCatalogFiltering ? createCombinedFilter(filterItems, searchText, communityAddress || 'all', undefined, false) : excludeArchivedFilter), + [communityAddress, excludeArchivedFilter, filterItems, hasActiveCatalogFiltering, searchText], ); // Keep suggestion feeds on a stable hook identity; the loader widens them by paging, not by recreating the feed. const suggestionPostsPerPage = multiboardCatalogPostsPerPage;