/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import {
Button,
Composite,
privateApis as componentsPrivateApis,
} from '@wordpress/components';
import { dateI18n, getDate, humanTimeDiff, getSettings } from '@wordpress/date';
import { store as coreStore } from '@wordpress/core-data';
import { useSelect } from '@wordpress/data';
import { getGlobalStylesChanges } from '@wordpress/global-styles-engine';
import { ENTER, SPACE } from '@wordpress/keycodes';
/**
* Internal dependencies
*/
import type { Revision } from './types';
import { unlock } from '../lock-unlock';
const { Badge: WCBadge } = unlock( componentsPrivateApis );
const DAY_IN_MILLISECONDS = 60 * 60 * 1000 * 24;
interface ChangesSummaryProps {
revision: Revision;
previousRevision?: Revision;
}
function ChangesSummary( { revision, previousRevision }: ChangesSummaryProps ) {
const changes: string[] = getGlobalStylesChanges(
revision,
previousRevision,
{
maxResults: 7,
}
);
if ( ! changes.length ) {
return null;
}
return (
{ changes.map( ( change ) => (
- { change }
) ) }
);
}
/**
* Returns a button label for the revision.
* @param id
* @param authorDisplayName
* @param formattedModifiedDate
* @param areStylesEqual
*/
function getRevisionLabel(
id: string | number,
authorDisplayName: string,
formattedModifiedDate: string,
areStylesEqual: boolean
): string {
if ( 'parent' === id ) {
return __( 'Reset the styles to the theme defaults' );
}
if ( 'unsaved' === id ) {
return sprintf(
/* translators: %s: author display name */
__( 'Unsaved changes by %s' ),
authorDisplayName
);
}
return areStylesEqual
? sprintf(
// translators: 1: author display name. 2: revision creation date.
__(
'Changes saved by %1$s on %2$s. This revision matches current editor styles.'
),
authorDisplayName,
formattedModifiedDate
)
: sprintf(
// translators: 1: author display name. 2: revision creation date.
__( 'Changes saved by %1$s on %2$s' ),
authorDisplayName,
formattedModifiedDate
);
}
interface RevisionButtonsProps {
userRevisions: Revision[];
selectedRevisionId?: string | number;
onChange: ( revision: Revision ) => void;
canApplyRevision?: boolean;
onApplyRevision?: () => void;
}
/**
* Returns a rendered list of revisions buttons.
* @param root0
* @param root0.userRevisions
* @param root0.selectedRevisionId
* @param root0.onChange
* @param root0.canApplyRevision
* @param root0.onApplyRevision
*/
function RevisionsButtons( {
userRevisions,
selectedRevisionId,
onChange,
canApplyRevision,
onApplyRevision,
}: RevisionButtonsProps ) {
const { currentThemeName, currentUser } = useSelect( ( select ) => {
const { getCurrentTheme, getCurrentUser } = select( coreStore );
const currentTheme = getCurrentTheme();
return {
currentThemeName:
currentTheme?.name?.rendered || currentTheme?.stylesheet,
currentUser: getCurrentUser(),
};
}, [] );
const dateNowInMs = getDate( null ).getTime();
const { datetimeAbbreviated } = getSettings().formats;
return (
{ userRevisions.map( ( revision, index ) => {
const { id, author, modified } = revision;
const isUnsaved = 'unsaved' === id;
// Unsaved changes are created by the current user.
const revisionAuthor = isUnsaved ? currentUser : author;
const authorDisplayName = revisionAuthor?.name || __( 'User' );
const authorAvatar = revisionAuthor?.avatar_urls?.[ '48' ];
const isFirstItem = index === 0;
const isSelected = selectedRevisionId
? selectedRevisionId === id
: isFirstItem;
const areStylesEqual = ! canApplyRevision && isSelected;
const isReset = 'parent' === id;
// Convert modified to string if it's a Date, for type compatibility
const modifiedString =
modified instanceof Date
? modified.toISOString()
: modified;
const modifiedDate = getDate( modifiedString ?? null );
const displayDate =
modifiedString &&
dateNowInMs - modifiedDate.getTime() > DAY_IN_MILLISECONDS
? dateI18n( datetimeAbbreviated, modifiedDate )
: humanTimeDiff(
modifiedString ?? modifiedDate,
undefined
);
const revisionLabel = getRevisionLabel(
id,
authorDisplayName,
dateI18n( datetimeAbbreviated, modifiedDate ),
areStylesEqual
);
return (
{
const { keyCode } = event;
if ( keyCode === ENTER || keyCode === SPACE ) {
onChange( revision );
}
} }
onClick={ ( event ) => {
event.preventDefault();
onChange( revision );
} }
aria-selected={ isSelected }
aria-label={ revisionLabel }
render={ }
>
{ isReset ? (
{ __( 'Default styles' ) }
{ currentThemeName }
) : (
{ isUnsaved ? (
{ __( '(Unsaved)' ) }
) : (
) }
{ authorDisplayName }
{ isSelected && (
) }
) }
{ isSelected &&
( areStylesEqual ? (
{ __( 'Active' ) }
) : (
) ) }
);
} ) }
);
}
export default RevisionsButtons;