/** * 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 ( ); } /** * 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;