Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patterns: add option to set sync status when adding from wp-admin patterns list #52352

Merged
merged 5 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 2 additions & 0 deletions packages/edit-post/src/components/layout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
EditorNotices,
EditorKeyboardShortcutsRegister,
EditorSnackbars,
PostSyncStatusModal,
store as editorStore,
} from '@wordpress/editor';
import { useSelect, useDispatch } from '@wordpress/data';
Expand Down Expand Up @@ -291,6 +292,7 @@ function Layout( { styles } ) {
<EditPostPreferencesModal />
<KeyboardShortcutHelpModal />
<WelcomeGuide />
<PostSyncStatusModal />
<StartPageOptions />
<Popover.Slot />
<PluginArea onError={ onPluginAreaError } />
Expand Down
5 changes: 4 additions & 1 deletion packages/editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ export { default as PostSlugCheck } from './post-slug/check';
export { default as PostSticky } from './post-sticky';
export { default as PostStickyCheck } from './post-sticky/check';
export { default as PostSwitchToDraftButton } from './post-switch-to-draft-button';
export { default as PostSyncStatus } from './post-sync-status';
export {
default as PostSyncStatus,
PostSyncStatusModal,
} from './post-sync-status';
export { default as PostTaxonomies } from './post-taxonomies';
export { FlatTermSelector as PostTaxonomiesFlatTermSelector } from './post-taxonomies/flat-term-selector';
export { HierarchicalTermSelector as PostTaxonomiesHierarchicalTermSelector } from './post-taxonomies/hierarchical-term-selector';
Expand Down
106 changes: 99 additions & 7 deletions packages/editor/src/components/post-sync-status/index.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,127 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { PanelRow } from '@wordpress/components';
import {
PanelRow,
Modal,
Button,
__experimentalHStack as HStack,
__experimentalVStack as VStack,
ToggleControl,
} from '@wordpress/components';
import { useEffect, useState } from '@wordpress/element';
import { ReusableBlocksRenameHint } from '@wordpress/block-editor';

/**
* Internal dependencies
*/
import { store as editorStore } from '../../store';

export default function PostSyncStatus() {
const { syncStatus, postType } = useSelect( ( select ) => {
const { syncStatus, postType, meta } = useSelect( ( select ) => {
const { getEditedPostAttribute } = select( editorStore );
return {
syncStatus: getEditedPostAttribute( 'wp_pattern_sync_status' ),
meta: getEditedPostAttribute( 'meta' ),
postType: getEditedPostAttribute( 'type' ),
};
}, [] );
} );

if ( postType !== 'wp_block' ) {
return null;
}

const isFullySynced = ! syncStatus;
// When the post is first created, the top level wp_pattern_sync_status is not set so get meta value instead.
const currentSyncStatus =
meta.wp_pattern_sync_status === 'unsynced' ? 'unsynced' : syncStatus;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is meta always an object? Just wondering if we need optional chaining here... feel free to ignore if it is always an object! 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In all my testing the entity always seems to have meta set as an object, even if it doesn't have any postmeta fields configured.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent. Thanks for confirming!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, to be 100% correct after double checking, sometimes it is actually an empty array, in which case it still won't fail with an undefined error as never seems to be undefined so I don't think optional chaining is needed - but let me know if you have any concerns about that

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated to meta?.wp_pattern_sync_status === 'unsynced' ? 'unsynced' : syncStatus

Can't hurt, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Probably good to be on the safe side if a call to a selector doesn't necessarily guarantee that an object is returned. From a quick look up from getEditedPostAttribute, it winds up calling getCurrentPostAttribute, which internally calls getCurrentPost. If for some reason getCurrentPost returns its default empty object, then getCurrentPostAttribute could potentially return undefined around these lines:

default:
const post = getCurrentPost( state );
if ( ! post.hasOwnProperty( attributeName ) ) {
break;

In practice, I doubt that'll ever be hit because (in theory) there'll always be a post object of some kind around, but this hardens the logic a little.

Cheers! 🍻

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice digging! We just might thank ourselves later 😄

ramonjd marked this conversation as resolved.
Show resolved Hide resolved

return (
<PanelRow className="edit-post-sync-status">
<span>{ __( 'Sync status' ) }</span>
<div>
{ isFullySynced ? __( 'Fully synced' ) : __( 'Not synced' ) }
{ currentSyncStatus === 'unsynced'
? __( 'Not synced' )
: __( 'Fully synced' ) }
</div>
</PanelRow>
);
}

export function PostSyncStatusModal() {
const { editPost } = useDispatch( editorStore );
const [ isModalOpen, setIsModalOpen ] = useState( false );
const [ syncType, setSyncType ] = useState( undefined );

const { postType, isNewPost } = useSelect( ( select ) => {
const { getEditedPostAttribute, isCleanNewPost } =
select( editorStore );
return {
postType: getEditedPostAttribute( 'type' ),
isNewPost: isCleanNewPost(),
};
}, [] );

useEffect( () => {
if ( isNewPost && postType === 'wp_block' ) {
setIsModalOpen( true );
}
// We only want the modal to open when the page is first loaded.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [] );

const setSyncStatus = () => {
editPost( {
meta: {
wp_pattern_sync_status: syncType,
},
} );
};

if ( postType !== 'wp_block' || ! isNewPost ) {
return null;
}

return (
<>
{ isModalOpen && (
<Modal
title={ __( 'Set pattern sync status' ) }
onRequestClose={ () => {
setIsModalOpen( false );
} }
overlayClassName="reusable-blocks-menu-items__convert-modal"
>
<form
onSubmit={ ( event ) => {
event.preventDefault();
setIsModalOpen( false );
setSyncStatus();
} }
>
<VStack spacing="5">
<ReusableBlocksRenameHint />
<ToggleControl
label={ __( 'Synced' ) }
help={ __(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change the help text to match the state depending on syncType?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to @SaxonF the toggle control standard is to always/only show the positive state help text

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, makes sense 👍🏻 I guess it can be seen as a checkbox in that regard. Cheers!

'Editing the pattern will update it anywhere it is used.'
) }
checked={ ! syncType }
onChange={ () => {
setSyncType(
! syncType ? 'unsynced' : undefined
);
} }
/>
<HStack justify="right">
<Button variant="primary" type="submit">
{ __( 'Create' ) }
</Button>
</HStack>
</VStack>
</form>
</Modal>
) }
</>
);
}