Skip to content

Commit

Permalink
Migrate block editor insert usage to preferences store
Browse files Browse the repository at this point in the history
Update tests

Make updateInsertUsage a proper action that can be unit tested

Fix reusable block tests
  • Loading branch information
talldan committed Oct 13, 2022
1 parent 314b474 commit 6d99419
Show file tree
Hide file tree
Showing 15 changed files with 548 additions and 291 deletions.
8 changes: 8 additions & 0 deletions docs/reference-guides/data/data-core-block-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,14 @@ _Returns_

- `Object`: Action object

### updateInsertUsage

Updates the inserter usage statistics in the preferences store.

_Parameters_

- _blocks_ `Array`: The array of blocks that were inserted.

### updateSettings

Action that updates the block editor settings.
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/block-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@wordpress/keyboard-shortcuts": "file:../keyboard-shortcuts",
"@wordpress/keycodes": "file:../keycodes",
"@wordpress/notices": "file:../notices",
"@wordpress/preferences": "file:../preferences",
"@wordpress/rich-text": "file:../rich-text",
"@wordpress/shortcode": "file:../shortcode",
"@wordpress/style-engine": "file:../style-engine",
Expand Down
51 changes: 50 additions & 1 deletion packages/block-editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import {
hasBlockSupport,
switchToBlockType,
synchronizeBlocksWithTemplate,
store as blocksStore,
} from '@wordpress/blocks';
import { speak } from '@wordpress/a11y';
import { __, _n, sprintf } from '@wordpress/i18n';
import { create, insert, remove, toHTMLString } from '@wordpress/rich-text';
import deprecated from '@wordpress/deprecated';
import { store as preferencesStore } from '@wordpress/preferences';

/**
* Internal dependencies
Expand Down Expand Up @@ -406,6 +408,7 @@ export const replaceBlocks =
return;
}
}
dispatch.updateInsertUsage( blocks );
dispatch( {
type: 'REPLACE_BLOCKS',
clientIds,
Expand Down Expand Up @@ -558,6 +561,52 @@ export function insertBlock(
);
}

/**
* Updates the inserter usage statistics in the preferences store.
*
* @param {Array} blocks The array of blocks that were inserted.
*/
export const updateInsertUsage =
( blocks ) =>
( { registry } ) => {
const previousInsertUsage =
registry.select( preferencesStore ).get( 'core', 'insertUsage' ) ??
{};

const time = Date.now();

const updatedInsertUsage = blocks.reduce( ( previousState, block ) => {
const { attributes, name: blockName } = block;
const match = registry
.select( blocksStore )
.getActiveBlockVariation( blockName, attributes );

// If a block variation match is found change the name to be the same with the
// one that is used for block variations in the Inserter (`getItemFromVariation`).
let id = match?.name ? `${ blockName }/${ match.name }` : blockName;
const _insert = { name: id };
if ( blockName === 'core/block' ) {
_insert.ref = attributes.ref;
id += '/' + attributes.ref;
}

const previousCount = previousState?.[ id ]?.count ?? 0;

return {
...previousState,
[ id ]: {
time,
count: previousCount + 1,
insert: _insert,
},
};
}, previousInsertUsage );

registry
.dispatch( preferencesStore )
.set( 'core', 'insertUsage', updatedInsertUsage );
};

/* eslint-disable jsdoc/valid-types */
/**
* Action that inserts an array of blocks, optionally at a specific index respective a root block list.
Expand Down Expand Up @@ -608,6 +657,7 @@ export const insertBlocks =
}
}
if ( allowedBlocks.length ) {
dispatch.updateInsertUsage( blocks );
dispatch( {
type: 'INSERT_BLOCKS',
blocks: allowedBlocks,
Expand Down Expand Up @@ -1238,7 +1288,6 @@ export function replaceInnerBlocks(
blocks,
updateSelection,
initialPosition: updateSelection ? initialPosition : null,
time: Date.now(),
};
}

Expand Down
4 changes: 0 additions & 4 deletions packages/block-editor/src/store/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
*/
import { __, _x } from '@wordpress/i18n';

export const PREFERENCES_DEFAULTS = {
insertUsage: {},
};

/**
* The default editor settings
*
Expand Down
7 changes: 2 additions & 5 deletions packages/block-editor/src/store/defaults.native.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
/**
* Internal dependencies
*/
import {
PREFERENCES_DEFAULTS,
SETTINGS_DEFAULTS as SETTINGS,
} from './defaults.js';
import { SETTINGS_DEFAULTS as SETTINGS } from './defaults.js';

const SETTINGS_DEFAULTS = {
...SETTINGS,
Expand All @@ -23,4 +20,4 @@ const SETTINGS_DEFAULTS = {
},
};

export { PREFERENCES_DEFAULTS, SETTINGS_DEFAULTS };
export { SETTINGS_DEFAULTS };
9 changes: 2 additions & 7 deletions packages/block-editor/src/store/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
import { createReduxStore, registerStore } from '@wordpress/data';
import { createReduxStore, register } from '@wordpress/data';

/**
* Internal dependencies
Expand Down Expand Up @@ -29,11 +29,6 @@ export const storeConfig = {
*/
export const store = createReduxStore( STORE_NAME, {
...storeConfig,
persist: [ 'preferences' ],
} );

// Ideally we'd use register instead of register stores.
registerStore( STORE_NAME, {
...storeConfig,
persist: [ 'preferences' ],
} );
register( store );
55 changes: 3 additions & 52 deletions packages/block-editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import {
* WordPress dependencies
*/
import { pipe } from '@wordpress/compose';
import { combineReducers, select } from '@wordpress/data';
import { store as blocksStore } from '@wordpress/blocks';
import { combineReducers } from '@wordpress/data';

/**
* Internal dependencies
*/
import { PREFERENCES_DEFAULTS, SETTINGS_DEFAULTS } from './defaults';
import { SETTINGS_DEFAULTS } from './defaults';
import { insertAt, moveTo } from './array';

const identity = ( x ) => x;
Expand Down Expand Up @@ -1526,54 +1526,6 @@ export function settings( state = SETTINGS_DEFAULTS, action ) {
return state;
}

/**
* Reducer returning the user preferences.
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*
* @return {string} Updated state.
*/
export function preferences( state = PREFERENCES_DEFAULTS, action ) {
switch ( action.type ) {
case 'INSERT_BLOCKS':
case 'REPLACE_BLOCKS':
return action.blocks.reduce( ( prevState, block ) => {
const { attributes, name: blockName } = block;
const match = select( blocksStore ).getActiveBlockVariation(
blockName,
attributes
);
// If a block variation match is found change the name to be the same with the
// one that is used for block variations in the Inserter (`getItemFromVariation`).
let id = match?.name
? `${ blockName }/${ match.name }`
: blockName;
const insert = { name: id };
if ( blockName === 'core/block' ) {
insert.ref = attributes.ref;
id += '/' + attributes.ref;
}

return {
...prevState,
insertUsage: {
...prevState.insertUsage,
[ id ]: {
time: action.time,
count: prevState.insertUsage[ id ]
? prevState.insertUsage[ id ].count + 1
: 1,
insert,
},
},
};
}, state );
}

return state;
}

/**
* Reducer returning an object where each key is a block client ID, its value
* representing the settings for its nested blocks.
Expand Down Expand Up @@ -1812,7 +1764,6 @@ export default combineReducers( {
insertionPoint,
template,
settings,
preferences,
lastBlockAttributesChange,
editorMode,
hasBlockMovingClientId,
Expand Down
46 changes: 38 additions & 8 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
parse,
switchToBlockType,
} from '@wordpress/blocks';
import { createRegistrySelector } from '@wordpress/data';
import { Platform } from '@wordpress/element';
import { applyFilters } from '@wordpress/hooks';
import { symbol } from '@wordpress/icons';
Expand All @@ -27,6 +28,7 @@ import deprecated from '@wordpress/deprecated';
* Internal dependencies
*/
import { mapRichTextSettings } from './utils';
import { store as preferencesStore } from '@wordpress/preferences';

/**
* A block selection object.
Expand Down Expand Up @@ -54,6 +56,7 @@ const MILLISECONDS_PER_WEEK = 7 * 24 * 3600 * 1000;
* @type {Array}
*/
const EMPTY_ARRAY = [];
const EMPTY_OBJECT = {};

/**
* Returns a block's name given its client ID, or null if no block exists with
Expand Down Expand Up @@ -1736,19 +1739,43 @@ export function canLockBlockType( state, nameOrType ) {
return !! state.settings?.canLockBlocks;
}

/**
* Return all insert usage stats.
*
* This is only exported since registry selectors need to be exported. It's marked
* as unstable so that it's not considered part of the public API.
*
* @return {Object<string,Object>} An object with an `id` key representing the type
* of block and an object value that contains
* block insertion statistics.
*/
export const __unstableGetInsertUsage = createRegistrySelector(
( select ) => () =>
select( preferencesStore ).get( 'core', 'insertUsage' ) ?? EMPTY_OBJECT
);

/**
* Returns information about how recently and frequently a block has been inserted.
*
* This is only exported since registry selectors need to be exported. It's marked
* as unstable so that it's not considered part of the public API.
*
* @param {Object} state Global application state.
* @param {string} id A string which identifies the insert, e.g. 'core/block/12'
*
* @return {?{ time: number, count: number }} An object containing `time` which is when the last
* insert occurred as a UNIX epoch, and `count` which is
* the number of inserts that have occurred.
*/
function getInsertUsage( state, id ) {
return state.preferences.insertUsage?.[ id ] ?? null;
}
export const __unstableGetInsertUsageForBlock = createRegistrySelector(
( select ) => ( state, id ) => {
const insertUsage = select( preferencesStore ).get(
'core',
'insertUsage'
);
return insertUsage?.[ id ] ?? null;
}
);

/**
* Returns whether we can show a block type in the inserter
Expand Down Expand Up @@ -1776,7 +1803,8 @@ const canIncludeBlockTypeInInserter = ( state, blockType, rootClientId ) => {
*/
const getItemFromVariation = ( state, item ) => ( variation ) => {
const variationId = `${ item.id }/${ variation.name }`;
const { time, count = 0 } = getInsertUsage( state, variationId ) || {};
const { time, count = 0 } =
__unstableGetInsertUsageForBlock( state, variationId ) || {};
return {
...item,
id: variationId,
Expand Down Expand Up @@ -1854,7 +1882,8 @@ const buildBlockTypeItem =
);
}

const { time, count = 0 } = getInsertUsage( state, id ) || {};
const { time, count = 0 } =
__unstableGetInsertUsageForBlock( state, id ) || {};
const blockItemBase = {
id,
name: blockType.name,
Expand Down Expand Up @@ -1962,7 +1991,8 @@ export const getInserterItems = createSelector(
}

const id = `core/block/${ reusableBlock.id }`;
const { time, count = 0 } = getInsertUsage( state, id ) || {};
const { time, count = 0 } =
__unstableGetInsertUsageForBlock( state, id ) || {};
const frecency = calculateFrecency( time, count );

return {
Expand Down Expand Up @@ -2029,7 +2059,7 @@ export const getInserterItems = createSelector(
state.blockListSettings[ rootClientId ],
state.blocks.byClientId,
state.blocks.order,
state.preferences.insertUsage,
__unstableGetInsertUsage(),
state.settings.allowedBlockTypes,
state.settings.templateLock,
getReusableBlocks( state ),
Expand Down Expand Up @@ -2112,7 +2142,7 @@ export const getBlockTransformItems = createSelector(
( state, rootClientId ) => [
state.blockListSettings[ rootClientId ],
state.blocks.byClientId,
state.preferences.insertUsage,
__unstableGetInsertUsage(),
state.settings.allowedBlockTypes,
state.settings.templateLock,
getBlockTypes(),
Expand Down
Loading

0 comments on commit 6d99419

Please sign in to comment.