mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(catalog): align pagination and postsPerPage with board view
Use useBoardFeedPageSize and useDirectoryByAddress so catalog respects directory postsPerPage, caps feed to guiPostsPerPage * 10 in non-infinite mode, and conditionally renders Virtuoso based on enableInfiniteScroll.
This commit is contained in:
@@ -4,7 +4,8 @@ import { Trans, useTranslation } from 'react-i18next';
|
|||||||
import { Comment, useAccount, useFeed, useSubplebbit, useAccountComments } from '@plebbit/plebbit-react-hooks';
|
import { Comment, useAccount, useFeed, useSubplebbit, useAccountComments } from '@plebbit/plebbit-react-hooks';
|
||||||
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
||||||
import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows';
|
import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows';
|
||||||
import { useDirectories } from '../../hooks/use-directories';
|
import { useDirectories, useDirectoryByAddress } from '../../hooks/use-directories';
|
||||||
|
import { useBoardFeedPageSize } from '../../hooks/use-board-feed-page-size';
|
||||||
import { useFilteredDirectoryAddresses } from '../../hooks/use-filtered-directory-addresses';
|
import { useFilteredDirectoryAddresses } from '../../hooks/use-filtered-directory-addresses';
|
||||||
import { useResolvedSubplebbitAddress, useBoardPath } from '../../hooks/use-resolved-subplebbit-address';
|
import { useResolvedSubplebbitAddress, useBoardPath } from '../../hooks/use-resolved-subplebbit-address';
|
||||||
import { useFeedStateString } from '../../hooks/use-state-string';
|
import { useFeedStateString } from '../../hooks/use-state-string';
|
||||||
@@ -12,6 +13,7 @@ import useTimeFilter, { timeFilterNameToSeconds } from '../../hooks/use-time-fil
|
|||||||
import useWindowWidth from '../../hooks/use-window-width';
|
import useWindowWidth from '../../hooks/use-window-width';
|
||||||
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
||||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||||
|
import useFeedViewSettingsStore from '../../stores/use-feed-view-settings-store';
|
||||||
import useSortingStore from '../../stores/use-sorting-store';
|
import useSortingStore from '../../stores/use-sorting-store';
|
||||||
import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
|
import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
|
||||||
import { getSubplebbitAddress, isDirectoryBoard } from '../../lib/utils/route-utils';
|
import { getSubplebbitAddress, isDirectoryBoard } from '../../lib/utils/route-utils';
|
||||||
@@ -38,6 +40,8 @@ interface CatalogFooterProps {
|
|||||||
yearlyFeedLength: number;
|
yearlyFeedLength: number;
|
||||||
boardPath: string | undefined;
|
boardPath: string | undefined;
|
||||||
currentTimeFilterName: string | undefined;
|
currentTimeFilterName: string | undefined;
|
||||||
|
/** When false, suppress the loading ellipsis (e.g. non-infinite mode) */
|
||||||
|
showLoadingEllipsis?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defined outside Catalog to preserve component identity across renders (Virtuoso optimization)
|
// Defined outside Catalog to preserve component identity across renders (Virtuoso optimization)
|
||||||
@@ -58,6 +62,7 @@ const CatalogFooter = ({
|
|||||||
yearlyFeedLength,
|
yearlyFeedLength,
|
||||||
boardPath,
|
boardPath,
|
||||||
currentTimeFilterName,
|
currentTimeFilterName,
|
||||||
|
showLoadingEllipsis = true,
|
||||||
}: CatalogFooterProps) => {
|
}: CatalogFooterProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -121,9 +126,11 @@ const CatalogFooter = ({
|
|||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
<div className={styles.stateString}>
|
{showLoadingEllipsis && (
|
||||||
<LoadingEllipsis string={loadingStateString} />
|
<div className={styles.stateString}>
|
||||||
</div>
|
<LoadingEllipsis string={loadingStateString} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -313,6 +320,10 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
const columnCount = Math.floor(useWindowWidth() / columnWidth);
|
const columnCount = Math.floor(useWindowWidth() / columnWidth);
|
||||||
const postsPerPage = columnCount <= 2 ? 10 : columnCount === 3 ? 15 : columnCount === 4 ? 20 : 25;
|
const postsPerPage = columnCount <= 2 ? 10 : columnCount === 3 ? 15 : columnCount === 4 ? 20 : 25;
|
||||||
|
|
||||||
|
const enableInfiniteScroll = useFeedViewSettingsStore((state) => state.enableInfiniteScroll);
|
||||||
|
const community = useDirectoryByAddress(isInAllView || isInSubscriptionsView ? undefined : subplebbitAddress);
|
||||||
|
const { guiPostsPerPage: boardPostsPerPage, maxGuiPages, paginationFeedPostsPerPage, infiniteFeedPostsPerPage } = useBoardFeedPageSize(community);
|
||||||
|
|
||||||
const { timeFilterSeconds: timeFilterSecondsFromHook, timeFilterName: timeFilterNameFromHook } = useTimeFilter();
|
const { timeFilterSeconds: timeFilterSecondsFromHook, timeFilterName: timeFilterNameFromHook } = useTimeFilter();
|
||||||
const { sortType } = useSortingStore();
|
const { sortType } = useSortingStore();
|
||||||
const timeFilterName = timeFilterNameFromCache || timeFilterNameFromHook;
|
const timeFilterName = timeFilterNameFromCache || timeFilterNameFromHook;
|
||||||
@@ -332,10 +343,13 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
}, [subplebbitAddress]);
|
}, [subplebbitAddress]);
|
||||||
|
|
||||||
const feedOptions = useMemo(() => {
|
const feedOptions = useMemo(() => {
|
||||||
|
const catalogPostsPerPage =
|
||||||
|
isInAllView || isInSubscriptionsView ? (enableInfiniteScroll ? 10 : 100) : enableInfiniteScroll ? infiniteFeedPostsPerPage : paginationFeedPostsPerPage;
|
||||||
|
|
||||||
const options: any = {
|
const options: any = {
|
||||||
subplebbitAddresses,
|
subplebbitAddresses,
|
||||||
sortType,
|
sortType,
|
||||||
postsPerPage: isInAllView || isInSubscriptionsView ? 10 : postsPerPage,
|
postsPerPage: catalogPostsPerPage,
|
||||||
filter: createCombinedFilter(filterItems, searchText, subplebbitAddress || 'all', handleFilterMatch),
|
filter: createCombinedFilter(filterItems, searchText, subplebbitAddress || 'all', handleFilterMatch),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -344,7 +358,20 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}, [subplebbitAddresses, sortType, isInAllView, isInSubscriptionsView, postsPerPage, timeFilterSeconds, filterItems, searchText, subplebbitAddress, handleFilterMatch]);
|
}, [
|
||||||
|
subplebbitAddresses,
|
||||||
|
sortType,
|
||||||
|
isInAllView,
|
||||||
|
isInSubscriptionsView,
|
||||||
|
enableInfiniteScroll,
|
||||||
|
infiniteFeedPostsPerPage,
|
||||||
|
paginationFeedPostsPerPage,
|
||||||
|
timeFilterSeconds,
|
||||||
|
filterItems,
|
||||||
|
searchText,
|
||||||
|
subplebbitAddress,
|
||||||
|
handleFilterMatch,
|
||||||
|
]);
|
||||||
|
|
||||||
const { feed, hasMore, loadMore, reset, subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
|
const { feed, hasMore, loadMore, reset, subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
|
||||||
const { accountComments } = useAccountComments();
|
const { accountComments } = useAccountComments();
|
||||||
@@ -392,6 +419,11 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
return newFeed;
|
return newFeed;
|
||||||
}, [feed, filteredComments]);
|
}, [feed, filteredComments]);
|
||||||
|
|
||||||
|
const cappedFeed = useMemo(
|
||||||
|
() => (enableInfiniteScroll ? combinedFeed : combinedFeed.slice(0, boardPostsPerPage * maxGuiPages)),
|
||||||
|
[enableInfiniteScroll, combinedFeed, boardPostsPerPage, maxGuiPages],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (filteredComments.length > 0 && !resetTriggeredRef.current) {
|
if (filteredComments.length > 0 && !resetTriggeredRef.current) {
|
||||||
reset();
|
reset();
|
||||||
@@ -462,7 +494,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
subplebbitAddresses={subplebbitAddresses}
|
subplebbitAddresses={subplebbitAddresses}
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
feedLength={feedLength}
|
feedLength={feedLength}
|
||||||
combinedFeedLength={combinedFeed.length}
|
combinedFeedLength={cappedFeed.length}
|
||||||
subplebbitAddressesWithNewerPosts={subplebbitAddressesWithNewerPosts}
|
subplebbitAddressesWithNewerPosts={subplebbitAddressesWithNewerPosts}
|
||||||
onNewerPostsClick={handleNewerPostsButtonClick}
|
onNewerPostsClick={handleNewerPostsButtonClick}
|
||||||
isInAllView={isInAllView}
|
isInAllView={isInAllView}
|
||||||
@@ -473,6 +505,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
yearlyFeedLength={yearlyFeedLength}
|
yearlyFeedLength={yearlyFeedLength}
|
||||||
boardPath={boardPath}
|
boardPath={boardPath}
|
||||||
currentTimeFilterName={currentTimeFilterName}
|
currentTimeFilterName={currentTimeFilterName}
|
||||||
|
showLoadingEllipsis={enableInfiniteScroll}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
@@ -480,7 +513,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
subplebbitAddresses,
|
subplebbitAddresses,
|
||||||
hasMore,
|
hasMore,
|
||||||
feedLength,
|
feedLength,
|
||||||
combinedFeed.length,
|
cappedFeed.length,
|
||||||
subplebbitAddressesWithNewerPosts,
|
subplebbitAddressesWithNewerPosts,
|
||||||
handleNewerPostsButtonClick,
|
handleNewerPostsButtonClick,
|
||||||
isInAllView,
|
isInAllView,
|
||||||
@@ -491,6 +524,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
yearlyFeedLength,
|
yearlyFeedLength,
|
||||||
boardPath,
|
boardPath,
|
||||||
currentTimeFilterName,
|
currentTimeFilterName,
|
||||||
|
enableInfiniteScroll,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -498,16 +532,16 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
|
|
||||||
// Process the feed to move "top" posts to the top
|
// Process the feed to move "top" posts to the top
|
||||||
const processedFeed = useMemo(() => {
|
const processedFeed = useMemo(() => {
|
||||||
if (!combinedFeed || combinedFeed.length === 0) return combinedFeed;
|
if (!cappedFeed || cappedFeed.length === 0) return cappedFeed;
|
||||||
|
|
||||||
const enabledTopFilters = filterItems.filter((item) => item.enabled && item.text.trim() !== '' && item.top);
|
const enabledTopFilters = filterItems.filter((item) => item.enabled && item.text.trim() !== '' && item.top);
|
||||||
if (enabledTopFilters.length === 0) return combinedFeed;
|
if (enabledTopFilters.length === 0) return cappedFeed;
|
||||||
|
|
||||||
// Separate posts that match "top" filters
|
// Separate posts that match "top" filters
|
||||||
const topPosts: Comment[] = [];
|
const topPosts: Comment[] = [];
|
||||||
const regularPosts: Comment[] = [];
|
const regularPosts: Comment[] = [];
|
||||||
|
|
||||||
combinedFeed.forEach((comment) => {
|
cappedFeed.forEach((comment) => {
|
||||||
if (!comment) return;
|
if (!comment) return;
|
||||||
|
|
||||||
let isTop = false;
|
let isTop = false;
|
||||||
@@ -527,7 +561,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
|
|
||||||
// Return top posts followed by regular posts
|
// Return top posts followed by regular posts
|
||||||
return [...topPosts, ...regularPosts];
|
return [...topPosts, ...regularPosts];
|
||||||
}, [combinedFeed, filterItems]);
|
}, [cappedFeed, filterItems]);
|
||||||
|
|
||||||
const rows = useCatalogFeedRows(columnCount, processedFeed, isFeedLoaded, subplebbit);
|
const rows = useCatalogFeedRows(columnCount, processedFeed, isFeedLoaded, subplebbit);
|
||||||
|
|
||||||
@@ -589,12 +623,12 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
|
|
||||||
// Memoize filter color application to avoid redundant iterations
|
// Memoize filter color application to avoid redundant iterations
|
||||||
useMemo(() => {
|
useMemo(() => {
|
||||||
if (combinedFeed.length > 0 && filterItems.length > 0) {
|
if (cappedFeed.length > 0 && filterItems.length > 0) {
|
||||||
// Clear existing matched filters
|
// Clear existing matched filters
|
||||||
clearMatchedFilters();
|
clearMatchedFilters();
|
||||||
|
|
||||||
// Apply colors to posts that match filters
|
// Apply colors to posts that match filters
|
||||||
combinedFeed.forEach((comment) => {
|
cappedFeed.forEach((comment) => {
|
||||||
if (!comment?.cid) return;
|
if (!comment?.cid) return;
|
||||||
|
|
||||||
// Check each filter
|
// Check each filter
|
||||||
@@ -608,7 +642,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [combinedFeed, filterItems, clearMatchedFilters]);
|
}, [cappedFeed, filterItems, clearMatchedFilters]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
@@ -616,20 +650,44 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
<div className={styles.catalog}>
|
<div className={styles.catalog}>
|
||||||
{processedFeed?.length !== 0 ? (
|
{processedFeed?.length !== 0 ? (
|
||||||
<>
|
<>
|
||||||
{/* Use Virtuoso for infinite scroll only when there's more content to paginate */}
|
{enableInfiniteScroll ? (
|
||||||
{hasMore ? (
|
hasMore ? (
|
||||||
<Virtuoso
|
<Virtuoso
|
||||||
increaseViewportBy={{ bottom: 1200, top: 1200 }}
|
increaseViewportBy={{ bottom: 1200, top: 1200 }}
|
||||||
totalCount={rows?.length || 0}
|
totalCount={rows?.length || 0}
|
||||||
data={rows}
|
data={rows}
|
||||||
itemContent={(index, row) => <CatalogRow index={index} row={row} />}
|
itemContent={(index, row) => <CatalogRow index={index} row={row} />}
|
||||||
useWindowScroll={true}
|
useWindowScroll={true}
|
||||||
components={footerComponents}
|
components={footerComponents}
|
||||||
endReached={loadMore}
|
endReached={loadMore}
|
||||||
ref={virtuosoRef}
|
ref={virtuosoRef}
|
||||||
restoreStateFrom={lastVirtuosoState}
|
restoreStateFrom={lastVirtuosoState}
|
||||||
initialScrollTop={lastVirtuosoState?.scrollTop}
|
initialScrollTop={lastVirtuosoState?.scrollTop}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{rows.map((row, index) => (
|
||||||
|
<CatalogRow key={index} index={index} row={row} />
|
||||||
|
))}
|
||||||
|
<CatalogFooter
|
||||||
|
subplebbitAddresses={subplebbitAddresses}
|
||||||
|
hasMore={hasMore}
|
||||||
|
feedLength={feedLength}
|
||||||
|
combinedFeedLength={cappedFeed.length}
|
||||||
|
subplebbitAddressesWithNewerPosts={subplebbitAddressesWithNewerPosts}
|
||||||
|
onNewerPostsClick={handleNewerPostsButtonClick}
|
||||||
|
isInAllView={isInAllView}
|
||||||
|
isInSubscriptionsView={isInSubscriptionsView}
|
||||||
|
showMorePostsSuggestion={showMorePostsSuggestion}
|
||||||
|
weeklyFeedLength={weeklyFeedLength}
|
||||||
|
monthlyFeedLength={monthlyFeedLength}
|
||||||
|
yearlyFeedLength={yearlyFeedLength}
|
||||||
|
boardPath={boardPath}
|
||||||
|
currentTimeFilterName={currentTimeFilterName}
|
||||||
|
showLoadingEllipsis={true}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{rows.map((row, index) => (
|
{rows.map((row, index) => (
|
||||||
@@ -639,7 +697,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
subplebbitAddresses={subplebbitAddresses}
|
subplebbitAddresses={subplebbitAddresses}
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
feedLength={feedLength}
|
feedLength={feedLength}
|
||||||
combinedFeedLength={combinedFeed.length}
|
combinedFeedLength={cappedFeed.length}
|
||||||
subplebbitAddressesWithNewerPosts={subplebbitAddressesWithNewerPosts}
|
subplebbitAddressesWithNewerPosts={subplebbitAddressesWithNewerPosts}
|
||||||
onNewerPostsClick={handleNewerPostsButtonClick}
|
onNewerPostsClick={handleNewerPostsButtonClick}
|
||||||
isInAllView={isInAllView}
|
isInAllView={isInAllView}
|
||||||
@@ -650,6 +708,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
yearlyFeedLength={yearlyFeedLength}
|
yearlyFeedLength={yearlyFeedLength}
|
||||||
boardPath={boardPath}
|
boardPath={boardPath}
|
||||||
currentTimeFilterName={currentTimeFilterName}
|
currentTimeFilterName={currentTimeFilterName}
|
||||||
|
showLoadingEllipsis={false}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -665,7 +724,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
|||||||
yearlyFeedLength={yearlyFeedLength}
|
yearlyFeedLength={yearlyFeedLength}
|
||||||
state={state}
|
state={state}
|
||||||
subscriptionsLength={isInSubscriptionsView ? subscriptions?.length || 0 : 1}
|
subscriptionsLength={isInSubscriptionsView ? subscriptions?.length || 0 : 1}
|
||||||
combinedFeedLength={combinedFeed.length}
|
combinedFeedLength={cappedFeed.length}
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user