@wordpress/editor 14.43.0 → 14.44.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 +2 -0
- package/README.md +8 -0
- package/build/components/autocompleters/index.cjs +3 -0
- package/build/components/autocompleters/index.cjs.map +2 -2
- package/build/components/autocompleters/link.cjs +71 -0
- package/build/components/autocompleters/link.cjs.map +7 -0
- package/build/components/collab-sidebar/index.cjs.map +2 -2
- package/build/components/collaborators-overlay/cursor-dom-utils.cjs +1 -1
- package/build/components/collaborators-overlay/cursor-dom-utils.cjs.map +2 -2
- package/build/components/collaborators-overlay/timing-utils.cjs +1 -1
- package/build/components/collaborators-overlay/timing-utils.cjs.map +2 -2
- package/build/components/collaborators-overlay/use-render-cursors.cjs.map +2 -2
- package/build/components/error-boundary/index.cjs +1 -1
- package/build/components/error-boundary/index.cjs.map +2 -2
- package/build/components/post-revisions-panel/index.cjs +44 -42
- package/build/components/post-revisions-panel/index.cjs.map +2 -2
- package/build/components/post-revisions-preview/revisions-slider.cjs +9 -17
- package/build/components/post-revisions-preview/revisions-slider.cjs.map +2 -2
- package/build/components/post-title/index.cjs +2 -2
- package/build/components/post-title/index.cjs.map +2 -2
- package/build/components/sidebar/index.cjs +7 -1
- package/build/components/sidebar/index.cjs.map +2 -2
- package/build/components/sidebar/post-revision-summary.cjs +11 -2
- package/build/components/sidebar/post-revision-summary.cjs.map +2 -2
- package/build/components/sidebar/post-summary.cjs +0 -18
- package/build/components/sidebar/post-summary.cjs.map +2 -2
- package/build/components/style-book/categories.cjs.map +2 -2
- package/build/components/style-book/examples.cjs +1 -1
- package/build/components/style-book/examples.cjs.map +2 -2
- package/build/components/style-book/types.cjs.map +1 -1
- package/build/components/styles-canvas/revisions.cjs +2 -2
- package/build/components/styles-canvas/revisions.cjs.map +1 -1
- package/build/components/sync-connection-error-modal/index.cjs +66 -74
- package/build/components/sync-connection-error-modal/index.cjs.map +3 -3
- package/build/components/sync-connection-error-modal/use-retry-countdown.cjs +32 -9
- package/build/components/sync-connection-error-modal/use-retry-countdown.cjs.map +2 -2
- package/build/hooks/default-autocompleters.cjs +1 -1
- package/build/hooks/default-autocompleters.cjs.map +2 -2
- package/build/store/private-actions.cjs +1 -6
- package/build/store/private-actions.cjs.map +2 -2
- package/build/store/private-selectors.cjs +4 -6
- package/build/store/private-selectors.cjs.map +2 -2
- package/build/store/reducer.cjs +1 -1
- package/build/store/reducer.cjs.map +2 -2
- package/build-module/components/autocompleters/index.mjs +4 -2
- package/build-module/components/autocompleters/index.mjs.map +2 -2
- package/build-module/components/autocompleters/link.mjs +40 -0
- package/build-module/components/autocompleters/link.mjs.map +7 -0
- package/build-module/components/collab-sidebar/index.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/cursor-dom-utils.mjs +1 -1
- package/build-module/components/collaborators-overlay/cursor-dom-utils.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/timing-utils.mjs +1 -1
- package/build-module/components/collaborators-overlay/timing-utils.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/use-render-cursors.mjs.map +2 -2
- package/build-module/components/error-boundary/index.mjs +1 -1
- package/build-module/components/error-boundary/index.mjs.map +2 -2
- package/build-module/components/post-revisions-panel/index.mjs +44 -42
- package/build-module/components/post-revisions-panel/index.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/revisions-slider.mjs +9 -17
- package/build-module/components/post-revisions-preview/revisions-slider.mjs.map +2 -2
- package/build-module/components/post-title/index.mjs +2 -2
- package/build-module/components/post-title/index.mjs.map +2 -2
- package/build-module/components/sidebar/index.mjs +7 -1
- package/build-module/components/sidebar/index.mjs.map +2 -2
- package/build-module/components/sidebar/post-revision-summary.mjs +15 -3
- package/build-module/components/sidebar/post-revision-summary.mjs.map +2 -2
- package/build-module/components/sidebar/post-summary.mjs +1 -18
- package/build-module/components/sidebar/post-summary.mjs.map +2 -2
- package/build-module/components/style-book/categories.mjs.map +2 -2
- package/build-module/components/style-book/examples.mjs +1 -1
- package/build-module/components/style-book/examples.mjs.map +2 -2
- package/build-module/components/styles-canvas/revisions.mjs +2 -2
- package/build-module/components/styles-canvas/revisions.mjs.map +1 -1
- package/build-module/components/sync-connection-error-modal/index.mjs +66 -75
- package/build-module/components/sync-connection-error-modal/index.mjs.map +2 -2
- package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs +33 -10
- package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs.map +2 -2
- package/build-module/hooks/default-autocompleters.mjs +2 -2
- package/build-module/hooks/default-autocompleters.mjs.map +2 -2
- package/build-module/store/private-actions.mjs +1 -6
- package/build-module/store/private-actions.mjs.map +2 -2
- package/build-module/store/private-selectors.mjs +4 -6
- package/build-module/store/private-selectors.mjs.map +2 -2
- package/build-module/store/reducer.mjs +1 -1
- package/build-module/store/reducer.mjs.map +2 -2
- package/build-style/style-rtl.css +40 -30
- package/build-style/style.css +40 -30
- package/build-types/bindings/post-data.d.ts +3 -3
- package/build-types/bindings/term-data.d.ts +14 -14
- package/build-types/components/autocompleters/index.d.ts +1 -0
- package/build-types/components/autocompleters/link.d.ts +12 -0
- package/build-types/components/autocompleters/link.d.ts.map +1 -0
- package/build-types/components/collab-sidebar/index.d.ts.map +1 -1
- package/build-types/components/collaborators-overlay/use-render-cursors.d.ts.map +1 -1
- package/build-types/components/keyboard-shortcut-help-modal/config.d.ts +11 -11
- package/build-types/components/post-actions/set-as-homepage.d.ts +1 -1
- package/build-types/components/post-actions/set-as-posts-page.d.ts +1 -1
- package/build-types/components/post-format/index.d.ts +10 -10
- package/build-types/components/post-locked-modal/index.d.ts +2 -2
- package/build-types/components/post-revisions-panel/index.d.ts.map +1 -1
- package/build-types/components/post-revisions-preview/revisions-slider.d.ts.map +1 -1
- package/build-types/components/post-status/index.d.ts +10 -10
- package/build-types/components/post-visibility/utils.d.ts +6 -6
- package/build-types/components/sidebar/index.d.ts.map +1 -1
- package/build-types/components/sidebar/post-revision-summary.d.ts.map +1 -1
- package/build-types/components/sidebar/post-summary.d.ts +0 -3
- package/build-types/components/sidebar/post-summary.d.ts.map +1 -1
- package/build-types/components/style-book/categories.d.ts.map +1 -1
- package/build-types/components/style-book/examples.d.ts.map +1 -1
- package/build-types/components/style-book/types.d.ts +1 -13
- package/build-types/components/style-book/types.d.ts.map +1 -1
- package/build-types/components/sync-connection-error-modal/index.d.ts +0 -14
- package/build-types/components/sync-connection-error-modal/index.d.ts.map +1 -1
- package/build-types/components/sync-connection-error-modal/use-retry-countdown.d.ts.map +1 -1
- package/build-types/hooks/custom-sources-backwards-compatibility.d.ts +1 -1
- package/build-types/hooks/custom-sources-backwards-compatibility.d.ts.map +1 -1
- package/build-types/hooks/pattern-overrides.d.ts +1 -1
- package/build-types/hooks/pattern-overrides.d.ts.map +1 -1
- package/build-types/store/private-actions.d.ts.map +1 -1
- package/build-types/store/private-selectors.d.ts.map +1 -1
- package/build-types/store/reducer.d.ts +10 -10
- package/build-types/store/reducer.d.ts.map +1 -1
- package/build-types/utils/pageTypeBadge.d.ts +1 -1
- package/build-types/utils/pageTypeBadge.d.ts.map +1 -1
- package/package.json +45 -45
- package/src/components/autocompleters/index.js +1 -0
- package/src/components/autocompleters/link.js +47 -0
- package/src/components/autocompleters/style.scss +6 -0
- package/src/components/collab-sidebar/index.js +1 -0
- package/src/components/collaborators-overlay/cursor-dom-utils.ts +1 -1
- package/src/components/collaborators-overlay/timing-utils.ts +1 -1
- package/src/components/collaborators-overlay/use-render-cursors.ts +4 -2
- package/src/components/error-boundary/index.js +1 -1
- package/src/components/error-boundary/index.native.js +1 -1
- package/src/components/post-revisions-panel/index.js +46 -44
- package/src/components/post-revisions-preview/revisions-slider.js +9 -27
- package/src/components/post-title/index.js +3 -3
- package/src/components/sidebar/index.js +7 -1
- package/src/components/sidebar/post-revision-summary.js +13 -3
- package/src/components/sidebar/post-summary.js +1 -18
- package/src/components/style-book/categories.ts +0 -1
- package/src/components/style-book/examples.tsx +6 -12
- package/src/components/style-book/types.ts +1 -18
- package/src/components/styles-canvas/revisions.js +2 -2
- package/src/components/sync-connection-error-modal/index.tsx +151 -163
- package/src/components/sync-connection-error-modal/use-retry-countdown.ts +46 -10
- package/src/hooks/default-autocompleters.js +2 -2
- package/src/hooks/test/default-autocompleters.js +2 -2
- package/src/store/private-actions.js +1 -6
- package/src/store/private-selectors.js +4 -13
- package/src/store/reducer.js +9 -8
|
@@ -124,7 +124,7 @@ const PostTitle = forwardRef( ( _, forwardedRef ) => {
|
|
|
124
124
|
try {
|
|
125
125
|
plainText = clipboardData.getData( 'text/plain' );
|
|
126
126
|
html = clipboardData.getData( 'text/html' );
|
|
127
|
-
} catch
|
|
127
|
+
} catch {
|
|
128
128
|
// Some browsers like UC Browser paste plain text by default and
|
|
129
129
|
// don't support clipboardData at all, so allow default
|
|
130
130
|
// behaviour.
|
|
@@ -181,7 +181,7 @@ const PostTitle = forwardRef( ( _, forwardedRef ) => {
|
|
|
181
181
|
const style = isEditingContentOnlySection ? { opacity: 0.2 } : undefined;
|
|
182
182
|
|
|
183
183
|
return (
|
|
184
|
-
/* eslint-disable jsx-a11y/
|
|
184
|
+
/* eslint-disable jsx-a11y/no-noninteractive-element-to-interactive-role */
|
|
185
185
|
<h1
|
|
186
186
|
ref={ useMergeRefs( [ richTextRef, focusRef ] ) }
|
|
187
187
|
contentEditable={ ! isEditingContentOnlySection && ! isPreview }
|
|
@@ -195,7 +195,7 @@ const PostTitle = forwardRef( ( _, forwardedRef ) => {
|
|
|
195
195
|
onPaste={ onPaste }
|
|
196
196
|
style={ style }
|
|
197
197
|
/>
|
|
198
|
-
/* eslint-enable jsx-a11y/
|
|
198
|
+
/* eslint-enable jsx-a11y/no-noninteractive-element-to-interactive-role */
|
|
199
199
|
);
|
|
200
200
|
} );
|
|
201
201
|
|
|
@@ -112,7 +112,13 @@ const SidebarContent = ( {
|
|
|
112
112
|
<PluginDocumentSettingPanel.Slot />
|
|
113
113
|
<TemplateContentPanel />
|
|
114
114
|
{ window?.__experimentalDataFormInspector &&
|
|
115
|
-
[
|
|
115
|
+
[
|
|
116
|
+
'post',
|
|
117
|
+
'page',
|
|
118
|
+
'wp_template',
|
|
119
|
+
'wp_template_part',
|
|
120
|
+
'wp_block',
|
|
121
|
+
].includes( postType ) && (
|
|
116
122
|
<>
|
|
117
123
|
<TemplateActionsPanel />
|
|
118
124
|
<PostRevisionsPanel />
|
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { useSelect } from '@wordpress/data';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ExternalLink,
|
|
7
|
+
__experimentalVStack as VStack,
|
|
8
|
+
} from '@wordpress/components';
|
|
9
|
+
import { __ } from '@wordpress/i18n';
|
|
10
|
+
import { addQueryArgs } from '@wordpress/url';
|
|
6
11
|
|
|
7
12
|
/**
|
|
8
13
|
* Internal dependencies
|
|
@@ -15,7 +20,6 @@ import { PostContentInformationUI } from '../post-content-information';
|
|
|
15
20
|
import RevisionFieldsDiffPanel from '../revision-fields-diff';
|
|
16
21
|
import PostPanelSection from '../post-panel-section';
|
|
17
22
|
import PostCardPanel from '../post-card-panel';
|
|
18
|
-
import { OpenRevisionsClassicScreen } from './post-summary';
|
|
19
23
|
|
|
20
24
|
export default function PostRevisionSummary() {
|
|
21
25
|
const { revisionId, postId, postContent } = useSelect( ( select ) => {
|
|
@@ -40,7 +44,13 @@ export default function PostRevisionSummary() {
|
|
|
40
44
|
<PostContentInformationUI postContent={ postContent } />
|
|
41
45
|
<RevisionCreatedPanel />
|
|
42
46
|
</VStack>
|
|
43
|
-
<
|
|
47
|
+
<ExternalLink
|
|
48
|
+
href={ addQueryArgs( 'revision.php', {
|
|
49
|
+
revision: revisionId,
|
|
50
|
+
} ) }
|
|
51
|
+
>
|
|
52
|
+
{ __( 'Open classic revisions screen' ) }
|
|
53
|
+
</ExternalLink>
|
|
44
54
|
<RevisionAuthorPanel />
|
|
45
55
|
</VStack>
|
|
46
56
|
</PostPanelSection>
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
__experimentalVStack as VStack,
|
|
6
|
-
ExternalLink,
|
|
7
|
-
} from '@wordpress/components';
|
|
4
|
+
import { __experimentalVStack as VStack } from '@wordpress/components';
|
|
8
5
|
import { useSelect } from '@wordpress/data';
|
|
9
|
-
import { __ } from '@wordpress/i18n';
|
|
10
|
-
import { addQueryArgs } from '@wordpress/url';
|
|
11
6
|
|
|
12
7
|
/**
|
|
13
8
|
* Internal dependencies
|
|
@@ -41,18 +36,6 @@ import PostTrash from '../post-trash';
|
|
|
41
36
|
*/
|
|
42
37
|
const PANEL_NAME = 'post-status';
|
|
43
38
|
|
|
44
|
-
export function OpenRevisionsClassicScreen( { revisionId } ) {
|
|
45
|
-
return (
|
|
46
|
-
<ExternalLink
|
|
47
|
-
href={ addQueryArgs( 'revision.php', {
|
|
48
|
-
revision: revisionId,
|
|
49
|
-
} ) }
|
|
50
|
-
>
|
|
51
|
-
{ __( 'Open classic revisions screen' ) }
|
|
52
|
-
</ExternalLink>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
39
|
export default function PostSummary( { onActionPerformed } ) {
|
|
57
40
|
const postType = useSelect(
|
|
58
41
|
( select ) => select( editorStore ).getCurrentPostType(),
|
|
@@ -2,24 +2,18 @@
|
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { __, sprintf } from '@wordpress/i18n';
|
|
5
|
+
import type { Block } from '@wordpress/blocks';
|
|
5
6
|
import {
|
|
6
7
|
getBlockType,
|
|
7
8
|
getBlockTypes,
|
|
8
9
|
getBlockFromExample,
|
|
9
10
|
createBlock,
|
|
10
|
-
// @wordpress/blocks imports are not typed.
|
|
11
|
-
// @ts-expect-error
|
|
12
11
|
} from '@wordpress/blocks';
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Internal dependencies
|
|
16
15
|
*/
|
|
17
|
-
import type {
|
|
18
|
-
BlockExample,
|
|
19
|
-
ColorOrigin,
|
|
20
|
-
MultiOriginPalettes,
|
|
21
|
-
BlockType,
|
|
22
|
-
} from './types';
|
|
16
|
+
import type { BlockExample, ColorOrigin, MultiOriginPalettes } from './types';
|
|
23
17
|
import ColorExamples from './color-examples';
|
|
24
18
|
import DuotoneExamples from './duotone-examples';
|
|
25
19
|
import { STYLE_BOOK_COLOR_GROUPS } from './constants';
|
|
@@ -111,7 +105,7 @@ function getOverviewBlockExamples(
|
|
|
111
105
|
}
|
|
112
106
|
|
|
113
107
|
// Get examples for typography blocks.
|
|
114
|
-
const typographyBlockExamples:
|
|
108
|
+
const typographyBlockExamples: Block[] = [];
|
|
115
109
|
|
|
116
110
|
if ( getBlockType( 'core/heading' ) ) {
|
|
117
111
|
const headingBlock = createBlock( 'core/heading', {
|
|
@@ -212,7 +206,7 @@ function getOverviewBlockExamples(
|
|
|
212
206
|
*/
|
|
213
207
|
export function getExamples( colors: MultiOriginPalettes ): BlockExample[] {
|
|
214
208
|
const nonHeadingBlockExamples = getBlockTypes()
|
|
215
|
-
.filter( ( blockType
|
|
209
|
+
.filter( ( blockType ) => {
|
|
216
210
|
const { name, example, supports } = blockType;
|
|
217
211
|
return (
|
|
218
212
|
name !== 'core/heading' &&
|
|
@@ -220,7 +214,7 @@ export function getExamples( colors: MultiOriginPalettes ): BlockExample[] {
|
|
|
220
214
|
supports?.inserter !== false
|
|
221
215
|
);
|
|
222
216
|
} )
|
|
223
|
-
.map( ( blockType
|
|
217
|
+
.map( ( blockType ) => ( {
|
|
224
218
|
name: blockType.name,
|
|
225
219
|
title: blockType.title,
|
|
226
220
|
category: blockType.category,
|
|
@@ -232,7 +226,7 @@ export function getExamples( colors: MultiOriginPalettes ): BlockExample[] {
|
|
|
232
226
|
blocks: getBlockFromExample( blockType.name, {
|
|
233
227
|
...blockType.example,
|
|
234
228
|
attributes: {
|
|
235
|
-
...blockType.example
|
|
229
|
+
...blockType.example?.attributes,
|
|
236
230
|
style: undefined,
|
|
237
231
|
},
|
|
238
232
|
} ),
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
name: string;
|
|
3
|
-
attributes: Record< string, unknown >;
|
|
4
|
-
innerBlocks?: Block[];
|
|
5
|
-
};
|
|
1
|
+
import type { Block } from '@wordpress/blocks';
|
|
6
2
|
|
|
7
3
|
export type StyleBookCategory = {
|
|
8
4
|
title: string;
|
|
@@ -65,16 +61,3 @@ export type MultiOriginPalettes = {
|
|
|
65
61
|
duotones: Omit< ColorOrigin, 'colors' | 'gradients' >;
|
|
66
62
|
gradients: Omit< ColorOrigin, 'colors' | 'duotones' >;
|
|
67
63
|
};
|
|
68
|
-
|
|
69
|
-
/*
|
|
70
|
-
* Typing the items from getBlockTypes from '@wordpress/blocks'
|
|
71
|
-
* to appease the TS linter.
|
|
72
|
-
*/
|
|
73
|
-
export type BlockType = {
|
|
74
|
-
name: string;
|
|
75
|
-
title: string;
|
|
76
|
-
category: string;
|
|
77
|
-
example: BlockType;
|
|
78
|
-
attributes: Record< string, unknown >;
|
|
79
|
-
supports: Record< string, unknown >;
|
|
80
|
-
};
|
|
@@ -23,7 +23,7 @@ import { unlock } from '../../lock-unlock';
|
|
|
23
23
|
|
|
24
24
|
const {
|
|
25
25
|
ExperimentalBlockEditorProvider,
|
|
26
|
-
|
|
26
|
+
BlockStyleVariationOverridesWithConfig,
|
|
27
27
|
} = unlock( blockEditorPrivateApis );
|
|
28
28
|
|
|
29
29
|
function isObjectEmpty( object ) {
|
|
@@ -133,7 +133,7 @@ function StylesCanvasRevisions( { path }, ref ) {
|
|
|
133
133
|
* so they can access any registered style overrides.
|
|
134
134
|
*/ }
|
|
135
135
|
<EditorStyles styles={ editorStyles } />
|
|
136
|
-
<
|
|
136
|
+
<BlockStyleVariationOverridesWithConfig
|
|
137
137
|
config={ mergedConfig }
|
|
138
138
|
/>
|
|
139
139
|
</ExperimentalBlockEditorProvider>
|
|
@@ -8,7 +8,6 @@ import { serialize } from '@wordpress/blocks';
|
|
|
8
8
|
import {
|
|
9
9
|
store as coreDataStore,
|
|
10
10
|
privateApis as coreDataPrivateApis,
|
|
11
|
-
type ConnectionError,
|
|
12
11
|
} from '@wordpress/core-data';
|
|
13
12
|
// @ts-expect-error - No type declarations available for @wordpress/block-editor
|
|
14
13
|
// prettier-ignore
|
|
@@ -16,10 +15,10 @@ import { privateApis, store as blockEditorStore } from '@wordpress/block-editor'
|
|
|
16
15
|
import {
|
|
17
16
|
Button,
|
|
18
17
|
Modal,
|
|
19
|
-
withFilters,
|
|
20
18
|
__experimentalHStack as HStack,
|
|
21
19
|
__experimentalVStack as VStack,
|
|
22
20
|
} from '@wordpress/components';
|
|
21
|
+
import { applyFilters } from '@wordpress/hooks';
|
|
23
22
|
import { useState, useEffect } from '@wordpress/element';
|
|
24
23
|
import { __, sprintf, _n } from '@wordpress/i18n';
|
|
25
24
|
|
|
@@ -37,144 +36,6 @@ const { retrySyncConnection } = unlock( coreDataPrivateApis );
|
|
|
37
36
|
// Debounce time for initial disconnected status to allow connection to establish.
|
|
38
37
|
const INITIAL_DISCONNECTED_DEBOUNCE_MS = 20000;
|
|
39
38
|
|
|
40
|
-
// Debounce time for showing the disconnect dialog after the intial connection,
|
|
41
|
-
// allowing brief network interruptions to resolve.
|
|
42
|
-
const DISCONNECTED_DEBOUNCE_MS = 8000;
|
|
43
|
-
|
|
44
|
-
export interface SyncConnectionErrorModalProps {
|
|
45
|
-
description: string; // Modal description.
|
|
46
|
-
error?: ConnectionError; // Error object with a `code` property.
|
|
47
|
-
manualRetry?: () => void; // Callback for when the retry button is clicked.
|
|
48
|
-
postType?: { slug?: string; labels?: { name?: string } } | null; // Current post type object.
|
|
49
|
-
secondsRemainingUntilAutoRetry?: number; // Seconds remaining until the next automatic retry attempt, if applicable.
|
|
50
|
-
title: string; // Modal title.
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Default sync connection modal component.
|
|
55
|
-
*
|
|
56
|
-
* Can be replaced or wrapped via the `editor.SyncConnectionErrorModal` filter.
|
|
57
|
-
*
|
|
58
|
-
* @param props - SyncConnectionErrorModalProps.
|
|
59
|
-
*/
|
|
60
|
-
function DefaultSyncConnectionErrorModal(
|
|
61
|
-
props: SyncConnectionErrorModalProps
|
|
62
|
-
) {
|
|
63
|
-
const {
|
|
64
|
-
description,
|
|
65
|
-
manualRetry,
|
|
66
|
-
postType,
|
|
67
|
-
secondsRemainingUntilAutoRetry,
|
|
68
|
-
title,
|
|
69
|
-
} = props;
|
|
70
|
-
const copyButtonRef = useCopyToClipboard( () => {
|
|
71
|
-
const blocks = select( blockEditorStore ).getBlocks();
|
|
72
|
-
return serialize( blocks );
|
|
73
|
-
} );
|
|
74
|
-
|
|
75
|
-
let retryCountdownText: string = '';
|
|
76
|
-
let isRetrying = false;
|
|
77
|
-
if (
|
|
78
|
-
secondsRemainingUntilAutoRetry &&
|
|
79
|
-
secondsRemainingUntilAutoRetry > 0
|
|
80
|
-
) {
|
|
81
|
-
retryCountdownText = sprintf(
|
|
82
|
-
/* translators: %d: number of seconds until retry */
|
|
83
|
-
_n(
|
|
84
|
-
'Retrying connection in %d second\u2026',
|
|
85
|
-
'Retrying connection in %d seconds\u2026',
|
|
86
|
-
secondsRemainingUntilAutoRetry
|
|
87
|
-
),
|
|
88
|
-
secondsRemainingUntilAutoRetry
|
|
89
|
-
);
|
|
90
|
-
} else if ( 0 === secondsRemainingUntilAutoRetry ) {
|
|
91
|
-
isRetrying = true;
|
|
92
|
-
retryCountdownText = __( 'Retrying\u2026' );
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
let editPostHref = 'edit.php';
|
|
96
|
-
if ( postType?.slug ) {
|
|
97
|
-
editPostHref = `edit.php?post_type=${ postType.slug }`;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
<Modal
|
|
102
|
-
overlayClassName="editor-sync-connection-error-modal"
|
|
103
|
-
isDismissible={ false }
|
|
104
|
-
onRequestClose={ () => {} }
|
|
105
|
-
shouldCloseOnClickOutside={ false }
|
|
106
|
-
shouldCloseOnEsc={ false }
|
|
107
|
-
size="medium"
|
|
108
|
-
title={ title }
|
|
109
|
-
>
|
|
110
|
-
<VStack spacing={ 6 }>
|
|
111
|
-
<p>{ description }</p>
|
|
112
|
-
{ retryCountdownText && (
|
|
113
|
-
<p className="editor-sync-connection-error-modal__retry-countdown">
|
|
114
|
-
{ retryCountdownText }
|
|
115
|
-
</p>
|
|
116
|
-
) }
|
|
117
|
-
<HStack justify="right">
|
|
118
|
-
<Button
|
|
119
|
-
__next40pxDefaultSize
|
|
120
|
-
href={ editPostHref }
|
|
121
|
-
isDestructive
|
|
122
|
-
variant="tertiary"
|
|
123
|
-
>
|
|
124
|
-
{ sprintf(
|
|
125
|
-
/* translators: %s: Post type name (e.g., "Posts", "Pages"). */
|
|
126
|
-
__( 'Back to %s' ),
|
|
127
|
-
postType?.labels?.name ?? __( 'Posts' )
|
|
128
|
-
) }
|
|
129
|
-
</Button>
|
|
130
|
-
<Button
|
|
131
|
-
__next40pxDefaultSize
|
|
132
|
-
ref={ copyButtonRef }
|
|
133
|
-
variant={ manualRetry ? 'secondary' : 'primary' }
|
|
134
|
-
>
|
|
135
|
-
{ __( 'Copy Post Content' ) }
|
|
136
|
-
</Button>
|
|
137
|
-
{ manualRetry && (
|
|
138
|
-
<Button
|
|
139
|
-
__next40pxDefaultSize
|
|
140
|
-
accessibleWhenDisabled
|
|
141
|
-
aria-disabled={ isRetrying }
|
|
142
|
-
disabled={ isRetrying }
|
|
143
|
-
isBusy={ isRetrying }
|
|
144
|
-
variant="primary"
|
|
145
|
-
onClick={ manualRetry }
|
|
146
|
-
>
|
|
147
|
-
{ __( 'Retry' ) }
|
|
148
|
-
</Button>
|
|
149
|
-
) }
|
|
150
|
-
</HStack>
|
|
151
|
-
</VStack>
|
|
152
|
-
</Modal>
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Filtered version of the sync connection modal, allowing third-party
|
|
158
|
-
* plugins to replace the default modal via:
|
|
159
|
-
*
|
|
160
|
-
* ```js
|
|
161
|
-
* wp.hooks.addFilter(
|
|
162
|
-
* 'editor.SyncConnectionErrorModal',
|
|
163
|
-
* 'my-plugin/custom-sync-connection-error-modal',
|
|
164
|
-
* ( OriginalComponent ) => ( props ) => {
|
|
165
|
-
* // Return a custom component or wrap the original.
|
|
166
|
-
* return <OriginalComponent { ...props } />;
|
|
167
|
-
* }
|
|
168
|
-
* );
|
|
169
|
-
* ```
|
|
170
|
-
*/
|
|
171
|
-
// @ts-ignore
|
|
172
|
-
const FilteredSyncConnectionErrorModal = globalThis.IS_GUTENBERG_PLUGIN
|
|
173
|
-
? withFilters( 'editor.SyncConnectionErrorModal' )(
|
|
174
|
-
DefaultSyncConnectionErrorModal
|
|
175
|
-
)
|
|
176
|
-
: DefaultSyncConnectionErrorModal;
|
|
177
|
-
|
|
178
39
|
/**
|
|
179
40
|
* Sync connection modal that displays when any entity reports a disconnection.
|
|
180
41
|
* Uses BlockCanvasCover.Fill to render in the block canvas.
|
|
@@ -184,6 +45,8 @@ const FilteredSyncConnectionErrorModal = globalThis.IS_GUTENBERG_PLUGIN
|
|
|
184
45
|
export function SyncConnectionErrorModal() {
|
|
185
46
|
const [ hasInitialized, setHasInitialized ] = useState( false );
|
|
186
47
|
const [ showModal, setShowModal ] = useState( false );
|
|
48
|
+
const [ isManualRetryAvailable, setIsManualRetryAvailable ] =
|
|
49
|
+
useState( false );
|
|
187
50
|
|
|
188
51
|
const { connectionStatus, isCollaborationEnabled, postType } = useSelect(
|
|
189
52
|
( selectFn ) => {
|
|
@@ -207,7 +70,10 @@ export function SyncConnectionErrorModal() {
|
|
|
207
70
|
const { onManualRetry, secondsRemaining } =
|
|
208
71
|
useRetryCountdown( connectionStatus );
|
|
209
72
|
|
|
210
|
-
const
|
|
73
|
+
const copyButtonRef = useCopyToClipboard( () => {
|
|
74
|
+
const blocks = select( blockEditorStore ).getBlocks();
|
|
75
|
+
return serialize( blocks );
|
|
76
|
+
} );
|
|
211
77
|
|
|
212
78
|
// Set hasInitialized after a debounce to give extra time on initial load.
|
|
213
79
|
useEffect( () => {
|
|
@@ -218,18 +84,46 @@ export function SyncConnectionErrorModal() {
|
|
|
218
84
|
return () => clearTimeout( timeout );
|
|
219
85
|
}, [] );
|
|
220
86
|
|
|
87
|
+
// Track retry availability separately from the raw connection status.
|
|
88
|
+
// The polling manager briefly emits `{ status: 'connecting' }` without
|
|
89
|
+
// `canManuallyRetry` when a retry is kicked off, which would otherwise
|
|
90
|
+
// unmount the Retry button briefly.
|
|
221
91
|
useEffect( () => {
|
|
222
|
-
if (
|
|
92
|
+
if ( 'connecting' === connectionStatus?.status ) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
setIsManualRetryAvailable(
|
|
97
|
+
connectionStatus !== null &&
|
|
98
|
+
'canManuallyRetry' in connectionStatus &&
|
|
99
|
+
connectionStatus.canManuallyRetry === true
|
|
100
|
+
);
|
|
101
|
+
}, [ connectionStatus ] );
|
|
102
|
+
|
|
103
|
+
// Show the modal when disconnected and either retries are exhausted or
|
|
104
|
+
// no retry is available (unrecoverable error). Hide on reconnect.
|
|
105
|
+
// The 'connecting' state is ignored so the modal preserves its current
|
|
106
|
+
// visibility during active retry attempts.
|
|
107
|
+
const canRetry =
|
|
108
|
+
connectionStatus &&
|
|
109
|
+
'disconnected' === connectionStatus.status &&
|
|
110
|
+
( connectionStatus.canManuallyRetry ||
|
|
111
|
+
connectionStatus.willAutoRetryInMs );
|
|
112
|
+
|
|
113
|
+
useEffect( () => {
|
|
114
|
+
if ( 'connected' === connectionStatus?.status ) {
|
|
223
115
|
setShowModal( false );
|
|
224
116
|
return;
|
|
225
117
|
}
|
|
226
118
|
|
|
227
|
-
|
|
119
|
+
if (
|
|
120
|
+
connectionStatus?.status &&
|
|
121
|
+
'connecting' !== connectionStatus.status &&
|
|
122
|
+
( ! canRetry || connectionStatus.backgroundRetriesFailed )
|
|
123
|
+
) {
|
|
228
124
|
setShowModal( true );
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return () => clearTimeout( timeout );
|
|
232
|
-
}, [ isConnected ] );
|
|
125
|
+
}
|
|
126
|
+
}, [ connectionStatus, canRetry ] );
|
|
233
127
|
|
|
234
128
|
if ( ! isCollaborationEnabled || ! hasInitialized || ! showModal ) {
|
|
235
129
|
return null;
|
|
@@ -239,27 +133,121 @@ export function SyncConnectionErrorModal() {
|
|
|
239
133
|
connectionStatus && 'error' in connectionStatus
|
|
240
134
|
? connectionStatus?.error
|
|
241
135
|
: undefined;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
136
|
+
|
|
137
|
+
// For unrecoverable errors (no retry available), allow plugins to handle
|
|
138
|
+
// the error themselves. If a plugin returns a value other than false, it
|
|
139
|
+
// signals that it has taken over error display and the default modal is
|
|
140
|
+
// suppressed.
|
|
141
|
+
//
|
|
142
|
+
// @example
|
|
143
|
+
// ```js
|
|
144
|
+
// wp.hooks.addFilter(
|
|
145
|
+
// 'editor.isSyncConnectionErrorHandled',
|
|
146
|
+
// 'my-plugin/handle-sync-error',
|
|
147
|
+
// ( isHandled, errorCode ) => {
|
|
148
|
+
// if ( errorCode === 'connection-limit-exceeded' ) {
|
|
149
|
+
// return true; // Plugin handles this error via its own UI.
|
|
150
|
+
// }
|
|
151
|
+
// return isHandled;
|
|
152
|
+
// }
|
|
153
|
+
// );
|
|
154
|
+
// ```
|
|
155
|
+
if (
|
|
156
|
+
! canRetry &&
|
|
157
|
+
applyFilters(
|
|
158
|
+
'editor.isSyncConnectionErrorHandled',
|
|
159
|
+
false,
|
|
160
|
+
error?.code
|
|
161
|
+
) !== false
|
|
162
|
+
) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const manualRetry = isManualRetryAvailable
|
|
167
|
+
? () => {
|
|
168
|
+
onManualRetry();
|
|
169
|
+
retrySyncConnection();
|
|
170
|
+
}
|
|
171
|
+
: undefined;
|
|
172
|
+
|
|
251
173
|
const messages = getSyncErrorMessages( error );
|
|
252
174
|
|
|
175
|
+
let retryCountdownText: string = '';
|
|
176
|
+
let isRetrying = false;
|
|
177
|
+
if ( secondsRemaining && secondsRemaining > 0 ) {
|
|
178
|
+
retryCountdownText = sprintf(
|
|
179
|
+
/* translators: %d: number of seconds until retry */
|
|
180
|
+
_n(
|
|
181
|
+
'Retrying connection in %d second\u2026',
|
|
182
|
+
'Retrying connection in %d seconds\u2026',
|
|
183
|
+
secondsRemaining
|
|
184
|
+
),
|
|
185
|
+
secondsRemaining
|
|
186
|
+
);
|
|
187
|
+
} else if ( 0 === secondsRemaining ) {
|
|
188
|
+
isRetrying = true;
|
|
189
|
+
retryCountdownText = __( 'Retrying\u2026' );
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
let editPostHref = 'edit.php';
|
|
193
|
+
if ( postType?.slug ) {
|
|
194
|
+
editPostHref = `edit.php?post_type=${ postType.slug }`;
|
|
195
|
+
}
|
|
196
|
+
|
|
253
197
|
return (
|
|
254
198
|
<BlockCanvasCover.Fill>
|
|
255
|
-
<
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
199
|
+
<Modal
|
|
200
|
+
overlayClassName="editor-sync-connection-error-modal"
|
|
201
|
+
isDismissible={ false }
|
|
202
|
+
onRequestClose={ () => {} }
|
|
203
|
+
shouldCloseOnClickOutside={ false }
|
|
204
|
+
shouldCloseOnEsc={ false }
|
|
205
|
+
size="medium"
|
|
261
206
|
title={ messages.title }
|
|
262
|
-
|
|
207
|
+
>
|
|
208
|
+
<VStack spacing={ 6 }>
|
|
209
|
+
<p>{ messages.description }</p>
|
|
210
|
+
{ retryCountdownText && (
|
|
211
|
+
<p className="editor-sync-connection-error-modal__retry-countdown">
|
|
212
|
+
{ retryCountdownText }
|
|
213
|
+
</p>
|
|
214
|
+
) }
|
|
215
|
+
<HStack justify="right">
|
|
216
|
+
<Button
|
|
217
|
+
__next40pxDefaultSize
|
|
218
|
+
href={ editPostHref }
|
|
219
|
+
isDestructive
|
|
220
|
+
variant="tertiary"
|
|
221
|
+
>
|
|
222
|
+
{ sprintf(
|
|
223
|
+
/* translators: %s: Post type name (e.g., "Posts", "Pages"). */
|
|
224
|
+
__( 'Back to %s' ),
|
|
225
|
+
postType?.labels?.name ?? __( 'Posts' )
|
|
226
|
+
) }
|
|
227
|
+
</Button>
|
|
228
|
+
<Button
|
|
229
|
+
__next40pxDefaultSize
|
|
230
|
+
ref={ copyButtonRef }
|
|
231
|
+
variant={ manualRetry ? 'secondary' : 'primary' }
|
|
232
|
+
>
|
|
233
|
+
{ __( 'Copy Post Content' ) }
|
|
234
|
+
</Button>
|
|
235
|
+
{ manualRetry && (
|
|
236
|
+
<Button
|
|
237
|
+
__next40pxDefaultSize
|
|
238
|
+
accessibleWhenDisabled
|
|
239
|
+
aria-disabled={ isRetrying }
|
|
240
|
+
disabled={ isRetrying }
|
|
241
|
+
isBusy={ isRetrying }
|
|
242
|
+
variant="primary"
|
|
243
|
+
onClick={ manualRetry }
|
|
244
|
+
>
|
|
245
|
+
{ __( 'Retry' ) }
|
|
246
|
+
</Button>
|
|
247
|
+
) }
|
|
248
|
+
</HStack>
|
|
249
|
+
</VStack>
|
|
250
|
+
</Modal>
|
|
263
251
|
</BlockCanvasCover.Fill>
|
|
264
252
|
);
|
|
265
253
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
4
|
import type { ConnectionStatus } from '@wordpress/core-data';
|
|
5
|
-
import { useState, useEffect } from '@wordpress/element';
|
|
5
|
+
import { useState, useEffect, useRef } from '@wordpress/element';
|
|
6
6
|
|
|
7
7
|
interface UseRetryCountdownResult {
|
|
8
8
|
onManualRetry: () => void;
|
|
@@ -13,6 +13,7 @@ export function useRetryCountdown(
|
|
|
13
13
|
connectionStatus?: ConnectionStatus | null
|
|
14
14
|
): UseRetryCountdownResult {
|
|
15
15
|
const [ secondsRemaining, setSecondsRemaining ] = useState< number >();
|
|
16
|
+
const hasRetriedRef = useRef( false );
|
|
16
17
|
|
|
17
18
|
useEffect( () => {
|
|
18
19
|
if ( ! connectionStatus ) {
|
|
@@ -22,6 +23,7 @@ export function useRetryCountdown(
|
|
|
22
23
|
// Only clear countdown when explicitly connected.
|
|
23
24
|
if ( 'connected' === connectionStatus.status ) {
|
|
24
25
|
setSecondsRemaining( undefined );
|
|
26
|
+
hasRetriedRef.current = false;
|
|
25
27
|
return;
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -37,21 +39,55 @@ export function useRetryCountdown(
|
|
|
37
39
|
|
|
38
40
|
const { willAutoRetryInMs: retryInMs } = connectionStatus;
|
|
39
41
|
const retryAt = Date.now() + retryInMs;
|
|
40
|
-
setSecondsRemaining( Math.ceil( retryInMs / 1000 ) );
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
// After a retry attempt (manual or automatic), show "Retrying..."
|
|
44
|
+
// for 500ms before starting the next countdown. Skip the delay on
|
|
45
|
+
// the very first disconnect so the countdown starts immediately.
|
|
46
|
+
const hasRetried = hasRetriedRef.current;
|
|
47
|
+
hasRetriedRef.current = true;
|
|
48
|
+
|
|
49
|
+
if ( hasRetried ) {
|
|
50
|
+
setSecondsRemaining( 0 );
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let countdownIntervalId: ReturnType< typeof setInterval > | null = null;
|
|
54
|
+
|
|
55
|
+
const startCountdown = () => {
|
|
56
|
+
setSecondsRemaining( Math.ceil( ( retryAt - Date.now() ) / 1000 ) );
|
|
57
|
+
|
|
58
|
+
countdownIntervalId = setInterval( () => {
|
|
59
|
+
const remaining = Math.ceil( ( retryAt - Date.now() ) / 1000 );
|
|
60
|
+
setSecondsRemaining( Math.max( 0, remaining ) );
|
|
61
|
+
|
|
62
|
+
if ( remaining <= 0 && countdownIntervalId ) {
|
|
63
|
+
clearInterval( countdownIntervalId );
|
|
64
|
+
}
|
|
65
|
+
}, 1000 );
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const retryingDelayId = hasRetried
|
|
69
|
+
? setTimeout( startCountdown, 500 )
|
|
70
|
+
: null;
|
|
71
|
+
|
|
72
|
+
if ( ! retryingDelayId ) {
|
|
73
|
+
startCountdown();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return () => {
|
|
77
|
+
if ( retryingDelayId ) {
|
|
78
|
+
clearTimeout( retryingDelayId );
|
|
47
79
|
}
|
|
48
|
-
}, 1000 );
|
|
49
80
|
|
|
50
|
-
|
|
81
|
+
if ( countdownIntervalId ) {
|
|
82
|
+
clearInterval( countdownIntervalId );
|
|
83
|
+
}
|
|
84
|
+
};
|
|
51
85
|
}, [ connectionStatus ] );
|
|
52
86
|
|
|
53
87
|
return {
|
|
54
|
-
onManualRetry: () =>
|
|
88
|
+
onManualRetry: () => {
|
|
89
|
+
setSecondsRemaining( 0 );
|
|
90
|
+
},
|
|
55
91
|
secondsRemaining,
|
|
56
92
|
};
|
|
57
93
|
}
|