diff --git a/client/reader/recent/index.tsx b/client/reader/recent/index.tsx
index 53276435b55aa9..64eaa1d453c274 100644
--- a/client/reader/recent/index.tsx
+++ b/client/reader/recent/index.tsx
@@ -13,6 +13,7 @@ import NavigationHeader from 'calypso/components/navigation-header';
import { getPostByKey } from 'calypso/state/reader/posts/selectors';
import { requestPaginatedStream } from 'calypso/state/reader/streams/actions';
import { viewStream } from 'calypso/state/reader-ui/actions';
+import Skeleton from '../components/skeleton';
import EngagementBar from './engagement-bar';
import RecentPostField from './recent-post-field';
import RecentPostSkeleton from './recent-post-skeleton';
@@ -25,6 +26,15 @@ interface RecentProps {
viewToggle?: React.ReactNode;
}
+interface PaddingItem {
+ isPadding: true;
+ postId: string;
+}
+
+function isPaddingItem( item: ReaderPost | PaddingItem ): item is PaddingItem {
+ return 'isPadding' in item;
+}
+
const Recent = ( { viewToggle }: RecentProps ) => {
const dispatch = useDispatch< ThunkDispatch< AppState, void, UnknownAction > >();
const [ selectedItem, setSelectedItem ] = useState< ReaderPost | null >( null );
@@ -65,7 +75,10 @@ const Recent = ( { viewToggle }: RecentProps ) => {
return {};
}
- return items.reduce( ( acc: Record< string, PostItem >, item: ReaderPost ) => {
+ return items.reduce( ( acc: Record< string, PostItem >, item: ReaderPost | PaddingItem ) => {
+ if ( isPaddingItem( item ) ) {
+ return acc;
+ }
const post = getPostByKey( state, {
feedId: item.feedId,
postId: item.postId,
@@ -90,7 +103,10 @@ const Recent = ( { viewToggle }: RecentProps ) => {
{
id: 'icon',
label: translate( 'Icon' ),
- render: ( { item }: { item: ReaderPost } ) => {
+ render: ( { item }: { item: ReaderPost | PaddingItem } ) => {
+ if ( isPaddingItem( item ) ) {
+ return ;
+ }
const post = getPostFromItem( item );
const iconUrl = post?.site_icon?.img || post?.author?.avatar_URL || '';
return iconUrl ? : null;
@@ -101,9 +117,19 @@ const Recent = ( { viewToggle }: RecentProps ) => {
{
id: 'post',
label: translate( 'Post' ),
- getValue: ( { item }: { item: ReaderPost } ) =>
- `${ getPostFromItem( item )?.title ?? '' } - ${ item?.site_name ?? '' }`,
- render: ( { item }: { item: ReaderPost } ) => {
+ getValue: ( { item }: { item: ReaderPost | PaddingItem } ) =>
+ isPaddingItem( item )
+ ? ''
+ : `${ getPostFromItem( item )?.title ?? '' } - ${ item?.site_name ?? '' }`,
+ render: ( { item }: { item: ReaderPost | PaddingItem } ) => {
+ if ( isPaddingItem( item ) ) {
+ return (
+ <>
+
+
+ >
+ );
+ }
return (
handleItemFocus( item.postId?.toString() ) }>
{
const focusedItem = shownData.find(
( item ) => item.postId?.toString() === focusedIndexRef.current
);
- if ( focusedItem ) {
+ if ( focusedItem && ! isPaddingItem( focusedItem ) ) {
setSelectedItem( focusedItem );
setTimeout( () => {
postColumnRef.current?.focus();
diff --git a/client/reader/recent/style.scss b/client/reader/recent/style.scss
index 1cef2a3df7df03..5690fd0f9db397 100644
--- a/client/reader/recent/style.scss
+++ b/client/reader/recent/style.scss
@@ -184,16 +184,6 @@ body.is-reader-full-post {
padding: $recent-feed-spacing;
}
}
-
- // This is a temporary fix to disable the pagination page selector because of this issue: Automattic/loop#247
- // Once that issue is fixed, this code should be removed.
- .dataviews-pagination__page-select {
- display: none;
-
- & + div {
- margin-left: auto;
- }
- }
}
&__post-column {
diff --git a/client/state/data-layer/wpcom/read/streams/index.js b/client/state/data-layer/wpcom/read/streams/index.js
index 040805ed36adc6..0e8ad564a32b6f 100644
--- a/client/state/data-layer/wpcom/read/streams/index.js
+++ b/client/state/data-layer/wpcom/read/streams/index.js
@@ -427,7 +427,7 @@ function get_page_handle( streamType, action, data ) {
export function handlePage( action, data ) {
const { posts, sites, cards } = data;
- const { streamKey, query, isPoll, gap, streamType } = action.payload;
+ const { streamKey, query, isPoll, gap, streamType, page, perPage } = action.payload;
const pageHandle = get_page_handle( streamType, action, data );
const { dateProperty } = streamApis[ streamType ];
@@ -502,6 +502,8 @@ export function handlePage( action, data ) {
gap,
totalItems,
totalPages,
+ page,
+ perPage,
} )
);
}
diff --git a/client/state/reader/streams/actions.js b/client/state/reader/streams/actions.js
index e52e4d22d50ec8..74aa891c768a54 100644
--- a/client/state/reader/streams/actions.js
+++ b/client/state/reader/streams/actions.js
@@ -47,7 +47,16 @@ export function requestPage( {
};
}
-export function receivePage( { streamKey, pageHandle, streamItems, gap, totalItems, totalPages } ) {
+export function receivePage( {
+ streamKey,
+ pageHandle,
+ streamItems,
+ gap,
+ totalItems,
+ totalPages,
+ page,
+ perPage,
+} ) {
return {
type: READER_STREAMS_PAGE_RECEIVE,
payload: {
@@ -57,6 +66,8 @@ export function receivePage( { streamKey, pageHandle, streamItems, gap, totalIte
gap,
totalItems,
totalPages,
+ page,
+ perPage,
},
};
}
diff --git a/client/state/reader/streams/reducer.js b/client/state/reader/streams/reducer.js
index 106bb46f81ff31..57fa9148db3acf 100644
--- a/client/state/reader/streams/reducer.js
+++ b/client/state/reader/streams/reducer.js
@@ -36,16 +36,52 @@ export const items = ( state = [], action ) => {
let gap;
let newState;
let newXPosts;
+ let perPage;
+ let page;
+ let streamKey;
switch ( action.type ) {
case READER_STREAMS_PAGE_RECEIVE:
gap = action.payload.gap;
streamItems = action.payload.streamItems;
+ perPage = action.payload.perPage;
+ page = action.payload.page;
+ streamKey = action.payload.streamKey;
if ( ! Array.isArray( streamItems ) ) {
return state;
}
+ // For the Recent feeds, we need to pad the stream with empty items
+ // for the DataViews pagination to work correctly
+ // see Automattic/loop#238
+ if ( streamKey?.startsWith( 'recent' ) && streamItems.length > 0 && perPage && page > 1 ) {
+ // Calculate where new items should start
+ const startIndex = ( page - 1 ) * perPage;
+ const existingLength = state.length;
+ const paddingNeeded = startIndex - existingLength;
+
+ // Case 1: Need to add padding before new items
+ if ( paddingNeeded > 0 ) {
+ const paddingItems = Array( paddingNeeded )
+ .fill( undefined )
+ .map( ( _, index ) => ( {
+ isPadding: true,
+ postId: `padding-${ index }`,
+ } ) );
+
+ return combineXPosts( [ ...state, ...paddingItems, ...streamItems ] );
+ }
+
+ // Case 2: Replace existing items at correct index
+ const updatedState = [ ...state ];
+ streamItems.forEach( ( item, index ) => {
+ updatedState[ startIndex + index ] = item;
+ } );
+
+ return combineXPosts( updatedState );
+ }
+
if ( gap ) {
const beforeGap = takeWhile( state, ( postKey ) => ! keysAreEqual( postKey, gap ) );
const afterGap = takeRightWhile( state, ( postKey ) => ! keysAreEqual( postKey, gap ) );