@automattic/jetpack-shared-extension-utils 0.16.5 → 0.17.1
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 +11 -0
- package/index.js +5 -1
- package/package.json +25 -14
- package/src/block-icons.js +25 -0
- package/src/components/index.js +2 -0
- package/src/components/upgrade-nudge/index.jsx +59 -0
- package/src/components/upgrade-nudge/style.scss +42 -0
- package/src/hooks/use-autosave-and-redirect/README.md +54 -0
- package/src/hooks/use-autosave-and-redirect/index.js +103 -0
- package/src/hooks/use-plan-type/index.ts +27 -0
- package/src/hooks/use-ref-interval.ts +67 -0
- package/src/icons.js +525 -0
- package/src/icons.native.scss +19 -0
- package/src/icons.scss +21 -0
- package/src/is-current-user-connected.js +8 -1
- package/src/libs/connection/index.ts +59 -0
- package/src/libs/index.js +1 -0
- package/src/store/wordpress-com/actions.ts +194 -0
- package/src/store/wordpress-com/constants.ts +31 -0
- package/src/store/wordpress-com/index.ts +108 -0
- package/src/store/wordpress-com/reducer.ts +224 -0
- package/src/store/wordpress-com/types.ts +161 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ 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.17.1] - 2025-02-11
|
|
9
|
+
### Changed
|
|
10
|
+
- Update dependencies. [#40231]
|
|
11
|
+
|
|
12
|
+
## [0.17.0] - 2025-02-05
|
|
13
|
+
### Changed
|
|
14
|
+
- External Media: Move the GooglePhotosMedia, OpenverseMedia, PexelsMedia to @automattic/jetpack-shared-extension-utils [#41078]
|
|
15
|
+
- Updated package dependencies. [#41491] [#41577]
|
|
16
|
+
|
|
8
17
|
## [0.16.5] - 2025-01-27
|
|
9
18
|
### Changed
|
|
10
19
|
- Internal updates.
|
|
@@ -523,6 +532,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
523
532
|
### Changed
|
|
524
533
|
- Core: prepare utility for release
|
|
525
534
|
|
|
535
|
+
[0.17.1]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.17.0...0.17.1
|
|
536
|
+
[0.17.0]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.16.5...0.17.0
|
|
526
537
|
[0.16.5]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.16.4...0.16.5
|
|
527
538
|
[0.16.4]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.16.3...0.16.4
|
|
528
539
|
[0.16.3]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.16.2...0.16.3
|
package/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from './src/block-icons';
|
|
1
2
|
export { default as getJetpackData, JETPACK_DATA_PATH } from './src/get-jetpack-data';
|
|
2
3
|
export { default as getSiteFragment } from './src/get-site-fragment';
|
|
3
4
|
export * from './src/site-type-utils';
|
|
@@ -15,9 +16,12 @@ export {
|
|
|
15
16
|
} from './src/plan-utils';
|
|
16
17
|
export { default as isCurrentUserConnected } from './src/is-current-user-connected';
|
|
17
18
|
export { default as useAnalytics } from './src/hooks/use-analytics';
|
|
19
|
+
export { default as useAutosaveAndRedirect } from './src/hooks/use-autosave-and-redirect';
|
|
20
|
+
export * from './src/hooks/use-plan-type';
|
|
21
|
+
export { default as useRefInterval } from './src/hooks/use-ref-interval';
|
|
18
22
|
export { default as useModuleStatus } from './src/hooks/use-module-status';
|
|
19
|
-
export { default as JetpackEditorPanelLogo } from './src/components/jetpack-editor-panel-logo';
|
|
20
23
|
export { getBlockIconComponent, getBlockIconProp } from './src/get-block-icon-from-metadata';
|
|
21
24
|
export { default as getJetpackBlocksVariation } from './src/get-jetpack-blocks-variation';
|
|
22
25
|
export * from './src/modules-state';
|
|
23
26
|
export { default as isMyJetpackAvailable } from './src/is-my-jetpack-available';
|
|
27
|
+
export * from './src/libs';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/jetpack-shared-extension-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
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,16 +18,22 @@
|
|
|
18
18
|
"test-coverage": "pnpm run test --coverage"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
+
"@automattic/color-studio": "4.0.0",
|
|
21
22
|
"@automattic/jetpack-analytics": "^0.1.35",
|
|
22
|
-
"@automattic/jetpack-
|
|
23
|
-
"@automattic/jetpack-
|
|
24
|
-
"@
|
|
25
|
-
"@wordpress/
|
|
26
|
-
"@wordpress/
|
|
27
|
-
"@wordpress/
|
|
28
|
-
"@wordpress/
|
|
29
|
-
"@wordpress/
|
|
30
|
-
"@wordpress/
|
|
23
|
+
"@automattic/jetpack-base-styles": "^0.6.42",
|
|
24
|
+
"@automattic/jetpack-components": "^0.66.1",
|
|
25
|
+
"@automattic/jetpack-connection": "^0.36.6",
|
|
26
|
+
"@wordpress/api-fetch": "7.17.0",
|
|
27
|
+
"@wordpress/block-editor": "14.12.0",
|
|
28
|
+
"@wordpress/components": "29.3.0",
|
|
29
|
+
"@wordpress/compose": "7.17.0",
|
|
30
|
+
"@wordpress/data": "10.17.0",
|
|
31
|
+
"@wordpress/element": "6.17.0",
|
|
32
|
+
"@wordpress/i18n": "5.17.0",
|
|
33
|
+
"@wordpress/plugins": "7.17.0",
|
|
34
|
+
"@wordpress/url": "4.17.0",
|
|
35
|
+
"clsx": "2.1.1",
|
|
36
|
+
"debug": "4.4.0",
|
|
31
37
|
"lodash": "4.17.21"
|
|
32
38
|
},
|
|
33
39
|
"devDependencies": {
|
|
@@ -35,10 +41,11 @@
|
|
|
35
41
|
"@babel/core": "7.26.0",
|
|
36
42
|
"@babel/plugin-transform-react-jsx": "7.25.9",
|
|
37
43
|
"@babel/preset-react": "7.26.3",
|
|
44
|
+
"@babel/runtime": "7.26.0",
|
|
38
45
|
"@testing-library/dom": "10.4.0",
|
|
39
|
-
"@testing-library/react": "16.0
|
|
40
|
-
"@testing-library/user-event": "14.
|
|
41
|
-
"@wordpress/babel-plugin-import-jsx-pragma": "5.
|
|
46
|
+
"@testing-library/react": "16.2.0",
|
|
47
|
+
"@testing-library/user-event": "14.6.1",
|
|
48
|
+
"@wordpress/babel-plugin-import-jsx-pragma": "5.17.0",
|
|
42
49
|
"babel-jest": "29.3.1",
|
|
43
50
|
"jest": "29.7.0",
|
|
44
51
|
"jest-environment-jsdom": "29.7.0",
|
|
@@ -47,6 +54,10 @@
|
|
|
47
54
|
"react-dom": "18.3.1"
|
|
48
55
|
},
|
|
49
56
|
"exports": {
|
|
50
|
-
".": "./index.js"
|
|
57
|
+
".": "./index.js",
|
|
58
|
+
"./components": "./src/components/index.js",
|
|
59
|
+
"./icons": "./src/icons.js",
|
|
60
|
+
"./store/wordpress-com": "./src/store/wordpress-com/index.ts",
|
|
61
|
+
"./store/wordpress-com/types": "./src/store/wordpress-com/types.ts"
|
|
51
62
|
}
|
|
52
63
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import colorStudio from '@automattic/color-studio';
|
|
2
|
+
import { isAtomicSite, isSimpleSite } from './site-type-utils';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Constants
|
|
6
|
+
*/
|
|
7
|
+
const PALETTE = colorStudio.colors;
|
|
8
|
+
const COLOR_JETPACK = PALETTE[ 'Jetpack Green 40' ];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns the icon color for Jetpack blocks.
|
|
12
|
+
*
|
|
13
|
+
* Green in the Jetpack context, otherwise black for Simple sites or Atomic sites.
|
|
14
|
+
*
|
|
15
|
+
* @return {string} HEX color for block editor icons
|
|
16
|
+
*/
|
|
17
|
+
export function getIconColor() {
|
|
18
|
+
if ( isAtomicSite() || isSimpleSite() ) {
|
|
19
|
+
// Return null to match core block styling
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Jetpack Green
|
|
24
|
+
return COLOR_JETPACK;
|
|
25
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Button } from '@wordpress/components';
|
|
2
|
+
import { __ } from '@wordpress/i18n';
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
import './style.scss';
|
|
7
|
+
|
|
8
|
+
export const Nudge = ( {
|
|
9
|
+
className,
|
|
10
|
+
description,
|
|
11
|
+
align = null,
|
|
12
|
+
title = null,
|
|
13
|
+
buttonText = null,
|
|
14
|
+
visible = true,
|
|
15
|
+
context = null,
|
|
16
|
+
checkoutUrl = null,
|
|
17
|
+
goToCheckoutPage = null,
|
|
18
|
+
isRedirecting = false,
|
|
19
|
+
showButton = true,
|
|
20
|
+
target = '_top',
|
|
21
|
+
} ) => {
|
|
22
|
+
const cssClasses = clsx( className, 'jetpack-upgrade-plan-banner', {
|
|
23
|
+
'wp-block': context === 'editor-canvas',
|
|
24
|
+
'block-editor-block-list__block': context === 'editor-canvas',
|
|
25
|
+
'jetpack-upgrade-plan__hidden': ! visible,
|
|
26
|
+
} );
|
|
27
|
+
|
|
28
|
+
const redirectingText = __( 'Redirecting…', 'jetpack-shared-extension-utils' );
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className={ cssClasses } data-align={ align }>
|
|
32
|
+
<div className="jetpack-upgrade-plan-banner__wrapper">
|
|
33
|
+
{ title && (
|
|
34
|
+
<strong className={ clsx( 'banner-title', { [ `${ className }__title` ]: className } ) }>
|
|
35
|
+
{ title }
|
|
36
|
+
</strong>
|
|
37
|
+
) }
|
|
38
|
+
{ description && (
|
|
39
|
+
<span className={ `${ className }__description banner-description` }>
|
|
40
|
+
{ description }
|
|
41
|
+
</span>
|
|
42
|
+
) }
|
|
43
|
+
{ showButton && (
|
|
44
|
+
<Button
|
|
45
|
+
href={ isRedirecting ? null : checkoutUrl } // Only for server-side rendering, since onClick doesn't work there.
|
|
46
|
+
onClick={ goToCheckoutPage }
|
|
47
|
+
target={ target }
|
|
48
|
+
className={ clsx( 'is-primary', {
|
|
49
|
+
'jetpack-upgrade-plan__hidden': ! checkoutUrl,
|
|
50
|
+
} ) }
|
|
51
|
+
isBusy={ isRedirecting }
|
|
52
|
+
>
|
|
53
|
+
{ isRedirecting ? redirectingText : buttonText }
|
|
54
|
+
</Button>
|
|
55
|
+
) }
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
@import '@automattic/jetpack-base-styles/gutenberg-base-styles';
|
|
2
|
+
@import "@automattic/color-studio/dist/color-variables";
|
|
3
|
+
|
|
4
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper {
|
|
5
|
+
display: flex;
|
|
6
|
+
justify-content: space-between;
|
|
7
|
+
align-items: center;
|
|
8
|
+
font-size: 14px;
|
|
9
|
+
background: $studio-black;
|
|
10
|
+
padding: 0 20px;
|
|
11
|
+
border-radius: 2px;
|
|
12
|
+
box-shadow: 0 0 1px inset $studio-white; }
|
|
13
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .banner-title,
|
|
14
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .banner-description {
|
|
15
|
+
color: $studio-white; }
|
|
16
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .jetpack-upgrade-plan-banner__title,
|
|
17
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .jetpack-upgrade-plan-banner__description {
|
|
18
|
+
margin-right: 10px; }
|
|
19
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .components-button {
|
|
20
|
+
flex-shrink: 0;
|
|
21
|
+
line-height: 1;
|
|
22
|
+
margin-left: auto;
|
|
23
|
+
height: 28px; }
|
|
24
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .components-button.is-primary {
|
|
25
|
+
background: $studio-pink-40;
|
|
26
|
+
color: $studio-white; }
|
|
27
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .components-button.is-primary:hover {
|
|
28
|
+
background: $studio-pink-30; }
|
|
29
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .components-button.is-primary.is-busy {
|
|
30
|
+
background-size: 100px 100%;
|
|
31
|
+
background-image: linear-gradient(-45deg, $studio-pink-40 28%, $studio-pink-60 28%, $studio-pink-60 72%, $studio-pink-40 72%); }
|
|
32
|
+
|
|
33
|
+
.jetpack-upgrade-plan-banner.block-editor-block-list__block {
|
|
34
|
+
margin-top: 0;
|
|
35
|
+
margin-bottom: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.jetpack-upgrade-plan-banner.wp-block[data-align=right] .jetpack-upgrade-plan-banner__wrapper,
|
|
39
|
+
.jetpack-upgrade-plan-banner.wp-block[data-align=left] .jetpack-upgrade-plan-banner__wrapper {
|
|
40
|
+
max-width: 580px;
|
|
41
|
+
width: 100%;
|
|
42
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
## useAutosaveAndRedirect hook
|
|
2
|
+
|
|
3
|
+
Use this hook to implement autosave and redirect functionality that works in both the block and site editor.
|
|
4
|
+
|
|
5
|
+
### Usage
|
|
6
|
+
|
|
7
|
+
```es6
|
|
8
|
+
/**
|
|
9
|
+
* Internal dependencies
|
|
10
|
+
*/
|
|
11
|
+
import useAutosaveAndRedirect from '../../shared/use-autosave-and-redirect/index';
|
|
12
|
+
|
|
13
|
+
const myComponent = ( myUrl ) => {
|
|
14
|
+
const [ autosave, autosaveAndRedirect, isRedirecting ] = useAutosaveAndRedirect( myUrl );
|
|
15
|
+
return (
|
|
16
|
+
<Button href={ myUrl } onClick={ autosaveAndRedirect } isBusy={ isRedirecting }>
|
|
17
|
+
Checkout
|
|
18
|
+
</Button>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### API
|
|
24
|
+
|
|
25
|
+
`const { autosave, autosaveAndRedirect, isRedirecting } = useAutosaveAndRedirect( redirectUrl, onRedirect );`
|
|
26
|
+
|
|
27
|
+
#### Arguments
|
|
28
|
+
|
|
29
|
+
The hook accepts two arguments.
|
|
30
|
+
|
|
31
|
+
- `redirectUrl` (`string`) - URL to redirect to after saving.
|
|
32
|
+
- _(optional)_ `onRedirect` (`(string) => void`) - callback function that will
|
|
33
|
+
be run when the redirect process triggers. The URL is passed.
|
|
34
|
+
|
|
35
|
+
### Return Values
|
|
36
|
+
|
|
37
|
+
The hook returns an array with three items.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
- `autosave` (`(event) => void`): Callback to be used in an onClick event.
|
|
41
|
+
|
|
42
|
+
Checks whether the current post/page/etc has changes to save and saves them. If
|
|
43
|
+
in the site editor, entities are saved. This callback can be used when a redirect
|
|
44
|
+
is not required (for example if an action is performed in a modal).
|
|
45
|
+
|
|
46
|
+
- `autosaveAndRedirect` (`(event) => void`): Callback to be used in an onClick event.
|
|
47
|
+
|
|
48
|
+
Redirects the user to the redirectURL, checking before whether the current
|
|
49
|
+
post/page/etc has changes to save. If so, it saves them before redirecting. If
|
|
50
|
+
in the site editor, entities are saved.
|
|
51
|
+
|
|
52
|
+
- `isRedirecting` (`bool`): If the component is in the process of redirecting the
|
|
53
|
+
user. It may be waiting for a save to complete before redirecting. Use
|
|
54
|
+
this to set a button as busy or in a loading state.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { useSelect, dispatch } from '@wordpress/data';
|
|
2
|
+
import { useState } from '@wordpress/element';
|
|
3
|
+
import { noop } from 'lodash';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* To handle the redirection
|
|
7
|
+
* @param {string} url - The redirect URL.
|
|
8
|
+
* @param {Function} callback - The callback of the redirection.
|
|
9
|
+
* @param {boolean} shouldOpenNewWindow - Whether to open the new window.
|
|
10
|
+
* @return {Window | null} - The open window.
|
|
11
|
+
*/
|
|
12
|
+
function redirect( url, callback, shouldOpenNewWindow = false ) {
|
|
13
|
+
if ( callback ) {
|
|
14
|
+
callback( url );
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return shouldOpenNewWindow ? window.open( url, '_blank' ) : ( window.top.location.href = url );
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Hook to get properties for AiImage
|
|
22
|
+
*
|
|
23
|
+
* @param {string} redirectUrl - The redirect URL.
|
|
24
|
+
* @param {Function} onRedirect - To handle the redirection.
|
|
25
|
+
* @return {object} - Object containing properties to handle autosave and redirect.
|
|
26
|
+
*/
|
|
27
|
+
export default function useAutosaveAndRedirect( redirectUrl, onRedirect = noop ) {
|
|
28
|
+
const [ isRedirecting, setIsRedirecting ] = useState( false );
|
|
29
|
+
|
|
30
|
+
const { isAutosaveablePost, isDirtyPost, currentPost } = useSelect( select => {
|
|
31
|
+
const editorSelector = select( 'core/editor' );
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
isAutosaveablePost: editorSelector.isEditedPostAutosaveable(),
|
|
35
|
+
isDirtyPost: editorSelector.isEditedPostDirty(),
|
|
36
|
+
currentPost: editorSelector.getCurrentPost(),
|
|
37
|
+
};
|
|
38
|
+
}, [] );
|
|
39
|
+
|
|
40
|
+
const isPostEditor = Object.keys( currentPost ).length > 0;
|
|
41
|
+
|
|
42
|
+
const isWidgetEditor = useSelect( select => {
|
|
43
|
+
if ( window.wp?.customize ) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return !! select( 'core/edit-widgets' );
|
|
48
|
+
} );
|
|
49
|
+
|
|
50
|
+
// Alias. Save post by dispatch.
|
|
51
|
+
const savePost = dispatch( 'core/editor' ).savePost;
|
|
52
|
+
|
|
53
|
+
// For the site editor, save entities
|
|
54
|
+
const entityRecords = useSelect( select => {
|
|
55
|
+
return select( 'core' ).__experimentalGetDirtyEntityRecords();
|
|
56
|
+
} );
|
|
57
|
+
|
|
58
|
+
// Save
|
|
59
|
+
const saveEntities = async () => {
|
|
60
|
+
for ( let i = 0; i < entityRecords.length; i++ ) {
|
|
61
|
+
// await is needed here due to the loop.
|
|
62
|
+
await dispatch( 'core' ).saveEditedEntityRecord(
|
|
63
|
+
entityRecords[ i ].kind,
|
|
64
|
+
entityRecords[ i ].name,
|
|
65
|
+
entityRecords[ i ].key
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const autosave = async event => {
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
|
|
73
|
+
if ( isPostEditor ) {
|
|
74
|
+
/**
|
|
75
|
+
* If there are not unsaved values, return.
|
|
76
|
+
* If the post is not auto-savable, return.
|
|
77
|
+
*/
|
|
78
|
+
if ( isDirtyPost && isAutosaveablePost ) {
|
|
79
|
+
await savePost( event );
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
// Save entities in the site editor.
|
|
83
|
+
await saveEntities( event );
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const autosaveAndRedirect = async event => {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
|
|
90
|
+
// Lock re-redirecting attempts.
|
|
91
|
+
if ( isRedirecting ) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
setIsRedirecting( true );
|
|
96
|
+
|
|
97
|
+
autosave( event ).then( () => {
|
|
98
|
+
redirect( redirectUrl, onRedirect, isWidgetEditor );
|
|
99
|
+
} );
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return { autosave, autosaveAndRedirect, isRedirecting };
|
|
103
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const PLAN_TYPE_FREE = 'free';
|
|
2
|
+
export const PLAN_TYPE_TIERED = 'tiered';
|
|
3
|
+
export const PLAN_TYPE_UNLIMITED = 'unlimited';
|
|
4
|
+
|
|
5
|
+
export type PlanType = typeof PLAN_TYPE_FREE | typeof PLAN_TYPE_TIERED | typeof PLAN_TYPE_UNLIMITED;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Simple hook to get the plan type from the current tier
|
|
9
|
+
*
|
|
10
|
+
* @param {object} currentTier - the current tier from the AI Feature data
|
|
11
|
+
* @return {PlanType} the plan type
|
|
12
|
+
*/
|
|
13
|
+
export const usePlanType = ( currentTier ): PlanType => {
|
|
14
|
+
if ( ! currentTier ) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if ( currentTier?.value === 0 ) {
|
|
19
|
+
return PLAN_TYPE_FREE;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if ( currentTier?.value === 1 ) {
|
|
23
|
+
return PLAN_TYPE_UNLIMITED;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return PLAN_TYPE_TIERED;
|
|
27
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from '@wordpress/element';
|
|
2
|
+
|
|
3
|
+
interface RafHandle {
|
|
4
|
+
id: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const setRafInterval = ( callback: () => void, timeout: number = 0 ) => {
|
|
8
|
+
const interval = timeout < 0 ? 0 : timeout;
|
|
9
|
+
const handle: RafHandle = {
|
|
10
|
+
id: 0,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
let startTime = Date.now();
|
|
14
|
+
|
|
15
|
+
const loop = () => {
|
|
16
|
+
const nowTime = Date.now();
|
|
17
|
+
if ( nowTime - startTime >= interval ) {
|
|
18
|
+
startTime = nowTime;
|
|
19
|
+
callback();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
handle.id = requestAnimationFrame( loop );
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
handle.id = requestAnimationFrame( loop );
|
|
26
|
+
|
|
27
|
+
return handle;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const clearRafInterval = ( handle?: RafHandle | null ) => {
|
|
31
|
+
if ( handle ) {
|
|
32
|
+
cancelAnimationFrame( handle.id );
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Invoke a function on an interval that uses requestAnimationFrame.
|
|
38
|
+
*
|
|
39
|
+
* @param {Function} callback - Function to invoke
|
|
40
|
+
* @param {number} timeout - Interval timout in MS.
|
|
41
|
+
*
|
|
42
|
+
* @return {Function} Function to clear the interval.
|
|
43
|
+
*/
|
|
44
|
+
const useRafInterval = ( callback: () => void, timeout = 0 ) => {
|
|
45
|
+
const timerRef = useRef< RafHandle >();
|
|
46
|
+
|
|
47
|
+
const callbackRef = useRef( callback );
|
|
48
|
+
callbackRef.current = callback;
|
|
49
|
+
|
|
50
|
+
useEffect( () => {
|
|
51
|
+
timerRef.current = setRafInterval( () => {
|
|
52
|
+
callbackRef.current();
|
|
53
|
+
}, timeout );
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
clearRafInterval( timerRef.current );
|
|
57
|
+
};
|
|
58
|
+
}, [ timeout ] );
|
|
59
|
+
|
|
60
|
+
const clear = useCallback( () => {
|
|
61
|
+
clearRafInterval( timerRef.current );
|
|
62
|
+
}, [] );
|
|
63
|
+
|
|
64
|
+
return clear;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default useRafInterval;
|