Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { RangeControl, Spinner } from '@wordpress/components';
import { RangeControl, Spinner, Button } from '@wordpress/components';
import { store as coreStore } from '@wordpress/core-data';
import { __ } from '@wordpress/i18n';
import { __, sprintf } from '@wordpress/i18n';
import { dateI18n, getSettings as getDateSettings } from '@wordpress/date';
import { useMemo } from '@wordpress/element';
import { chevronLeft, chevronRight } from '@wordpress/icons';
import { Stack } from '@wordpress/ui';

/**
* Internal dependencies
Expand All @@ -14,64 +17,61 @@ import { store as editorStore } from '../../store';
import { unlock } from '../../lock-unlock';

/**
* Slider component for navigating revisions.
* Slider component for navigating revisions with pagination.
*
* @return {React.JSX.Element} The revisions slider component.
*/
function RevisionsSlider() {
const { revisions, isLoading, currentRevisionId, revisionKey } = useSelect(
( select ) => {
const { getCurrentPostId, getCurrentPostType } =
select( editorStore );
const { getRevisions, isResolving, getEntityConfig } =
select( coreStore );

const postId = getCurrentPostId();
const postType = getCurrentPostType();

if ( ! postId || ! postType ) {
return {};
}

const entityConfig = getEntityConfig( 'postType', postType );
const _revisionKey = entityConfig?.revisionKey || 'id';
const query = {
per_page: -1,
context: 'edit',
orderby: 'date',
order: 'asc',
_fields: [
...new Set( [
'id',
'date',
'modified',
'author',
'meta',
'title.raw',
'excerpt.raw',
'content.raw',
_revisionKey,
] ),
].join(),
};
return {
revisions: getRevisions( 'postType', postType, postId, query ),
isLoading: isResolving( 'getRevisions', [
'postType',
postType,
postId,
query,
] ),
currentRevisionId: unlock(
select( editorStore )
).getCurrentRevisionId(),
revisionKey: _revisionKey,
};
},
[]
const {
revisions: rawRevisions,
perPage,
currentRevisionId,
revisionKey,
revisionPage,
totalRevisions,
} = useSelect( ( select ) => {
const {
getCurrentRevisionId,
getRevisionPage,
getPageRevisions,
getRevisionsPerPage,
} = unlock( select( editorStore ) );

const postType = select( editorStore ).getCurrentPostType();
if ( ! postType ) {
return {};
}

const entityConfig = select( coreStore ).getEntityConfig(
'postType',
postType
);
const _revisionKey = entityConfig?.revisionKey || 'id';
const _revisionPage = getRevisionPage();

return {
revisions: getPageRevisions( _revisionPage ),
perPage: getRevisionsPerPage(),
currentRevisionId: getCurrentRevisionId(),
revisionKey: _revisionKey,
revisionPage: _revisionPage,
totalRevisions:
select( editorStore ).getCurrentPostRevisionsCount(),
};
}, [] );

const { setCurrentRevisionId, setRevisionPage } = unlock(
useDispatch( editorStore )
);

const { setCurrentRevisionId } = unlock( useDispatch( editorStore ) );
const isLoading = ! rawRevisions;
const totalPages = Math.ceil( totalRevisions / perPage ) || 1;

// Reverse desc→asc so the slider reads oldest (left) → newest (right).
const revisions = useMemo(
() => rawRevisions && [ ...rawRevisions ].reverse(),
[ rawRevisions ]
);

const selectedIndex = revisions?.findIndex(
( r ) => r[ revisionKey ] === currentRevisionId
Expand All @@ -94,41 +94,100 @@ function RevisionsSlider() {
return dateI18n( dateSettings.formats.datetime, revision.date );
};

if ( isLoading ) {
const showPagination = totalPages > 1;

if ( isLoading && ! showPagination ) {
return <Spinner />;
}

if ( ! revisions?.length ) {
if ( ! isLoading && ! revisions?.length ) {
return (
<span className="editor-revisions-header__no-revisions">
{ __( 'No revisions found.' ) }
</span>
);
}

if ( revisions?.length === 1 ) {
if ( totalRevisions <= 1 ) {
return (
<span className="editor-revisions-header__no-revisions">
{ __( 'Only one revision found.' ) }
</span>
);
}

const getPageRangeLabel = ( page ) => {
const end = totalRevisions - ( page - 1 ) * perPage;
const start = Math.max( 1, end - perPage + 1 );
return sprintf(
/* translators: 1: first revision number, 2: last revision number */
__( 'Revisions %1$s–%2$s' ),
start,
end
);
};

const sliderOrSpinner =
isLoading || selectedIndex === -1 ? (
<Spinner />
) : (
<RangeControl
__next40pxDefaultSize
aria-valuetext={ renderTooltipContent( selectedIndex ) }
className="editor-revisions-header__slider"
hideLabelFromVision
label={ __( 'Revision' ) }
max={ revisions?.length - 1 }
min={ 0 }
marks
onChange={ handleSliderChange }
renderTooltipContent={ renderTooltipContent }
value={ selectedIndex }
withInputField={ false }
/>
);

if ( ! showPagination ) {
return sliderOrSpinner;
}

return (
<RangeControl
__next40pxDefaultSize
aria-valuetext={ renderTooltipContent( selectedIndex ) }
className="editor-revisions-header__slider"
hideLabelFromVision
label={ __( 'Revision' ) }
max={ revisions?.length - 1 }
min={ 0 }
marks
onChange={ handleSliderChange }
renderTooltipContent={ renderTooltipContent }
value={ selectedIndex }
withInputField={ false }
/>
<Stack direction="row" gap="sm" align="center" style={ { flex: 1 } }>
<Button
icon={ chevronLeft }
label={
revisionPage < totalPages
? getPageRangeLabel( revisionPage + 1 )
: __( 'No older revisions' )
}
onClick={ () => setRevisionPage( revisionPage + 1 ) }
disabled={ isLoading || revisionPage >= totalPages }
size="compact"
accessibleWhenDisabled
/>
<div
style={ {
flex: 1,
minWidth: 0,
display: 'flex',
justifyContent: 'center',
} }
>
{ sliderOrSpinner }
</div>
<Button
icon={ chevronRight }
label={
revisionPage > 1
? getPageRangeLabel( revisionPage - 1 )
: __( 'No newer revisions' )
}
onClick={ () => setRevisionPage( revisionPage - 1 ) }
disabled={ isLoading || revisionPage <= 1 }
size="compact"
accessibleWhenDisabled
/>
</Stack>
);
}

Expand Down
34 changes: 34 additions & 0 deletions packages/editor/src/store/private-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { dateI18n, getSettings as getDateSettings } from '@wordpress/date';
* Internal dependencies
*/
import isTemplateRevertable from './utils/is-template-revertable';
import { buildRevisionsPageQuery } from './private-selectors';
export * from '../dataviews/store/private-actions';

/**
Expand Down Expand Up @@ -589,6 +590,39 @@ export function setCurrentRevisionId( revisionId ) {
};
}

/**
* Set the current revisions page number and select the newest
* revision on that page once it loads.
*
* @param {number} page The page number.
*/
export const setRevisionPage =
( page ) =>
async ( { dispatch, select, registry } ) => {
const postType = select.getCurrentPostType();
const postId = select.getCurrentPostId();
const entityConfig = registry
.select( coreStore )
.getEntityConfig( 'postType', postType );
const revisionKey = entityConfig?.revisionKey || 'id';

const revisions = await registry
.resolveSelect( coreStore )
.getRevisions(
'postType',
postType,
postId,
buildRevisionsPageQuery( revisionKey, page )
);

registry.batch( () => {
dispatch( { type: 'SET_REVISION_PAGE', page } );
if ( revisions?.length ) {
dispatch.setCurrentRevisionId( revisions[ 0 ][ revisionKey ] );
}
} );
};

/**
* Set whether the revision diff highlighting is shown.
*
Expand Down
Loading
Loading