@automattic/jetpack-shared-extension-utils 0.12.5 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +58 -0
- package/index.js +2 -0
- package/package.json +4 -3
- package/src/get-block-icon-from-metadata.native.js +37 -0
- package/src/modules-state/actions.js +96 -0
- package/src/modules-state/controls.js +48 -0
- package/src/modules-state/index.js +17 -0
- package/src/modules-state/reducer.js +18 -0
- package/src/modules-state/resolvers.js +30 -0
- package/src/modules-state/selectors.js +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.13.0] - 2023-11-13
|
|
9
|
+
### Added
|
|
10
|
+
- Added a Redux store for Jetpack modules data. [#33397]
|
|
11
|
+
|
|
12
|
+
## [0.12.6] - 2023-11-08
|
|
13
|
+
### Fixed
|
|
14
|
+
- Mobile: Fix a regression preventing correct block registration on mobile. [#33890]
|
|
15
|
+
|
|
8
16
|
## [0.12.5] - 2023-11-03
|
|
9
17
|
### Changed
|
|
10
18
|
- Update dependencies.
|
|
@@ -276,6 +284,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
276
284
|
### Changed
|
|
277
285
|
- Core: prepare utility for release
|
|
278
286
|
|
|
287
|
+
[0.13.0]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.12.6...0.13.0
|
|
288
|
+
[0.12.6]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.12.5...0.12.6
|
|
279
289
|
[0.12.5]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.12.4...0.12.5
|
|
280
290
|
[0.12.4]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.12.3...0.12.4
|
|
281
291
|
[0.12.3]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.12.2...0.12.3
|
package/README.md
CHANGED
|
@@ -8,6 +8,64 @@ of the Jetpack plugin, so that plugins can share it. To begin with, we moving
|
|
|
8
8
|
the code used by the Publicize editor extension, but the goal is to bring over
|
|
9
9
|
all the shared code.
|
|
10
10
|
|
|
11
|
+
## Fetching modules data from the store
|
|
12
|
+
The package relies on [controls](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-data/#controls)
|
|
13
|
+
and [resolvers](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-data/#resolvers)
|
|
14
|
+
to pull modules data from the API, and put it into the package's Redux store.
|
|
15
|
+
|
|
16
|
+
### Basic Usage
|
|
17
|
+
|
|
18
|
+
In order to have all modules related data synced within different packages, let's use this Redux store as a source of truth, for both, getting and updating the data.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
Use [`withSelect`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-data/#withselect), `withDispatch` higher-order component to pull the data or [`useSelect`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-data/#useselect) hook to pull the data from the store to pull directly in component. Example:
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
```jsx
|
|
25
|
+
// Imports.
|
|
26
|
+
import { withSelect, withDispatch } from '@wordpress/data';
|
|
27
|
+
import { JETPACK_MODULES_STORE_ID } from '@automattic/jetpack-shared-extension-utils';
|
|
28
|
+
|
|
29
|
+
const SampleComponent = props => {
|
|
30
|
+
const { isModuleActive, isLoadingModules, isChangingStatus, updateJetpackModuleStatus } = props;
|
|
31
|
+
|
|
32
|
+
if ( isModuleActive ) {
|
|
33
|
+
return <div>Module is active</div>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if ( isLoadingModules ) {
|
|
37
|
+
return <div>Loading modules...</div>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if ( !isModuleActive ) {
|
|
41
|
+
return <button onClick={ () => updateJetpackModuleStatus( 'contact-form', true ) }>
|
|
42
|
+
Activate module
|
|
43
|
+
</button>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return <div>Active contact form module</div>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// We wrap `SampleComponent` into the composition of `withSelect` and `withDispatch` HOCs,
|
|
50
|
+
// which will pull the data from the store and pass as a parameter into the component.
|
|
51
|
+
// Jetpack modules will be pulled after first selection `isModuleActive`.
|
|
52
|
+
export default compose( [
|
|
53
|
+
withSelect( ( select, props ) => {
|
|
54
|
+
const { isModuleActive, areModulesLoading, areModulesUpdating } = select( 'jetpack-modules' );
|
|
55
|
+
return {
|
|
56
|
+
isModuleActive: isModuleActive( 'contact-form' ),
|
|
57
|
+
isLoadingModules: areModulesLoading(),
|
|
58
|
+
isChangingStatus: areModulesUpdating(),
|
|
59
|
+
};
|
|
60
|
+
} ),
|
|
61
|
+
withDispatch( dispatch => {
|
|
62
|
+
const { updateJetpackModuleStatus } = dispatch( 'jetpack-modules' );
|
|
63
|
+
return { updateJetpackModuleStatus };
|
|
64
|
+
} ),
|
|
65
|
+
] )( ( SampleComponent ) );
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
|
|
11
69
|
## How to install shared-extension-utils
|
|
12
70
|
|
|
13
71
|
### Installation From Git Repo
|
package/index.js
CHANGED
|
@@ -18,3 +18,5 @@ export { default as useAnalytics } from './src/hooks/use-analytics';
|
|
|
18
18
|
export { default as useModuleStatus } from './src/hooks/use-module-status';
|
|
19
19
|
export { default as JetpackEditorPanelLogo } from './src/components/jetpack-editor-panel-logo';
|
|
20
20
|
export { getBlockIconComponent, getBlockIconProp } from './src/get-block-icon-from-metadata';
|
|
21
|
+
|
|
22
|
+
export * from './src/modules-state';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/jetpack-shared-extension-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Utility functions used by the block editor extensions",
|
|
5
5
|
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/shared-extension-utils/#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@automattic/jetpack-analytics": "^0.1.27",
|
|
21
|
-
"@automattic/jetpack-components": "^0.
|
|
22
|
-
"@automattic/jetpack-connection": "^0.30.
|
|
21
|
+
"@automattic/jetpack-components": "^0.45.0",
|
|
22
|
+
"@automattic/jetpack-connection": "^0.30.6",
|
|
23
23
|
"@wordpress/api-fetch": "6.41.0",
|
|
24
24
|
"@wordpress/compose": "6.21.0",
|
|
25
|
+
"@wordpress/data": "9.13.0",
|
|
25
26
|
"@wordpress/element": "5.21.0",
|
|
26
27
|
"@wordpress/i18n": "4.44.0",
|
|
27
28
|
"@wordpress/plugins": "6.12.0",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* eslint-disable jsdoc/no-undefined-types */
|
|
2
|
+
import { SvgXml } from '@wordpress/primitives';
|
|
3
|
+
import getIconColor from './get-icon-color';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate an icon as a React component from the SVG markup defined in a block.json metadata file.
|
|
7
|
+
* This prevents us from duplicating the markup in various places.
|
|
8
|
+
*
|
|
9
|
+
* Note: using an `img` tag and passing the SVG markup as a data URI doesn't allow us to
|
|
10
|
+
* dynamically set the icon color later on.
|
|
11
|
+
*
|
|
12
|
+
* @param {object} metadata - Block.json content
|
|
13
|
+
* @returns {JSX.Element|string} Icon component
|
|
14
|
+
*/
|
|
15
|
+
export function getBlockIconComponent( metadata ) {
|
|
16
|
+
// If the SVG has been passed as a string, use SvgXml to correctly parse it.
|
|
17
|
+
if ( typeof metadata.icon === 'string' && metadata.icon.startsWith( '<svg' ) ) {
|
|
18
|
+
return <SvgXml xml={ metadata.icon } />;
|
|
19
|
+
}
|
|
20
|
+
return metadata.icon || '';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A block icon needs to be redefined on the front end as a React component, since a string - even
|
|
25
|
+
* SVG markup - is interpreted as a dashicon. This function returns the object that must be passed
|
|
26
|
+
* to the `icon` attribute when registering the block in the front end. It also sets the color
|
|
27
|
+
* of the icon.
|
|
28
|
+
*
|
|
29
|
+
* @param {object} metadata - Block.json content
|
|
30
|
+
* @returns {object} Icon property for client registration
|
|
31
|
+
*/
|
|
32
|
+
export function getBlockIconProp( metadata ) {
|
|
33
|
+
return {
|
|
34
|
+
src: getBlockIconComponent( metadata ),
|
|
35
|
+
foreground: getIconColor(),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { select } from '@wordpress/data';
|
|
2
|
+
import { isSimpleSite } from '../site-type-utils';
|
|
3
|
+
import {
|
|
4
|
+
fetchJetpackModules,
|
|
5
|
+
updateJetpackModuleStatus as updateJetpackModuleStatusControl,
|
|
6
|
+
} from './controls';
|
|
7
|
+
import { JETPACK_MODULES_STORE_ID } from '.';
|
|
8
|
+
|
|
9
|
+
export const SET_JETPACK_MODULES = 'SET_JETPACK_MODULES';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Yield actions to update module status
|
|
13
|
+
*
|
|
14
|
+
* @param {object} settings - Jetpack module settings.
|
|
15
|
+
* @param {string} settings.name - Jetpack module name.
|
|
16
|
+
* @param {boolean} settings.active - If the module is active or not.
|
|
17
|
+
* @yields {object} - an action object.
|
|
18
|
+
* @returns {object} - an action object.
|
|
19
|
+
*/
|
|
20
|
+
export function* updateJetpackModuleStatus( settings ) {
|
|
21
|
+
try {
|
|
22
|
+
const originalData = select( JETPACK_MODULES_STORE_ID ).getJetpackModules();
|
|
23
|
+
yield setIsUpdating( true );
|
|
24
|
+
if ( originalData.data?.[ settings.name ]?.activated !== settings.active ) {
|
|
25
|
+
yield setJetpackModules( originalData );
|
|
26
|
+
}
|
|
27
|
+
yield updateJetpackModuleStatusControl( settings );
|
|
28
|
+
const data = yield fetchJetpackModules();
|
|
29
|
+
yield setJetpackModules( { data } );
|
|
30
|
+
return true;
|
|
31
|
+
} catch ( e ) {
|
|
32
|
+
const oldSettings = select( JETPACK_MODULES_STORE_ID ).getJetpackModules();
|
|
33
|
+
yield setJetpackModules( oldSettings );
|
|
34
|
+
return false;
|
|
35
|
+
} finally {
|
|
36
|
+
yield setIsUpdating( false );
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Yield actions to update module status
|
|
42
|
+
* @yields {object} - an action object.
|
|
43
|
+
* @returns {boolean} - if operation is successful or not.
|
|
44
|
+
*/
|
|
45
|
+
export function* fetchModules() {
|
|
46
|
+
// We don't fetch modules for Simple Site and aknowledge that all modules are active
|
|
47
|
+
if ( isSimpleSite() ) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
yield setIsLoading( true );
|
|
52
|
+
const data = yield fetchJetpackModules();
|
|
53
|
+
yield setJetpackModules( { data } );
|
|
54
|
+
return true;
|
|
55
|
+
} catch ( e ) {
|
|
56
|
+
const oldSettings = select( JETPACK_MODULES_STORE_ID ).getJetpackModules();
|
|
57
|
+
yield setJetpackModules( oldSettings );
|
|
58
|
+
return false;
|
|
59
|
+
} finally {
|
|
60
|
+
yield setIsLoading( false );
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Set modules as loading action
|
|
66
|
+
*
|
|
67
|
+
* @param {boolean} isLoading - If the modules are loading or not.
|
|
68
|
+
* @returns {object} - an action object.
|
|
69
|
+
*/
|
|
70
|
+
function setIsLoading( isLoading ) {
|
|
71
|
+
return setJetpackModules( { isLoading } );
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Set modules as updating action
|
|
76
|
+
*
|
|
77
|
+
* @param {boolean} isUpdating - If the modules are updating or not.
|
|
78
|
+
* @returns {object} - an action object.
|
|
79
|
+
*/
|
|
80
|
+
function setIsUpdating( isUpdating ) {
|
|
81
|
+
return setJetpackModules( { isUpdating } );
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Set Jetpack module action
|
|
86
|
+
*
|
|
87
|
+
* @param {object} options - Jetpack settings.
|
|
88
|
+
* @param {object} options.modules - Jetpack modules.
|
|
89
|
+
* @param {boolean} options.isLoading - If the modules are loading or not.
|
|
90
|
+
* @returns {object} - an action object.
|
|
91
|
+
*/
|
|
92
|
+
export function setJetpackModules( options ) {
|
|
93
|
+
return { type: SET_JETPACK_MODULES, options };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default { updateJetpackModuleStatus, setJetpackModules, fetchModules };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import apiFetch from '@wordpress/api-fetch';
|
|
2
|
+
|
|
3
|
+
export const FETCH_JETPACK_MODULES = 'FETCH_JETPACK_MODULES';
|
|
4
|
+
export const UPDATE_JETPACK_MODULE_STATUS = 'UPDATE_JETPACK_MODULE_STATUS';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* fetchJetpackModules action
|
|
8
|
+
*
|
|
9
|
+
* @returns {object} - an action object.
|
|
10
|
+
*/
|
|
11
|
+
export const fetchJetpackModules = () => {
|
|
12
|
+
return {
|
|
13
|
+
type: FETCH_JETPACK_MODULES,
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Updating single module status action
|
|
19
|
+
*
|
|
20
|
+
* @param settings - Jetpack module settings.
|
|
21
|
+
* @param {string} settings.name - Jetpack module name.
|
|
22
|
+
* @param {boolean} settings.active - If the module is active or not.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
export const updateJetpackModuleStatus = settings => {
|
|
26
|
+
return {
|
|
27
|
+
type: UPDATE_JETPACK_MODULE_STATUS,
|
|
28
|
+
settings,
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default {
|
|
33
|
+
[ FETCH_JETPACK_MODULES ]: function () {
|
|
34
|
+
return apiFetch( {
|
|
35
|
+
path: `/jetpack/v4/module/all`,
|
|
36
|
+
method: 'GET',
|
|
37
|
+
} );
|
|
38
|
+
},
|
|
39
|
+
[ UPDATE_JETPACK_MODULE_STATUS ]: function ( { settings } ) {
|
|
40
|
+
return apiFetch( {
|
|
41
|
+
path: `/jetpack/v4/module/${ settings.name }/active`,
|
|
42
|
+
method: 'POST',
|
|
43
|
+
data: {
|
|
44
|
+
active: settings.active,
|
|
45
|
+
},
|
|
46
|
+
} );
|
|
47
|
+
},
|
|
48
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createReduxStore, register } from '@wordpress/data';
|
|
2
|
+
import actions from './actions';
|
|
3
|
+
import controls from './controls';
|
|
4
|
+
import reducer from './reducer';
|
|
5
|
+
import resolvers from './resolvers';
|
|
6
|
+
import selectors from './selectors';
|
|
7
|
+
|
|
8
|
+
export const JETPACK_MODULES_STORE_ID = 'jetpack-modules';
|
|
9
|
+
|
|
10
|
+
const store = createReduxStore( JETPACK_MODULES_STORE_ID, {
|
|
11
|
+
reducer,
|
|
12
|
+
actions,
|
|
13
|
+
controls,
|
|
14
|
+
resolvers,
|
|
15
|
+
selectors,
|
|
16
|
+
} );
|
|
17
|
+
register( store );
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const defaultState = {
|
|
2
|
+
isLoading: false,
|
|
3
|
+
isUpdating: false,
|
|
4
|
+
data: {},
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const setModulesData = ( state = defaultState, action ) => {
|
|
8
|
+
switch ( action.type ) {
|
|
9
|
+
case 'SET_JETPACK_MODULES':
|
|
10
|
+
return {
|
|
11
|
+
...state,
|
|
12
|
+
...action.options,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return state;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default setModulesData;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { setJetpackModules, fetchModules } from './actions';
|
|
2
|
+
import { fetchJetpackModules } from './controls';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Yield actions to get the Jetpack modules.
|
|
6
|
+
*
|
|
7
|
+
* @yields {object} - an action object.
|
|
8
|
+
* @returns {object} - an action object.
|
|
9
|
+
*/
|
|
10
|
+
export function* getJetpackModules() {
|
|
11
|
+
try {
|
|
12
|
+
const data = yield fetchJetpackModules();
|
|
13
|
+
if ( data ) {
|
|
14
|
+
return setJetpackModules( { data } );
|
|
15
|
+
}
|
|
16
|
+
} catch ( e ) {
|
|
17
|
+
console.error( e ); // eslint-disable-line no-console
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* When requesting data on particular module
|
|
23
|
+
* we want to make sure to have the latest state
|
|
24
|
+
* @returns {boolean} - if action was completed successfully.
|
|
25
|
+
*/
|
|
26
|
+
export function isModuleActive() {
|
|
27
|
+
return fetchModules();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default { getJetpackModules, isModuleActive };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { isSimpleSite } from '../site-type-utils';
|
|
2
|
+
|
|
3
|
+
const jetpackModulesSelectors = {
|
|
4
|
+
getJetpackModules: state => state.data,
|
|
5
|
+
// We consider simple sites to have all modules active
|
|
6
|
+
// TODO: we would remove this when wrapping logic with hooks
|
|
7
|
+
isModuleActive: ( state, moduleName ) =>
|
|
8
|
+
isSimpleSite() || ( state?.data?.[ moduleName ]?.activated ?? false ),
|
|
9
|
+
areModulesLoading: state => state.isLoading ?? false,
|
|
10
|
+
areModulesUpdating: state => state.isUpdating ?? false,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default jetpackModulesSelectors;
|