@wordpress/editor 14.41.2-next.v.202603161435.0 → 14.42.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 +1 -1
- package/build/components/collaborators-overlay/compute-selection.cjs +10 -10
- package/build/components/collaborators-overlay/compute-selection.cjs.map +2 -2
- package/build/components/collaborators-overlay/overlay.cjs +11 -0
- package/build/components/collaborators-overlay/overlay.cjs.map +2 -2
- package/build/components/collaborators-overlay/timing-utils.cjs +46 -0
- package/build/components/collaborators-overlay/timing-utils.cjs.map +7 -0
- package/build/components/collaborators-overlay/use-render-cursors.cjs +1 -1
- package/build/components/collaborators-overlay/use-render-cursors.cjs.map +2 -2
- package/build/components/post-locked-modal/index.cjs +16 -3
- package/build/components/post-locked-modal/index.cjs.map +2 -2
- package/build/components/post-revisions-preview/block-diff.cjs +39 -11
- package/build/components/post-revisions-preview/block-diff.cjs.map +2 -2
- package/build/components/post-revisions-preview/diff-markers.cjs +2 -2
- package/build/components/post-revisions-preview/diff-markers.cjs.map +2 -2
- package/build/components/post-revisions-preview/revisions-canvas.cjs +1 -1
- package/build/components/post-revisions-preview/revisions-canvas.cjs.map +2 -2
- package/build/components/post-template/block-theme.cjs +7 -4
- package/build/components/post-template/block-theme.cjs.map +2 -2
- package/build/components/post-template/hooks.cjs +39 -2
- package/build/components/post-template/hooks.cjs.map +2 -2
- package/build/components/post-template/panel.cjs +5 -42
- package/build/components/post-template/panel.cjs.map +3 -3
- package/build/components/provider/use-block-editor-settings.cjs +2 -0
- package/build/components/provider/use-block-editor-settings.cjs.map +3 -3
- package/build/components/revision-block-diff/index.cjs +84 -0
- package/build/components/revision-block-diff/index.cjs.map +7 -0
- package/build/components/sidebar/dataform-post-summary.cjs +17 -2
- package/build/components/sidebar/dataform-post-summary.cjs.map +2 -2
- package/build/components/sidebar/index.cjs +5 -1
- package/build/components/sidebar/index.cjs.map +3 -3
- package/build/components/{sync-connection-modal → sync-connection-error-modal}/index.cjs +87 -78
- package/build/components/sync-connection-error-modal/index.cjs.map +7 -0
- package/build/components/{sync-connection-modal → sync-connection-error-modal}/use-retry-countdown.cjs +14 -27
- package/build/components/sync-connection-error-modal/use-retry-countdown.cjs.map +7 -0
- package/build/components/visual-editor/index.cjs +2 -2
- package/build/components/visual-editor/index.cjs.map +2 -2
- package/build/store/actions.cjs +1 -3
- package/build/store/actions.cjs.map +2 -2
- package/build/utils/media-finalize/index.cjs +43 -0
- package/build/utils/media-finalize/index.cjs.map +7 -0
- package/build/utils/sync-error-messages.cjs +29 -16
- package/build/utils/sync-error-messages.cjs.map +3 -3
- package/build-module/components/collaborators-overlay/compute-selection.mjs +10 -10
- package/build-module/components/collaborators-overlay/compute-selection.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/overlay.mjs +11 -0
- package/build-module/components/collaborators-overlay/overlay.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/timing-utils.mjs +21 -0
- package/build-module/components/collaborators-overlay/timing-utils.mjs.map +7 -0
- package/build-module/components/collaborators-overlay/use-render-cursors.mjs +1 -1
- package/build-module/components/collaborators-overlay/use-render-cursors.mjs.map +2 -2
- package/build-module/components/post-locked-modal/index.mjs +16 -3
- package/build-module/components/post-locked-modal/index.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/block-diff.mjs +39 -11
- package/build-module/components/post-revisions-preview/block-diff.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/diff-markers.mjs +2 -2
- package/build-module/components/post-revisions-preview/diff-markers.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/revisions-canvas.mjs +1 -1
- package/build-module/components/post-revisions-preview/revisions-canvas.mjs.map +2 -2
- package/build-module/components/post-template/block-theme.mjs +7 -4
- package/build-module/components/post-template/block-theme.mjs.map +2 -2
- package/build-module/components/post-template/hooks.mjs +37 -1
- package/build-module/components/post-template/hooks.mjs.map +2 -2
- package/build-module/components/post-template/panel.mjs +5 -42
- package/build-module/components/post-template/panel.mjs.map +2 -2
- package/build-module/components/provider/use-block-editor-settings.mjs +2 -0
- package/build-module/components/provider/use-block-editor-settings.mjs.map +2 -2
- package/build-module/components/revision-block-diff/index.mjs +53 -0
- package/build-module/components/revision-block-diff/index.mjs.map +7 -0
- package/build-module/components/sidebar/dataform-post-summary.mjs +17 -2
- package/build-module/components/sidebar/dataform-post-summary.mjs.map +2 -2
- package/build-module/components/sidebar/index.mjs +5 -1
- package/build-module/components/sidebar/index.mjs.map +2 -2
- package/build-module/components/sync-connection-error-modal/index.mjs +177 -0
- package/build-module/components/sync-connection-error-modal/index.mjs.map +7 -0
- package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs +36 -0
- package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs.map +7 -0
- package/build-module/components/visual-editor/index.mjs +2 -2
- package/build-module/components/visual-editor/index.mjs.map +2 -2
- package/build-module/store/actions.mjs +1 -3
- package/build-module/store/actions.mjs.map +2 -2
- package/build-module/utils/media-finalize/index.mjs +12 -0
- package/build-module/utils/media-finalize/index.mjs.map +7 -0
- package/build-module/utils/sync-error-messages.mjs +24 -16
- package/build-module/utils/sync-error-messages.mjs.map +3 -3
- package/build-style/style-rtl.css +24 -8
- package/build-style/style.css +24 -8
- package/build-types/components/collaborators-overlay/overlay.d.ts.map +1 -1
- package/build-types/components/collaborators-overlay/timing-utils.d.ts +11 -0
- package/build-types/components/collaborators-overlay/timing-utils.d.ts.map +1 -0
- package/build-types/components/post-locked-modal/index.d.ts +2 -2
- package/build-types/components/post-locked-modal/index.d.ts.map +1 -1
- package/build-types/components/post-revisions-preview/block-diff.d.ts.map +1 -1
- package/build-types/components/post-template/block-theme.d.ts +1 -3
- package/build-types/components/post-template/block-theme.d.ts.map +1 -1
- package/build-types/components/post-template/hooks.d.ts +1 -0
- package/build-types/components/post-template/hooks.d.ts.map +1 -1
- package/build-types/components/post-template/panel.d.ts.map +1 -1
- package/build-types/components/provider/use-block-editor-settings.d.ts.map +1 -1
- package/build-types/components/revision-block-diff/index.d.ts +6 -0
- package/build-types/components/revision-block-diff/index.d.ts.map +1 -0
- package/build-types/components/sidebar/dataform-post-summary.d.ts.map +1 -1
- package/build-types/components/sidebar/index.d.ts.map +1 -1
- package/build-types/components/sync-connection-error-modal/index.d.ts +22 -0
- package/build-types/components/sync-connection-error-modal/index.d.ts.map +1 -0
- package/build-types/components/sync-connection-error-modal/use-retry-countdown.d.ts +11 -0
- package/build-types/components/sync-connection-error-modal/use-retry-countdown.d.ts.map +1 -0
- package/build-types/store/actions.d.ts.map +1 -1
- package/build-types/utils/media-finalize/index.d.ts +2 -0
- package/build-types/utils/media-finalize/index.d.ts.map +1 -0
- package/build-types/utils/sync-error-messages.d.ts +17 -3
- package/build-types/utils/sync-error-messages.d.ts.map +1 -1
- package/package.json +44 -44
- package/src/components/collaborators-overlay/compute-selection.ts +13 -13
- package/src/components/collaborators-overlay/overlay.tsx +19 -0
- package/src/components/collaborators-overlay/timing-utils.ts +30 -0
- package/src/components/collaborators-overlay/use-render-cursors.ts +1 -1
- package/src/components/post-locked-modal/index.js +21 -3
- package/src/components/post-revisions-preview/block-diff.js +59 -20
- package/src/components/post-revisions-preview/diff-markers.js +2 -2
- package/src/components/post-revisions-preview/revisions-canvas.js +1 -1
- package/src/components/post-revisions-preview/test/block-diff.js +69 -31
- package/src/components/post-template/block-theme.js +4 -1
- package/src/components/post-template/hooks.js +42 -0
- package/src/components/post-template/panel.js +5 -59
- package/src/components/provider/use-block-editor-settings.js +2 -0
- package/src/components/revision-block-diff/index.js +74 -0
- package/src/components/revision-block-diff/style.scss +13 -0
- package/src/components/sidebar/dataform-post-summary.js +37 -13
- package/src/components/sidebar/index.js +2 -0
- package/src/components/sync-connection-error-modal/index.tsx +265 -0
- package/src/components/sync-connection-error-modal/style.scss +14 -0
- package/src/components/sync-connection-error-modal/use-retry-countdown.ts +57 -0
- package/src/components/visual-editor/index.js +2 -2
- package/src/store/actions.js +1 -4
- package/src/style.scss +2 -1
- package/src/utils/media-finalize/index.js +11 -0
- package/src/utils/media-finalize/test/index.js +34 -0
- package/src/utils/sync-error-messages.ts +72 -0
- package/src/utils/test/sync-error-messages.js +9 -32
- package/build/components/sync-connection-modal/index.cjs.map +0 -7
- package/build/components/sync-connection-modal/use-retry-countdown.cjs.map +0 -7
- package/build-module/components/sync-connection-modal/index.mjs +0 -170
- package/build-module/components/sync-connection-modal/index.mjs.map +0 -7
- package/build-module/components/sync-connection-modal/use-retry-countdown.mjs +0 -49
- package/build-module/components/sync-connection-modal/use-retry-countdown.mjs.map +0 -7
- package/build-types/components/sync-connection-modal/index.d.ts +0 -8
- package/build-types/components/sync-connection-modal/index.d.ts.map +0 -1
- package/build-types/components/sync-connection-modal/use-retry-countdown.d.ts +0 -9
- package/build-types/components/sync-connection-modal/use-retry-countdown.d.ts.map +0 -1
- package/src/components/sync-connection-modal/index.js +0 -206
- package/src/components/sync-connection-modal/style.scss +0 -14
- package/src/components/sync-connection-modal/use-retry-countdown.js +0 -70
- package/src/utils/sync-error-messages.js +0 -58
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WordPress dependencies
|
|
3
|
-
*/
|
|
4
|
-
import { useSelect } from '@wordpress/data';
|
|
5
|
-
import { store as coreStore } from '@wordpress/core-data';
|
|
6
|
-
|
|
7
1
|
/**
|
|
8
2
|
* Internal dependencies
|
|
9
3
|
*/
|
|
10
|
-
import {
|
|
4
|
+
import { usePostTemplatePanelMode } from './hooks';
|
|
11
5
|
import ClassicThemeControl from './classic-theme';
|
|
12
6
|
import BlockThemeControl from './block-theme';
|
|
13
7
|
|
|
@@ -17,60 +11,12 @@ import BlockThemeControl from './block-theme';
|
|
|
17
11
|
* @return {React.ReactNode} The rendered PostTemplatePanel component.
|
|
18
12
|
*/
|
|
19
13
|
export default function PostTemplatePanel() {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
select( editorStore );
|
|
23
|
-
return {
|
|
24
|
-
templateId: getCurrentTemplateId(),
|
|
25
|
-
isBlockTheme: getEditorSettings().__unstableIsBlockBasedTheme,
|
|
26
|
-
};
|
|
27
|
-
}, [] );
|
|
28
|
-
|
|
29
|
-
const isVisible = useSelect( ( select ) => {
|
|
30
|
-
const postTypeSlug = select( editorStore ).getCurrentPostType();
|
|
31
|
-
const postType = select( coreStore ).getPostType( postTypeSlug );
|
|
32
|
-
if ( ! postType?.viewable ) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const settings = select( editorStore ).getEditorSettings();
|
|
37
|
-
const hasTemplates =
|
|
38
|
-
!! settings.availableTemplates &&
|
|
39
|
-
Object.keys( settings.availableTemplates ).length > 0;
|
|
40
|
-
if ( hasTemplates ) {
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if ( ! settings.supportsTemplateMode ) {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const canCreateTemplates =
|
|
49
|
-
select( coreStore ).canUser( 'create', {
|
|
50
|
-
kind: 'postType',
|
|
51
|
-
name: 'wp_template',
|
|
52
|
-
} ) ?? false;
|
|
53
|
-
return canCreateTemplates;
|
|
54
|
-
}, [] );
|
|
55
|
-
|
|
56
|
-
const canViewTemplates = useSelect(
|
|
57
|
-
( select ) => {
|
|
58
|
-
return isVisible
|
|
59
|
-
? select( coreStore ).canUser( 'read', {
|
|
60
|
-
kind: 'postType',
|
|
61
|
-
name: 'wp_template',
|
|
62
|
-
} )
|
|
63
|
-
: false;
|
|
64
|
-
},
|
|
65
|
-
[ isVisible ]
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
if ( ( ! isBlockTheme || ! canViewTemplates ) && isVisible ) {
|
|
14
|
+
const mode = usePostTemplatePanelMode();
|
|
15
|
+
if ( mode === 'classic' ) {
|
|
69
16
|
return <ClassicThemeControl />;
|
|
70
17
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return <BlockThemeControl id={ templateId } />;
|
|
18
|
+
if ( mode === 'block-theme' ) {
|
|
19
|
+
return <BlockThemeControl />;
|
|
74
20
|
}
|
|
75
21
|
return null;
|
|
76
22
|
}
|
|
@@ -25,6 +25,7 @@ import inserterMediaCategories from '../media-categories';
|
|
|
25
25
|
import { mediaUpload } from '../../utils';
|
|
26
26
|
import mediaUploadOnSuccess from '../../utils/media-upload/on-success';
|
|
27
27
|
import { default as mediaSideload } from '../../utils/media-sideload';
|
|
28
|
+
import { default as mediaFinalize } from '../../utils/media-finalize';
|
|
28
29
|
import { store as editorStore } from '../../store';
|
|
29
30
|
import { unlock } from '../../lock-unlock';
|
|
30
31
|
import { useGlobalStylesContext } from '../global-styles-provider';
|
|
@@ -343,6 +344,7 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) {
|
|
|
343
344
|
? mediaUploadOnSuccess
|
|
344
345
|
: undefined,
|
|
345
346
|
mediaSideload: hasUploadPermissions ? mediaSideload : undefined,
|
|
347
|
+
mediaFinalize: hasUploadPermissions ? mediaFinalize : undefined,
|
|
346
348
|
__experimentalBlockPatterns: blockPatterns,
|
|
347
349
|
[ selectBlockPatternsKey ]: ( select ) => {
|
|
348
350
|
const { hasFinishedResolution, getBlockPatternsForPostType } =
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { PanelBody } from '@wordpress/components';
|
|
5
|
+
import { store as blockEditorStore } from '@wordpress/block-editor';
|
|
6
|
+
import { useSelect } from '@wordpress/data';
|
|
7
|
+
import { __ } from '@wordpress/i18n';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Internal dependencies
|
|
11
|
+
*/
|
|
12
|
+
import PostPanelRow from '../post-panel-row';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Panel that shows changed block attributes for the selected block
|
|
16
|
+
* when viewing revisions.
|
|
17
|
+
*/
|
|
18
|
+
export default function RevisionBlockDiffPanel() {
|
|
19
|
+
const { block } = useSelect( ( select ) => {
|
|
20
|
+
const { getSelectedBlock } = select( blockEditorStore );
|
|
21
|
+
return {
|
|
22
|
+
block: getSelectedBlock(),
|
|
23
|
+
};
|
|
24
|
+
}, [] );
|
|
25
|
+
|
|
26
|
+
if ( ! block ) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const diffInfo = block.attributes?.__revisionDiffStatus;
|
|
31
|
+
const changedAttributes = diffInfo?.changedAttributes;
|
|
32
|
+
|
|
33
|
+
if ( ! changedAttributes ) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const fields = Object.entries( changedAttributes ).map(
|
|
38
|
+
( [ key, parts ] ) => (
|
|
39
|
+
<PostPanelRow key={ key } label={ key }>
|
|
40
|
+
<span className="editor-revision-fields-diff__value">
|
|
41
|
+
{ parts.map( ( part, index ) => {
|
|
42
|
+
if ( part.added ) {
|
|
43
|
+
return (
|
|
44
|
+
<ins
|
|
45
|
+
key={ index }
|
|
46
|
+
className="editor-revision-fields-diff__added"
|
|
47
|
+
>
|
|
48
|
+
{ part.value }
|
|
49
|
+
</ins>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
if ( part.removed ) {
|
|
53
|
+
return (
|
|
54
|
+
<del
|
|
55
|
+
key={ index }
|
|
56
|
+
className="editor-revision-fields-diff__removed"
|
|
57
|
+
>
|
|
58
|
+
{ part.value }
|
|
59
|
+
</del>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return <span key={ index }>{ part.value }</span>;
|
|
63
|
+
} ) }
|
|
64
|
+
</span>
|
|
65
|
+
</PostPanelRow>
|
|
66
|
+
)
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<PanelBody title={ __( 'Changed attributes' ) } initialOpen>
|
|
71
|
+
{ fields }
|
|
72
|
+
</PanelBody>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
.editor-revision-fields-diff__value {
|
|
2
|
+
word-break: break-word;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.editor-revision-fields-diff__added {
|
|
6
|
+
background-color: color-mix(in srgb, currentColor 5%, #00a32a 15%);
|
|
7
|
+
text-decoration: none;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.editor-revision-fields-diff__removed {
|
|
11
|
+
text-decoration: line-through;
|
|
12
|
+
color: #d63638;
|
|
13
|
+
}
|
|
@@ -17,6 +17,7 @@ import { store as editorStore } from '../../store';
|
|
|
17
17
|
import PostTrash from '../post-trash';
|
|
18
18
|
import usePostFields from '../post-fields';
|
|
19
19
|
import { unlock } from '../../lock-unlock';
|
|
20
|
+
import { usePostTemplatePanelMode } from '../post-template/hooks';
|
|
20
21
|
|
|
21
22
|
const form = {
|
|
22
23
|
layout: {
|
|
@@ -93,7 +94,8 @@ export default function DataFormPostSummary( { onActionPerformed } ) {
|
|
|
93
94
|
[ postType, postId ]
|
|
94
95
|
);
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
const templatePanelMode = usePostTemplatePanelMode();
|
|
98
|
+
|
|
97
99
|
const availableTemplates = useSelect( ( select ) => {
|
|
98
100
|
if ( select( coreDataStore ).getCurrentTheme()?.is_block_theme ) {
|
|
99
101
|
return null;
|
|
@@ -119,18 +121,40 @@ export default function DataFormPostSummary( { onActionPerformed } ) {
|
|
|
119
121
|
const _fields = usePostFields( { postType } );
|
|
120
122
|
const fields = useMemo(
|
|
121
123
|
() =>
|
|
122
|
-
_fields
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
124
|
+
_fields
|
|
125
|
+
?.map( ( field ) => {
|
|
126
|
+
if ( field.id === 'status' ) {
|
|
127
|
+
return {
|
|
128
|
+
...field,
|
|
129
|
+
elements: field.elements.filter(
|
|
130
|
+
( element ) => element.value !== 'trash'
|
|
131
|
+
),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
if ( field.id === 'template' ) {
|
|
135
|
+
// `usePostTemplatePanelMode` is reused in the Post Template panel to match
|
|
136
|
+
// the existing behavior. If the panel rendered nothing we should exclude the
|
|
137
|
+
// template field from the form.
|
|
138
|
+
if ( ! templatePanelMode ) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
// In classic themes without available templates we need to make the field read-only.
|
|
142
|
+
if (
|
|
143
|
+
templatePanelMode === 'classic' &&
|
|
144
|
+
Object.keys( availableTemplates ?? {} ).length === 0
|
|
145
|
+
) {
|
|
146
|
+
return {
|
|
147
|
+
...field,
|
|
148
|
+
readOnly: true,
|
|
149
|
+
render: () => __( 'Default template' ),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return field;
|
|
153
|
+
}
|
|
154
|
+
return field;
|
|
155
|
+
} )
|
|
156
|
+
.filter( Boolean ),
|
|
157
|
+
[ _fields, templatePanelMode, availableTemplates ]
|
|
134
158
|
);
|
|
135
159
|
|
|
136
160
|
const onChange = ( edits ) => {
|
|
@@ -32,6 +32,7 @@ import SidebarHeader from './header';
|
|
|
32
32
|
import TemplateContentPanel from '../template-content-panel';
|
|
33
33
|
import TemplatePartContentPanel from '../template-part-content-panel';
|
|
34
34
|
import { MediaMetadataPanel } from '../media';
|
|
35
|
+
import RevisionBlockDiffPanel from '../revision-block-diff';
|
|
35
36
|
import useAutoSwitchEditorSidebars from '../provider/use-auto-switch-editor-sidebars';
|
|
36
37
|
import { sidebars } from './constants';
|
|
37
38
|
import { unlock } from '../../lock-unlock';
|
|
@@ -144,6 +145,7 @@ const SidebarContent = ( {
|
|
|
144
145
|
{ ! isAttachment && (
|
|
145
146
|
<Tabs.TabPanel tabId={ sidebars.block } focusable={ false }>
|
|
146
147
|
<BlockInspector />
|
|
148
|
+
{ isRevisionsMode && <RevisionBlockDiffPanel /> }
|
|
147
149
|
</Tabs.TabPanel>
|
|
148
150
|
) }
|
|
149
151
|
</Tabs.Context.Provider>
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useSelect, select } from '@wordpress/data';
|
|
5
|
+
import { useCopyToClipboard } from '@wordpress/compose';
|
|
6
|
+
// @ts-ignore No exported types.
|
|
7
|
+
import { serialize } from '@wordpress/blocks';
|
|
8
|
+
import {
|
|
9
|
+
store as coreDataStore,
|
|
10
|
+
privateApis as coreDataPrivateApis,
|
|
11
|
+
type ConnectionError,
|
|
12
|
+
} from '@wordpress/core-data';
|
|
13
|
+
// @ts-expect-error - No type declarations available for @wordpress/block-editor
|
|
14
|
+
// prettier-ignore
|
|
15
|
+
import { privateApis, store as blockEditorStore } from '@wordpress/block-editor';
|
|
16
|
+
import {
|
|
17
|
+
Button,
|
|
18
|
+
Modal,
|
|
19
|
+
withFilters,
|
|
20
|
+
__experimentalHStack as HStack,
|
|
21
|
+
__experimentalVStack as VStack,
|
|
22
|
+
} from '@wordpress/components';
|
|
23
|
+
import { useState, useEffect } from '@wordpress/element';
|
|
24
|
+
import { __, sprintf, _n } from '@wordpress/i18n';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Internal dependencies
|
|
28
|
+
*/
|
|
29
|
+
import { getSyncErrorMessages } from '../../utils/sync-error-messages';
|
|
30
|
+
import { store as editorStore } from '../../store';
|
|
31
|
+
import { unlock } from '../../lock-unlock';
|
|
32
|
+
import { useRetryCountdown } from './use-retry-countdown';
|
|
33
|
+
|
|
34
|
+
const { BlockCanvasCover } = unlock( privateApis );
|
|
35
|
+
const { retrySyncConnection } = unlock( coreDataPrivateApis );
|
|
36
|
+
|
|
37
|
+
// Debounce time for initial disconnected status to allow connection to establish.
|
|
38
|
+
const INITIAL_DISCONNECTED_DEBOUNCE_MS = 5000;
|
|
39
|
+
|
|
40
|
+
// Debounce time for showing the disconnect dialog after the intial connection,
|
|
41
|
+
// allowing brief network interruptions to resolve.
|
|
42
|
+
const DISCONNECTED_DEBOUNCE_MS = 2000;
|
|
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
|
+
/**
|
|
179
|
+
* Sync connection modal that displays when any entity reports a disconnection.
|
|
180
|
+
* Uses BlockCanvasCover.Fill to render in the block canvas.
|
|
181
|
+
*
|
|
182
|
+
* @return The modal component or null if not disconnected.
|
|
183
|
+
*/
|
|
184
|
+
export function SyncConnectionErrorModal() {
|
|
185
|
+
const [ hasInitialized, setHasInitialized ] = useState( false );
|
|
186
|
+
const [ showModal, setShowModal ] = useState( false );
|
|
187
|
+
|
|
188
|
+
const { connectionStatus, isCollaborationEnabled, postType } = useSelect(
|
|
189
|
+
( selectFn ) => {
|
|
190
|
+
const currentPostType =
|
|
191
|
+
selectFn( editorStore ).getCurrentPostType();
|
|
192
|
+
return {
|
|
193
|
+
connectionStatus:
|
|
194
|
+
selectFn( coreDataStore ).getSyncConnectionStatus() || null,
|
|
195
|
+
isCollaborationEnabled:
|
|
196
|
+
selectFn(
|
|
197
|
+
editorStore
|
|
198
|
+
).isCollaborationEnabledForCurrentPost(),
|
|
199
|
+
postType: currentPostType
|
|
200
|
+
? selectFn( coreDataStore ).getPostType( currentPostType )
|
|
201
|
+
: null,
|
|
202
|
+
};
|
|
203
|
+
},
|
|
204
|
+
[]
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const { onManualRetry, secondsRemaining } =
|
|
208
|
+
useRetryCountdown( connectionStatus );
|
|
209
|
+
|
|
210
|
+
const isConnected = 'connected' === connectionStatus?.status;
|
|
211
|
+
|
|
212
|
+
// Set hasInitialized after a debounce to give extra time on initial load.
|
|
213
|
+
useEffect( () => {
|
|
214
|
+
const timeout = setTimeout( () => {
|
|
215
|
+
setHasInitialized( true );
|
|
216
|
+
}, INITIAL_DISCONNECTED_DEBOUNCE_MS );
|
|
217
|
+
|
|
218
|
+
return () => clearTimeout( timeout );
|
|
219
|
+
}, [] );
|
|
220
|
+
|
|
221
|
+
useEffect( () => {
|
|
222
|
+
if ( isConnected ) {
|
|
223
|
+
setShowModal( false );
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const timeout = setTimeout( () => {
|
|
228
|
+
setShowModal( true );
|
|
229
|
+
}, DISCONNECTED_DEBOUNCE_MS );
|
|
230
|
+
|
|
231
|
+
return () => clearTimeout( timeout );
|
|
232
|
+
}, [ isConnected ] );
|
|
233
|
+
|
|
234
|
+
if ( ! isCollaborationEnabled || ! hasInitialized || ! showModal ) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const error =
|
|
239
|
+
connectionStatus && 'error' in connectionStatus
|
|
240
|
+
? connectionStatus?.error
|
|
241
|
+
: undefined;
|
|
242
|
+
const manualRetry =
|
|
243
|
+
connectionStatus &&
|
|
244
|
+
'canManuallyRetry' in connectionStatus &&
|
|
245
|
+
connectionStatus.canManuallyRetry
|
|
246
|
+
? () => {
|
|
247
|
+
onManualRetry();
|
|
248
|
+
retrySyncConnection();
|
|
249
|
+
}
|
|
250
|
+
: undefined;
|
|
251
|
+
const messages = getSyncErrorMessages( error );
|
|
252
|
+
|
|
253
|
+
return (
|
|
254
|
+
<BlockCanvasCover.Fill>
|
|
255
|
+
<FilteredSyncConnectionErrorModal
|
|
256
|
+
description={ messages.description }
|
|
257
|
+
error={ error }
|
|
258
|
+
manualRetry={ manualRetry }
|
|
259
|
+
postType={ postType }
|
|
260
|
+
secondsRemainingUntilAutoRetry={ secondsRemaining }
|
|
261
|
+
title={ messages.title }
|
|
262
|
+
/>
|
|
263
|
+
</BlockCanvasCover.Fill>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
@use "@wordpress/base-styles/colors" as *;
|
|
2
|
+
@use "@wordpress/base-styles/z-index" as *;
|
|
3
|
+
|
|
4
|
+
.editor-sync-connection-error-modal {
|
|
5
|
+
z-index: z-index(".editor-sync-connection-error-modal");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.editor-sync-connection-error-modal p {
|
|
9
|
+
margin: 0;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.editor-sync-connection-error-modal__retry-countdown {
|
|
13
|
+
color: $gray-700;
|
|
14
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { ConnectionStatus } from '@wordpress/core-data';
|
|
5
|
+
import { useState, useEffect } from '@wordpress/element';
|
|
6
|
+
|
|
7
|
+
interface UseRetryCountdownResult {
|
|
8
|
+
onManualRetry: () => void;
|
|
9
|
+
secondsRemaining?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function useRetryCountdown(
|
|
13
|
+
connectionStatus?: ConnectionStatus | null
|
|
14
|
+
): UseRetryCountdownResult {
|
|
15
|
+
const [ secondsRemaining, setSecondsRemaining ] = useState< number >();
|
|
16
|
+
|
|
17
|
+
useEffect( () => {
|
|
18
|
+
if ( ! connectionStatus ) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Only clear countdown when explicitly connected.
|
|
23
|
+
if ( 'connected' === connectionStatus.status ) {
|
|
24
|
+
setSecondsRemaining( undefined );
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// For transient states (e.g. 'connecting' during a retry attempt)
|
|
29
|
+
// or when retryInMs is not yet available, keep the previous
|
|
30
|
+
// countdown value to avoid a brief flash.
|
|
31
|
+
if (
|
|
32
|
+
'disconnected' !== connectionStatus.status ||
|
|
33
|
+
! connectionStatus.willAutoRetryInMs
|
|
34
|
+
) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { willAutoRetryInMs: retryInMs } = connectionStatus;
|
|
39
|
+
const retryAt = Date.now() + retryInMs;
|
|
40
|
+
setSecondsRemaining( Math.ceil( retryInMs / 1000 ) );
|
|
41
|
+
|
|
42
|
+
const intervalId = setInterval( () => {
|
|
43
|
+
const remaining = Math.ceil( ( retryAt - Date.now() ) / 1000 );
|
|
44
|
+
setSecondsRemaining( Math.max( 0, remaining ) );
|
|
45
|
+
if ( remaining <= 0 ) {
|
|
46
|
+
clearInterval( intervalId );
|
|
47
|
+
}
|
|
48
|
+
}, 1000 );
|
|
49
|
+
|
|
50
|
+
return () => clearInterval( intervalId );
|
|
51
|
+
}, [ connectionStatus ] );
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
onManualRetry: () => setSecondsRemaining( 0 ),
|
|
55
|
+
secondsRemaining,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
import { useZoomOutModeExit } from './use-zoom-out-mode-exit';
|
|
42
42
|
import { usePaddingAppender } from './use-padding-appender';
|
|
43
43
|
import { useEditContentOnlySectionExit } from './use-edit-content-only-section-exit';
|
|
44
|
-
import {
|
|
44
|
+
import { SyncConnectionErrorModal } from '../sync-connection-error-modal';
|
|
45
45
|
|
|
46
46
|
const {
|
|
47
47
|
LayoutStyle,
|
|
@@ -427,7 +427,7 @@ function VisualEditor( {
|
|
|
427
427
|
}
|
|
428
428
|
) }
|
|
429
429
|
>
|
|
430
|
-
<
|
|
430
|
+
<SyncConnectionErrorModal />
|
|
431
431
|
<ResizableEditor enableResizing={ enableResizing } height="100%">
|
|
432
432
|
<BlockCanvas
|
|
433
433
|
shouldIframe={ ! disableIframe }
|
package/src/store/actions.js
CHANGED
|
@@ -189,10 +189,7 @@ export const savePost =
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
const content = select.getEditedPostContent();
|
|
192
|
-
|
|
193
|
-
if ( ! options.isAutosave ) {
|
|
194
|
-
dispatch.editPost( { content }, { undoIgnore: true } );
|
|
195
|
-
}
|
|
192
|
+
dispatch.editPost( { content }, { undoIgnore: true } );
|
|
196
193
|
|
|
197
194
|
const previousRecord = select.getCurrentPost();
|
|
198
195
|
let edits = {
|
package/src/style.scss
CHANGED
|
@@ -39,13 +39,14 @@
|
|
|
39
39
|
@use "./components/post-panel-section/style.scss" as *;
|
|
40
40
|
@use "./components/post-publish-panel/style.scss" as *;
|
|
41
41
|
@use "./components/post-revisions-preview/style.scss" as *;
|
|
42
|
+
@use "./components/revision-block-diff/style.scss" as *;
|
|
42
43
|
@use "./components/post-saved-state/style.scss" as *;
|
|
43
44
|
@use "./components/post-schedule/style.scss" as *;
|
|
44
45
|
@use "./components/post-status/style.scss" as *;
|
|
45
46
|
@use "./components/post-sticky/style.scss" as *;
|
|
46
47
|
@use "./components/post-sync-status/style.scss" as *;
|
|
47
48
|
@use "./components/post-taxonomies/style.scss" as *;
|
|
48
|
-
@use "./components/sync-connection-modal/style.scss" as *;
|
|
49
|
+
@use "./components/sync-connection-error-modal/style.scss" as *;
|
|
49
50
|
@use "./components/post-template/style.scss" as *;
|
|
50
51
|
@use "./components/post-text-editor/style.scss" as *;
|
|
51
52
|
@use "./components/post-title/style.scss" as *;
|