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

Navigation screen: Atomic save using customizer API endpoint #22603

Merged
merged 50 commits into from
May 29, 2020

Conversation

adamziel
Copy link
Contributor

@adamziel adamziel commented May 25, 2020

Description

This PR enables saving the entire navigation using a single HTTP request.

The current behavior is sending one request per change at the moment. If you e.g. add 20 menu items, update 17 and remove 10, it will result in 47 http requests. This is pretty fragile and would easily break under a number of scenarios - validation errors, network failures, accidentally closing the browser, and so on.

This PR proposes the following implementation:

  • Whenever a new navigation link block is added, we create a draft menu item post - this is consistent with the way existing menus page works.
  • Save action sends the entire state over to the existing customizer endpoint where it's validated in bulk and then saved.

The previous approach also proposed a new batch endpoint for handling these requests - ultimately we agreed on implementing this on the core level instead of adding a custom endpoint: #22148

How has this been tested?

  1. Enable the navigation experiment in Gutenberg > Experiments
  2. Go to Gutenberg > Navigation (beta)
  3. Add a few menu items, update a few other, delete a few existing ones, then hit save
  4. Confirm everything was saved

Types of changes

New feature (non-breaking change which adds functionality)

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR.

@adamziel adamziel added [Feature] List View Menu item in the top toolbar to select blocks from a list of links. [Block] Navigation Affects the Navigation Block [Feature] Navigation Screen labels May 25, 2020
@adamziel adamziel self-assigned this May 25, 2020
@github-actions
Copy link

github-actions bot commented May 25, 2020

Size Change: -18.3 kB (1%)

Total Size: 1.11 MB

Filename Size Change
build/annotations/index.js 3.62 kB +1 B
build/block-directory/index.js 6.48 kB +4 B (0%)
build/block-editor/index.js 105 kB -140 B (0%)
build/block-editor/style-rtl.css 11.3 kB +307 B (2%)
build/block-editor/style.css 11.3 kB +309 B (2%)
build/block-library/editor-rtl.css 7.61 kB +401 B (5%) 🔍
build/block-library/editor.css 7.61 kB +402 B (5%) 🔍
build/block-library/index.js 119 kB -9 B (0%)
build/block-serialization-default-parser/index.js 1.88 kB -1 B
build/blocks/index.js 48.1 kB +4 B (0%)
build/components/index.js 190 kB +1 B
build/components/style-rtl.css 19.5 kB +2.39 kB (12%) ⚠️
build/components/style.css 19.5 kB +2.4 kB (12%) ⚠️
build/compose/index.js 9.32 kB +1 B
build/core-data/index.js 11.4 kB +3 B (0%)
build/data-controls/index.js 1.29 kB +1 B
build/data/index.js 8.43 kB +4 B (0%)
build/edit-navigation/index.js 7.87 kB +1.25 kB (15%) ⚠️
build/edit-post/index.js 302 kB +73 B (0%)
build/edit-post/style-rtl.css 5.43 kB -6.79 kB (125%) 🏆
build/edit-post/style.css 5.43 kB -6.79 kB (125%) 🏆
build/edit-site/index.js 14.1 kB +109 B (0%)
build/edit-site/style-rtl.css 2.96 kB -2.57 kB (86%) 🏆
build/edit-site/style.css 2.96 kB -2.57 kB (86%) 🏆
build/edit-widgets/index.js 8.05 kB +1 B
build/edit-widgets/style-rtl.css 1.81 kB -2.78 kB (153%) 🏆
build/edit-widgets/style.css 1.81 kB -2.78 kB (153%) 🏆
build/editor/style-rtl.css 4.26 kB -793 B (18%) 👏
build/editor/style.css 4.27 kB -793 B (18%) 👏
build/element/index.js 4.64 kB -2 B (0%)
build/format-library/index.js 7.71 kB +4 B (0%)
build/hooks/index.js 2.13 kB +1 B
build/keyboard-shortcuts/index.js 2.51 kB +2 B (0%)
build/media-utils/index.js 5.29 kB +2 B (0%)
build/notices/index.js 1.79 kB +1 B
build/nux/index.js 3.4 kB +2 B (0%)
build/plugins/index.js 2.56 kB -1 B
build/rich-text/index.js 14.8 kB -4 B (0%)
build/viewport/index.js 1.84 kB -2 B (0%)
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.14 kB 0 B
build/api-fetch/index.js 3.4 kB 0 B
build/autop/index.js 2.83 kB 0 B
build/blob/index.js 620 B 0 B
build/block-directory/style-rtl.css 788 B 0 B
build/block-directory/style.css 788 B 0 B
build/block-library/style-rtl.css 7.68 kB 0 B
build/block-library/style.css 7.68 kB 0 B
build/block-library/theme-rtl.css 684 B 0 B
build/block-library/theme.css 686 B 0 B
build/block-serialization-spec-parser/index.js 3.1 kB 0 B
build/date/index.js 5.47 kB 0 B
build/deprecated/index.js 771 B 0 B
build/dom-ready/index.js 568 B 0 B
build/dom/index.js 3.11 kB 0 B
build/edit-navigation/style-rtl.css 857 B 0 B
build/edit-navigation/style.css 856 B 0 B
build/editor/editor-styles-rtl.css 425 B 0 B
build/editor/editor-styles.css 428 B 0 B
build/editor/index.js 44.6 kB 0 B
build/escape-html/index.js 733 B 0 B
build/format-library/style-rtl.css 502 B 0 B
build/format-library/style.css 502 B 0 B
build/html-entities/index.js 622 B 0 B
build/i18n/index.js 3.56 kB 0 B
build/is-shallow-equal/index.js 711 B 0 B
build/keycodes/index.js 1.94 kB 0 B
build/list-reusable-blocks/index.js 3.12 kB 0 B
build/list-reusable-blocks/style-rtl.css 226 B 0 B
build/list-reusable-blocks/style.css 226 B 0 B
build/nux/style-rtl.css 616 B 0 B
build/nux/style.css 613 B 0 B
build/primitives/index.js 1.5 kB 0 B
build/priority-queue/index.js 789 B 0 B
build/redux-routine/index.js 2.85 kB 0 B
build/server-side-render/index.js 2.68 kB 0 B
build/shortcode/index.js 1.7 kB 0 B
build/token-list/index.js 1.28 kB 0 B
build/url/index.js 4.02 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.17 kB 0 B

compressed-size-action

@adamziel adamziel changed the title [Navigation screen] Atomic save [Navigation screen] Atomic save using customizer API endpoint May 26, 2020
Comment on lines +111 to +121
createErrorNotice( __( 'There was an error.' ), {
type: 'snackbar',
} );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We should implement a more sophisticated error handling in a follow-up PR


export default function useNavigationBlocks( menuId ) {
const [ deletedMenuItemsIds, setDeletedMenuItemsIds ] = useState( [] );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a hack to work around re-appearing menu items. I'd like to get rid of it and only rely on the data layer in a follow-up PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Merging the delete part will make it work.

Comment on lines 69 to 77
promiseQueueRef.current.enqueue( () =>
createDraftMenuItem( clientId ).then(
async ( menuItem ) => {
menuItemsRef.current[ clientId ] = menuItem;
processedBlocksIds.current.splice(
processedBlocksIds.current.indexOf( clientId )
);
}
)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We should handle failures here - let's discuss in a follow-up PR.

createSuccessNotice( __( 'Navigation saved.' ), {
type: 'snackbar',
} );
receiveEntityRecords( 'root', 'menuItem', [], query, true );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This re-requests menu items related to current menu. Ideally we won't need to do it - let's discuss in a follow-up PR.

lib/class-wp-rest-customizer-nonces.php Show resolved Hide resolved
lib/class-wp-rest-customizer-nonces.php Show resolved Hide resolved

export default function useNavigationBlocks( menuId ) {
const [ deletedMenuItemsIds, setDeletedMenuItemsIds ] = useState( [] );
Copy link
Contributor

Choose a reason for hiding this comment

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

Merging the delete part will make it work.

Copy link
Contributor

@draganescu draganescu left a comment

Choose a reason for hiding this comment

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

Reviewed, didn't test yet. Pretty much going on! Will comment more once I also test.

useEffect(
function() {
for ( const clientId of getAllClientIds( debouncedBlocks ) ) {
// Already has a related menu item
Copy link
Contributor

Choose a reason for hiding this comment

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

What does "related" mean here?

);
setDeletedMenuItemsIds( [
...deletedMenuItemsIds,
difference( allMenuItemsIds, savedMenuItemIds ),
Copy link
Contributor

Choose a reason for hiding this comment

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

In 21557 I fixed an issue where we delete all the nested items because of a bad difference.

@draganescu
Copy link
Contributor

With this kind of manual saveBlocks and the manual call to receiveEntityRecords you have to wonder why do we still go through entities?

@adamziel
Copy link
Contributor Author

With this kind of manual saveBlocks and the manual call to receiveEntityRecords you have to wonder why do we still go through entities?

@draganescu while out of scope for this PR, this is a pretty good question! Would you open an issue to discuss that more?

@draganescu
Copy link
Contributor

@adamziel here is how it works for me, can you reproduce this behavior (Firefox)

bulk

I used to have this problem when I attempted to save a state version of menu items in my deleteEntity explorations.

@adamziel
Copy link
Contributor Author

adamziel commented May 28, 2020

@draganescu ah thank you for reporting. I just took another pass on it and turns out select() is affected by a bug that causes it to return duplicate results after deleting some items. I added a hacky workaround using filter() and uniq() to this PR but I'd really like to address the root cause.

@adamziel
Copy link
Contributor Author

A stab at fixing the underlying data layer issue is available here: #22707

Alternatively, we could leverage the logic from #21557 to first remove the deleted IDs from the query state and only then call receiveEntityRecords.

@adamziel adamziel force-pushed the add/navigation-atomic-save-customizer-endpoint branch from fa7be78 to 1ff5329 Compare May 28, 2020 14:29
@adamziel adamziel force-pushed the add/navigation-atomic-save-customizer-endpoint branch from 1ff5329 to b6315f4 Compare May 28, 2020 18:14
@adamziel
Copy link
Contributor Author

The data layer PR is now merged and I removed the workarounds in this one - it looks good in my testing.

Copy link
Contributor

@draganescu draganescu left a comment

Choose a reason for hiding this comment

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

In my testing this works as expected now. It is considered a stop gap solution to both the entities in Gutenberg and the REST API not supporting batch and bulk operations applied to multiple entities.

@draganescu draganescu merged commit d3ad37b into master May 29, 2020
@draganescu draganescu deleted the add/navigation-atomic-save-customizer-endpoint branch May 29, 2020 12:32
@github-actions github-actions bot added this to the Gutenberg 8.3 milestone May 29, 2020
@oandregal oandregal changed the title [Navigation screen] Atomic save using customizer API endpoint Navigation screen: Atomic save using customizer API endpoint Jun 8, 2020
@ellatrix ellatrix mentioned this pull request Jun 16, 2020
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Navigation Affects the Navigation Block [Feature] List View Menu item in the top toolbar to select blocks from a list of links.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants