@atlaskit/editor-plugin-synced-block 5.3.33 → 5.3.35
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 +15 -0
- package/dist/cjs/editor-commands/index.js +2 -4
- package/dist/cjs/nodeviews/bodiedSyncedBlock.js +14 -7
- package/dist/cjs/pm-plugins/main.js +273 -110
- package/dist/cjs/pm-plugins/utils/handle-bodied-sync-block-creation.js +2 -1
- package/dist/cjs/pm-plugins/utils/utils.js +13 -1
- package/dist/es2019/editor-commands/index.js +10 -10
- package/dist/es2019/nodeviews/bodiedSyncedBlock.js +14 -7
- package/dist/es2019/pm-plugins/main.js +263 -116
- package/dist/es2019/pm-plugins/utils/handle-bodied-sync-block-creation.js +2 -1
- package/dist/es2019/pm-plugins/utils/utils.js +13 -0
- package/dist/esm/editor-commands/index.js +3 -5
- package/dist/esm/nodeviews/bodiedSyncedBlock.js +13 -6
- package/dist/esm/pm-plugins/main.js +273 -110
- package/dist/esm/pm-plugins/utils/handle-bodied-sync-block-creation.js +2 -1
- package/dist/esm/pm-plugins/utils/utils.js +13 -0
- package/dist/types/nodeviews/bodiedSyncedBlock.d.ts +22 -1
- package/dist/types/pm-plugins/utils/utils.d.ts +5 -0
- package/dist/types-ts4.5/nodeviews/bodiedSyncedBlock.d.ts +22 -1
- package/dist/types-ts4.5/pm-plugins/utils/utils.d.ts +5 -0
- package/package.json +4 -1
|
@@ -5,15 +5,17 @@ import ReactNodeView from '@atlaskit/editor-common/react-node-view';
|
|
|
5
5
|
import { BodiedSyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block';
|
|
6
6
|
import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
7
7
|
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
8
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
8
9
|
import { BodiedSyncBlockWrapper } from '../ui/BodiedSyncBlockWrapper';
|
|
9
10
|
const toDOM = () => ['div', {
|
|
10
11
|
class: BodiedSyncBlockSharedCssClassName.content,
|
|
11
12
|
contenteditable: true
|
|
12
13
|
}, 0];
|
|
13
|
-
class BodiedSyncBlock extends ReactNodeView {
|
|
14
|
+
export class BodiedSyncBlock extends ReactNodeView {
|
|
14
15
|
constructor(props) {
|
|
15
16
|
super(props.node, props.view, props.getPos, props.portalProviderAPI, props.eventDispatcher, props);
|
|
16
17
|
this.api = props.api;
|
|
18
|
+
this.syncBlockStore = props.syncBlockStore;
|
|
17
19
|
this.handleConnectivityModeChange();
|
|
18
20
|
this.handleViewModeChange();
|
|
19
21
|
}
|
|
@@ -62,8 +64,9 @@ class BodiedSyncBlock extends ReactNodeView {
|
|
|
62
64
|
return domRef;
|
|
63
65
|
}
|
|
64
66
|
render(_props, forwardRef) {
|
|
65
|
-
var _this$api5, _this$api5$syncedBloc, _this$api5$syncedBloc2, _this$api6, _this$api6$analytics;
|
|
66
|
-
|
|
67
|
+
var _this$api$syncedBlock, _this$api5, _this$api5$syncedBloc, _this$api5$syncedBloc2, _this$api6, _this$api6$analytics;
|
|
68
|
+
// Use passed syncBlockStore for SSR where sharedState.currentState() is delayed
|
|
69
|
+
const syncBlockStore = (_this$api$syncedBlock = (_this$api5 = this.api) === null || _this$api5 === void 0 ? void 0 : (_this$api5$syncedBloc = _this$api5.syncedBlock.sharedState) === null || _this$api5$syncedBloc === void 0 ? void 0 : (_this$api5$syncedBloc2 = _this$api5$syncedBloc.currentState()) === null || _this$api5$syncedBloc2 === void 0 ? void 0 : _this$api5$syncedBloc2.syncBlockStore) !== null && _this$api$syncedBlock !== void 0 ? _this$api$syncedBlock : this.syncBlockStore;
|
|
67
70
|
if (!syncBlockStore) {
|
|
68
71
|
return null;
|
|
69
72
|
}
|
|
@@ -82,12 +85,14 @@ class BodiedSyncBlock extends ReactNodeView {
|
|
|
82
85
|
dom,
|
|
83
86
|
contentDOM
|
|
84
87
|
} = DOMSerializer.renderSpec(document, toDOM());
|
|
85
|
-
|
|
88
|
+
// In SSR, the first check won't work, so fallback to nodeType check
|
|
89
|
+
if (dom instanceof HTMLElement || dom.nodeType === 1 && fg('platform_synced_block_patch_5')) {
|
|
86
90
|
this.updateContentEditable({
|
|
87
91
|
contentDOM
|
|
88
92
|
});
|
|
93
|
+
// eslint-disable-next-line @atlaskit/editor/no-as-casting
|
|
89
94
|
return {
|
|
90
|
-
dom,
|
|
95
|
+
dom: dom,
|
|
91
96
|
contentDOM
|
|
92
97
|
};
|
|
93
98
|
}
|
|
@@ -105,7 +110,8 @@ class BodiedSyncBlock extends ReactNodeView {
|
|
|
105
110
|
export const bodiedSyncBlockNodeView = ({
|
|
106
111
|
pluginOptions,
|
|
107
112
|
pmPluginFactoryParams,
|
|
108
|
-
api
|
|
113
|
+
api,
|
|
114
|
+
syncBlockStore
|
|
109
115
|
}) => (node, view, getPos) => {
|
|
110
116
|
const {
|
|
111
117
|
portalProviderAPI,
|
|
@@ -118,6 +124,7 @@ export const bodiedSyncBlockNodeView = ({
|
|
|
118
124
|
view,
|
|
119
125
|
getPos,
|
|
120
126
|
portalProviderAPI,
|
|
121
|
-
eventDispatcher
|
|
127
|
+
eventDispatcher,
|
|
128
|
+
syncBlockStore
|
|
122
129
|
}).init();
|
|
123
130
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
1
2
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
2
3
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
3
4
|
import { createSelectionClickHandler } from '@atlaskit/editor-common/selection';
|
|
@@ -9,6 +10,7 @@ import { DecorationSet, Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
|
9
10
|
import { convertPMNodesToSyncBlockNodes, rebaseTransaction } from '@atlaskit/editor-synced-block-provider';
|
|
10
11
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
11
12
|
import { lazyBodiedSyncBlockView } from '../nodeviews/bodiedLazySyncedBlock';
|
|
13
|
+
import { bodiedSyncBlockNodeView } from '../nodeviews/bodiedSyncedBlock';
|
|
12
14
|
import { SyncBlock as SyncBlockView } from '../nodeviews/syncedBlock';
|
|
13
15
|
import { FLAG_ID } from '../types';
|
|
14
16
|
import { handleBodiedSyncBlockCreation } from './utils/handle-bodied-sync-block-creation';
|
|
@@ -16,13 +18,15 @@ import { handleBodiedSyncBlockRemoval } from './utils/handle-bodied-sync-block-r
|
|
|
16
18
|
import { shouldIgnoreDomEvent } from './utils/ignore-dom-event';
|
|
17
19
|
import { calculateDecorations } from './utils/selection-decorations';
|
|
18
20
|
import { hasEditInSyncBlock, trackSyncBlocks } from './utils/track-sync-blocks';
|
|
19
|
-
import { wasInlineExtensionInsertedInBodiedSyncBlock, sliceFullyContainsNode } from './utils/utils';
|
|
21
|
+
import { deferDispatch, wasInlineExtensionInsertedInBodiedSyncBlock, sliceFullyContainsNode } from './utils/utils';
|
|
20
22
|
export const syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
|
|
21
23
|
const mapRetryCreationPosMap = (oldMap, newRetryCreationPos, mapPos) => {
|
|
22
24
|
const resourceId = newRetryCreationPos === null || newRetryCreationPos === void 0 ? void 0 : newRetryCreationPos.resourceId;
|
|
23
25
|
const newMap = new Map(oldMap);
|
|
24
26
|
if (resourceId) {
|
|
25
|
-
const
|
|
27
|
+
const {
|
|
28
|
+
pos
|
|
29
|
+
} = newRetryCreationPos;
|
|
26
30
|
if (!pos) {
|
|
27
31
|
newMap.delete(resourceId);
|
|
28
32
|
} else {
|
|
@@ -41,18 +45,15 @@ const mapRetryCreationPosMap = (oldMap, newRetryCreationPos, mapPos) => {
|
|
|
41
45
|
return newMap;
|
|
42
46
|
};
|
|
43
47
|
const showCopiedFlag = api => {
|
|
44
|
-
|
|
45
|
-
setTimeout(() => {
|
|
48
|
+
deferDispatch(() => {
|
|
46
49
|
api === null || api === void 0 ? void 0 : api.core.actions.execute(({
|
|
47
50
|
tr
|
|
48
|
-
}) => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
});
|
|
55
|
-
}, 0);
|
|
51
|
+
}) => tr.setMeta(syncedBlockPluginKey, {
|
|
52
|
+
activeFlag: {
|
|
53
|
+
id: FLAG_ID.SYNC_BLOCK_COPIED
|
|
54
|
+
}
|
|
55
|
+
}));
|
|
56
|
+
});
|
|
56
57
|
};
|
|
57
58
|
const showInlineExtensionInSyncBlockWarningIfNeeded = (tr, state, api, inlineExtensionFlagShown) => {
|
|
58
59
|
var _api$connectivity, _api$connectivity$sha;
|
|
@@ -66,18 +67,15 @@ const showInlineExtensionInSyncBlockWarningIfNeeded = (tr, state, api, inlineExt
|
|
|
66
67
|
// Only show the flag on the first instance per sync block (same as UNPUBLISHED_SYNC_BLOCK_PASTED)
|
|
67
68
|
if (resourceId && !inlineExtensionFlagShown.has(resourceId)) {
|
|
68
69
|
inlineExtensionFlagShown.add(resourceId);
|
|
69
|
-
|
|
70
|
-
setTimeout(() => {
|
|
70
|
+
deferDispatch(() => {
|
|
71
71
|
api === null || api === void 0 ? void 0 : api.core.actions.execute(({
|
|
72
72
|
tr
|
|
73
|
-
}) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
});
|
|
80
|
-
}, 0);
|
|
73
|
+
}) => tr.setMeta(syncedBlockPluginKey, {
|
|
74
|
+
activeFlag: {
|
|
75
|
+
id: FLAG_ID.INLINE_EXTENSION_IN_SYNC_BLOCK
|
|
76
|
+
}
|
|
77
|
+
}));
|
|
78
|
+
});
|
|
81
79
|
}
|
|
82
80
|
};
|
|
83
81
|
const getDeleteReason = tr => {
|
|
@@ -87,37 +85,166 @@ const getDeleteReason = tr => {
|
|
|
87
85
|
}
|
|
88
86
|
return reason;
|
|
89
87
|
};
|
|
88
|
+
const filterTransactionOnline = ({
|
|
89
|
+
tr,
|
|
90
|
+
state,
|
|
91
|
+
syncBlockStore,
|
|
92
|
+
api,
|
|
93
|
+
confirmationTransactionRef,
|
|
94
|
+
bodiedSyncBlockRemoved,
|
|
95
|
+
bodiedSyncBlockAdded,
|
|
96
|
+
inlineExtensionFlagShown
|
|
97
|
+
}) => {
|
|
98
|
+
const {
|
|
99
|
+
removed: syncBlockRemoved,
|
|
100
|
+
added: syncBlockAdded
|
|
101
|
+
} = trackSyncBlocks(node => node.type.name === 'syncBlock', tr, state);
|
|
102
|
+
syncBlockRemoved.forEach(syncBlock => {
|
|
103
|
+
var _api$analytics, _api$analytics$action;
|
|
104
|
+
api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : (_api$analytics$action = _api$analytics.actions) === null || _api$analytics$action === void 0 ? void 0 : _api$analytics$action.fireAnalyticsEvent({
|
|
105
|
+
action: ACTION.DELETED,
|
|
106
|
+
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
107
|
+
actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_DELETE,
|
|
108
|
+
attributes: {
|
|
109
|
+
resourceId: syncBlock.attrs.resourceId,
|
|
110
|
+
blockInstanceId: syncBlock.attrs.localId
|
|
111
|
+
},
|
|
112
|
+
eventType: EVENT_TYPE.OPERATIONAL
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
syncBlockAdded.forEach(syncBlock => {
|
|
116
|
+
if (fg('platform_synced_block_patch_3')) {
|
|
117
|
+
var _api$analytics2, _api$analytics2$actio;
|
|
118
|
+
api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : (_api$analytics2$actio = _api$analytics2.actions) === null || _api$analytics2$actio === void 0 ? void 0 : _api$analytics2$actio.fireAnalyticsEvent({
|
|
119
|
+
action: ACTION.INSERTED,
|
|
120
|
+
actionSubject: ACTION_SUBJECT.DOCUMENT,
|
|
121
|
+
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK,
|
|
122
|
+
attributes: {
|
|
123
|
+
resourceId: syncBlock.attrs.resourceId,
|
|
124
|
+
blockInstanceId: syncBlock.attrs.localId
|
|
125
|
+
},
|
|
126
|
+
eventType: EVENT_TYPE.TRACK
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
var _api$analytics3, _api$analytics3$actio;
|
|
130
|
+
api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : (_api$analytics3$actio = _api$analytics3.actions) === null || _api$analytics3$actio === void 0 ? void 0 : _api$analytics3$actio.fireAnalyticsEvent({
|
|
131
|
+
action: ACTION.INSERTED,
|
|
132
|
+
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
133
|
+
actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_CREATE,
|
|
134
|
+
attributes: {
|
|
135
|
+
resourceId: syncBlock.attrs.resourceId,
|
|
136
|
+
blockInstanceId: syncBlock.attrs.localId
|
|
137
|
+
},
|
|
138
|
+
eventType: EVENT_TYPE.OPERATIONAL
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
if (bodiedSyncBlockRemoved.length > 0) {
|
|
143
|
+
// eslint-disable-next-line no-param-reassign
|
|
144
|
+
confirmationTransactionRef.current = tr;
|
|
145
|
+
return handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, getDeleteReason(tr));
|
|
146
|
+
}
|
|
147
|
+
if (bodiedSyncBlockAdded.length > 0) {
|
|
148
|
+
if (tr.getMeta(pmHistoryPluginKey)) {
|
|
149
|
+
// We don't allow bodiedSyncBlock creation via redo, however, we need to return true here to let transaction through so history can be updated properly.
|
|
150
|
+
// If we simply returns false, creation from redo is blocked as desired, but this results in editor showing redo as possible even though it's not.
|
|
151
|
+
// After true is returned here and the node is created, we delete the node in the filterTransaction immediately, which cancels out the creation
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, state, api);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
showInlineExtensionInSyncBlockWarningIfNeeded(tr, state, api, inlineExtensionFlagShown);
|
|
158
|
+
return true;
|
|
159
|
+
};
|
|
160
|
+
const filterTransactionOffline = ({
|
|
161
|
+
tr,
|
|
162
|
+
state,
|
|
163
|
+
api,
|
|
164
|
+
isConfirmedSyncBlockDeletion,
|
|
165
|
+
bodiedSyncBlockRemoved,
|
|
166
|
+
bodiedSyncBlockAdded
|
|
167
|
+
}) => {
|
|
168
|
+
const {
|
|
169
|
+
removed: syncBlockRemoved,
|
|
170
|
+
added: syncBlockAdded
|
|
171
|
+
} = trackSyncBlocks(node => node.type.name === 'syncBlock', tr, state);
|
|
172
|
+
let errorFlag = false;
|
|
173
|
+
if (isConfirmedSyncBlockDeletion || bodiedSyncBlockRemoved.length > 0 || syncBlockRemoved.length > 0) {
|
|
174
|
+
errorFlag = FLAG_ID.CANNOT_DELETE_WHEN_OFFLINE;
|
|
175
|
+
} else if (bodiedSyncBlockAdded.length > 0 || syncBlockAdded.length > 0) {
|
|
176
|
+
errorFlag = FLAG_ID.CANNOT_CREATE_WHEN_OFFLINE;
|
|
177
|
+
} else if (hasEditInSyncBlock(tr, state)) {
|
|
178
|
+
errorFlag = FLAG_ID.CANNOT_EDIT_WHEN_OFFLINE;
|
|
179
|
+
}
|
|
180
|
+
if (errorFlag) {
|
|
181
|
+
deferDispatch(() => {
|
|
182
|
+
api === null || api === void 0 ? void 0 : api.core.actions.execute(({
|
|
183
|
+
tr
|
|
184
|
+
}) => tr.setMeta(syncedBlockPluginKey, {
|
|
185
|
+
activeFlag: {
|
|
186
|
+
id: errorFlag
|
|
187
|
+
}
|
|
188
|
+
}));
|
|
189
|
+
});
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
return true;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Encapsulates mutable state that persists across transactions in the
|
|
197
|
+
* synced block plugin. Replaces module-level closure variables so state
|
|
198
|
+
* is explicitly scoped to a single plugin instance.
|
|
199
|
+
*/
|
|
200
|
+
class SyncedBlockPluginContext {
|
|
201
|
+
constructor() {
|
|
202
|
+
_defineProperty(this, "confirmationTransactionRef", {
|
|
203
|
+
current: undefined
|
|
204
|
+
});
|
|
205
|
+
_defineProperty(this, "_isCopyEvent", false);
|
|
206
|
+
_defineProperty(this, "unpublishedFlagShown", new Set());
|
|
207
|
+
_defineProperty(this, "inlineExtensionFlagShown", new Set());
|
|
208
|
+
}
|
|
209
|
+
get isCopyEvent() {
|
|
210
|
+
return this._isCopyEvent;
|
|
211
|
+
}
|
|
212
|
+
markCopyEvent() {
|
|
213
|
+
this._isCopyEvent = true;
|
|
214
|
+
}
|
|
215
|
+
consumeCopyEvent() {
|
|
216
|
+
const was = this._isCopyEvent;
|
|
217
|
+
this._isCopyEvent = false;
|
|
218
|
+
return was;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
90
221
|
export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api) => {
|
|
222
|
+
var _ctx$confirmationTran, _ctx$unpublishedFlagS, _ctx$inlineExtensionF;
|
|
91
223
|
const {
|
|
92
224
|
useLongPressSelection = false
|
|
93
225
|
} = options || {};
|
|
94
|
-
const
|
|
226
|
+
const ctx = fg('platform_synced_block_patch_5') ? new SyncedBlockPluginContext() : undefined;
|
|
227
|
+
const confirmationTransactionRef = (_ctx$confirmationTran = ctx === null || ctx === void 0 ? void 0 : ctx.confirmationTransactionRef) !== null && _ctx$confirmationTran !== void 0 ? _ctx$confirmationTran : {
|
|
95
228
|
current: undefined
|
|
96
229
|
};
|
|
97
|
-
// Track if a copy event occurred to distinguish copy from drag and drop
|
|
98
230
|
let isCopyEvent = false;
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
// Track which sync blocks have already triggered the inline extension in sync block flag
|
|
102
|
-
const inlineExtensionFlagShown = new Set();
|
|
231
|
+
const unpublishedFlagShown = (_ctx$unpublishedFlagS = ctx === null || ctx === void 0 ? void 0 : ctx.unpublishedFlagShown) !== null && _ctx$unpublishedFlagS !== void 0 ? _ctx$unpublishedFlagS : new Set();
|
|
232
|
+
const inlineExtensionFlagShown = (_ctx$inlineExtensionF = ctx === null || ctx === void 0 ? void 0 : ctx.inlineExtensionFlagShown) !== null && _ctx$inlineExtensionF !== void 0 ? _ctx$inlineExtensionF : new Set();
|
|
103
233
|
|
|
104
234
|
// Set up callback to detect unpublished sync blocks when they're fetched
|
|
105
235
|
syncBlockStore.referenceManager.setOnUnpublishedSyncBlockDetected(resourceId => {
|
|
106
236
|
// Only show the flag once per sync block
|
|
107
237
|
if (!unpublishedFlagShown.has(resourceId)) {
|
|
108
238
|
unpublishedFlagShown.add(resourceId);
|
|
109
|
-
|
|
110
|
-
setTimeout(() => {
|
|
239
|
+
deferDispatch(() => {
|
|
111
240
|
api === null || api === void 0 ? void 0 : api.core.actions.execute(({
|
|
112
241
|
tr
|
|
113
|
-
}) => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
});
|
|
120
|
-
}, 0);
|
|
242
|
+
}) => tr.setMeta(syncedBlockPluginKey, {
|
|
243
|
+
activeFlag: {
|
|
244
|
+
id: FLAG_ID.UNPUBLISHED_SYNC_BLOCK_PASTED
|
|
245
|
+
}
|
|
246
|
+
}));
|
|
247
|
+
});
|
|
121
248
|
}
|
|
122
249
|
});
|
|
123
250
|
return new SafePlugin({
|
|
@@ -159,23 +286,27 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
159
286
|
},
|
|
160
287
|
props: {
|
|
161
288
|
nodeViews: {
|
|
162
|
-
syncBlock: (node, view, getPos, _decorations) =>
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
289
|
+
syncBlock: (node, view, getPos, _decorations) =>
|
|
290
|
+
// To support SSR, pass `syncBlockStore` here
|
|
291
|
+
// and do not use lazy loading.
|
|
292
|
+
// We cannot start rendering and then load `syncBlockStore` asynchronously,
|
|
293
|
+
// because obtaining it is asynchronous (sharedPluginState.currentState() is delayed).
|
|
294
|
+
new SyncBlockView({
|
|
295
|
+
api,
|
|
296
|
+
options,
|
|
297
|
+
node,
|
|
298
|
+
view,
|
|
299
|
+
getPos,
|
|
300
|
+
portalProviderAPI: pmPluginFactoryParams.portalProviderAPI,
|
|
301
|
+
eventDispatcher: pmPluginFactoryParams.eventDispatcher,
|
|
302
|
+
syncBlockStore: syncBlockStore
|
|
303
|
+
}).init(),
|
|
304
|
+
bodiedSyncBlock: fg('platform_synced_block_patch_5') ? bodiedSyncBlockNodeView({
|
|
305
|
+
pluginOptions: options,
|
|
306
|
+
pmPluginFactoryParams,
|
|
307
|
+
api,
|
|
308
|
+
syncBlockStore
|
|
309
|
+
}) : lazyBodiedSyncBlockView({
|
|
179
310
|
pluginOptions: options,
|
|
180
311
|
pmPluginFactoryParams,
|
|
181
312
|
api
|
|
@@ -224,7 +355,11 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
224
355
|
return shouldIgnoreDomEvent(view, event, api);
|
|
225
356
|
},
|
|
226
357
|
copy: () => {
|
|
227
|
-
|
|
358
|
+
if (ctx) {
|
|
359
|
+
ctx.markCopyEvent();
|
|
360
|
+
} else {
|
|
361
|
+
isCopyEvent = true;
|
|
362
|
+
}
|
|
228
363
|
return false;
|
|
229
364
|
}
|
|
230
365
|
},
|
|
@@ -236,8 +371,10 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
236
371
|
const {
|
|
237
372
|
schema
|
|
238
373
|
} = state;
|
|
239
|
-
const isCopy = isCopyEvent;
|
|
240
|
-
|
|
374
|
+
const isCopy = ctx ? ctx.consumeCopyEvent() : isCopyEvent;
|
|
375
|
+
if (!ctx) {
|
|
376
|
+
isCopyEvent = false;
|
|
377
|
+
}
|
|
241
378
|
if (!syncBlockStore || !isCopy) {
|
|
242
379
|
return slice;
|
|
243
380
|
}
|
|
@@ -280,7 +417,6 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
280
417
|
const {
|
|
281
418
|
added
|
|
282
419
|
} = trackSyncBlocks(node => node.type.name === 'syncBlock', tr, state);
|
|
283
|
-
// Mark newly added sync blocks so we can detect unpublished status when data is fetched
|
|
284
420
|
added.forEach(nodeInfo => {
|
|
285
421
|
var _nodeInfo$attrs;
|
|
286
422
|
if ((_nodeInfo$attrs = nodeInfo.attrs) !== null && _nodeInfo$attrs !== void 0 && _nodeInfo$attrs.resourceId) {
|
|
@@ -288,11 +424,6 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
288
424
|
}
|
|
289
425
|
});
|
|
290
426
|
}
|
|
291
|
-
|
|
292
|
-
// Ignore transactions that don't change the document
|
|
293
|
-
// or are from remote (collab) or already confirmed sync block deletion
|
|
294
|
-
// We only care about local changes that change the document
|
|
295
|
-
// and are not yet confirmed for sync block deletion
|
|
296
427
|
if (!tr.docChanged || Boolean(tr.getMeta('isRemote')) || !isOffline && isConfirmedSyncBlockDeletion) {
|
|
297
428
|
return true;
|
|
298
429
|
}
|
|
@@ -300,14 +431,33 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
300
431
|
removed: bodiedSyncBlockRemoved,
|
|
301
432
|
added: bodiedSyncBlockAdded
|
|
302
433
|
} = trackSyncBlocks(syncBlockStore.sourceManager.isSourceBlock, tr, state);
|
|
434
|
+
if (fg('platform_synced_block_patch_5')) {
|
|
435
|
+
return isOffline ? filterTransactionOffline({
|
|
436
|
+
tr,
|
|
437
|
+
state,
|
|
438
|
+
api,
|
|
439
|
+
isConfirmedSyncBlockDeletion,
|
|
440
|
+
bodiedSyncBlockRemoved,
|
|
441
|
+
bodiedSyncBlockAdded
|
|
442
|
+
}) : filterTransactionOnline({
|
|
443
|
+
tr,
|
|
444
|
+
state,
|
|
445
|
+
syncBlockStore,
|
|
446
|
+
api,
|
|
447
|
+
confirmationTransactionRef,
|
|
448
|
+
bodiedSyncBlockRemoved,
|
|
449
|
+
bodiedSyncBlockAdded,
|
|
450
|
+
inlineExtensionFlagShown
|
|
451
|
+
});
|
|
452
|
+
}
|
|
303
453
|
if (!isOffline) {
|
|
304
454
|
const {
|
|
305
455
|
removed: syncBlockRemoved,
|
|
306
456
|
added: syncBlockAdded
|
|
307
457
|
} = trackSyncBlocks(node => node.type.name === 'syncBlock', tr, state);
|
|
308
458
|
syncBlockRemoved.forEach(syncBlock => {
|
|
309
|
-
var _api$
|
|
310
|
-
api === null || api === void 0 ? void 0 : (_api$
|
|
459
|
+
var _api$analytics4, _api$analytics4$actio;
|
|
460
|
+
api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : (_api$analytics4$actio = _api$analytics4.actions) === null || _api$analytics4$actio === void 0 ? void 0 : _api$analytics4$actio.fireAnalyticsEvent({
|
|
311
461
|
action: ACTION.DELETED,
|
|
312
462
|
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
313
463
|
actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_DELETE,
|
|
@@ -320,8 +470,8 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
320
470
|
});
|
|
321
471
|
syncBlockAdded.forEach(syncBlock => {
|
|
322
472
|
if (fg('platform_synced_block_patch_3')) {
|
|
323
|
-
var _api$
|
|
324
|
-
api === null || api === void 0 ? void 0 : (_api$
|
|
473
|
+
var _api$analytics5, _api$analytics5$actio;
|
|
474
|
+
api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : (_api$analytics5$actio = _api$analytics5.actions) === null || _api$analytics5$actio === void 0 ? void 0 : _api$analytics5$actio.fireAnalyticsEvent({
|
|
325
475
|
action: ACTION.INSERTED,
|
|
326
476
|
actionSubject: ACTION_SUBJECT.DOCUMENT,
|
|
327
477
|
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK,
|
|
@@ -332,8 +482,8 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
332
482
|
eventType: EVENT_TYPE.TRACK
|
|
333
483
|
});
|
|
334
484
|
} else {
|
|
335
|
-
var _api$
|
|
336
|
-
api === null || api === void 0 ? void 0 : (_api$
|
|
485
|
+
var _api$analytics6, _api$analytics6$actio;
|
|
486
|
+
api === null || api === void 0 ? void 0 : (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : (_api$analytics6$actio = _api$analytics6.actions) === null || _api$analytics6$actio === void 0 ? void 0 : _api$analytics6$actio.fireAnalyticsEvent({
|
|
337
487
|
action: ACTION.INSERTED,
|
|
338
488
|
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
339
489
|
actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_CREATE,
|
|
@@ -350,7 +500,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
350
500
|
return handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, getDeleteReason(tr));
|
|
351
501
|
}
|
|
352
502
|
if (bodiedSyncBlockAdded.length > 0) {
|
|
353
|
-
if (
|
|
503
|
+
if (tr.getMeta(pmHistoryPluginKey)) {
|
|
354
504
|
// We don't allow bodiedSyncBlock creation via redo, however, we need to return true here to let transaction through so history can be updated properly.
|
|
355
505
|
// If we simply returns false, creation from redo is blocked as desired, but this results in editor showing redo as possible even though it's not.
|
|
356
506
|
// After true is returned here and the node is created, we delete the node in the filterTransaction immediately, which cancels out the creation
|
|
@@ -361,36 +511,32 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
361
511
|
}
|
|
362
512
|
showInlineExtensionInSyncBlockWarningIfNeeded(tr, state, api, inlineExtensionFlagShown);
|
|
363
513
|
return true;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
514
|
+
}
|
|
515
|
+
const {
|
|
516
|
+
removed: syncBlockRemoved,
|
|
517
|
+
added: syncBlockAdded
|
|
518
|
+
} = trackSyncBlocks(node => node.type.name === 'syncBlock', tr, state);
|
|
519
|
+
let errorFlag = false;
|
|
370
520
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
});
|
|
391
|
-
}, 0);
|
|
392
|
-
return false;
|
|
393
|
-
}
|
|
521
|
+
// Disable (bodied)syncBlock node deletion/creation/edition in offline mode and trigger an error flag instead
|
|
522
|
+
if (isConfirmedSyncBlockDeletion || bodiedSyncBlockRemoved.length > 0 || syncBlockRemoved.length > 0) {
|
|
523
|
+
errorFlag = FLAG_ID.CANNOT_DELETE_WHEN_OFFLINE;
|
|
524
|
+
} else if (bodiedSyncBlockAdded.length > 0 || syncBlockAdded.length > 0) {
|
|
525
|
+
errorFlag = FLAG_ID.CANNOT_CREATE_WHEN_OFFLINE;
|
|
526
|
+
} else if (hasEditInSyncBlock(tr, state)) {
|
|
527
|
+
errorFlag = FLAG_ID.CANNOT_EDIT_WHEN_OFFLINE;
|
|
528
|
+
}
|
|
529
|
+
if (errorFlag) {
|
|
530
|
+
deferDispatch(() => {
|
|
531
|
+
api === null || api === void 0 ? void 0 : api.core.actions.execute(({
|
|
532
|
+
tr
|
|
533
|
+
}) => tr.setMeta(syncedBlockPluginKey, {
|
|
534
|
+
activeFlag: {
|
|
535
|
+
id: errorFlag
|
|
536
|
+
}
|
|
537
|
+
}));
|
|
538
|
+
});
|
|
539
|
+
return false;
|
|
394
540
|
}
|
|
395
541
|
return true;
|
|
396
542
|
},
|
|
@@ -401,22 +547,23 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
401
547
|
}
|
|
402
548
|
});
|
|
403
549
|
for (const tr of trs) {
|
|
404
|
-
if (
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
added
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
550
|
+
if (tr.getMeta(pmHistoryPluginKey)) {
|
|
551
|
+
const {
|
|
552
|
+
added
|
|
553
|
+
} = trackSyncBlocks(syncBlockStore.sourceManager.isSourceBlock, tr, oldState);
|
|
554
|
+
if (added.length > 0) {
|
|
555
|
+
// Delete bodiedSyncBlock if it's originated from history, i.e. redo creation
|
|
556
|
+
// See filterTransaction above for more details
|
|
557
|
+
const {
|
|
558
|
+
tr
|
|
559
|
+
} = newState;
|
|
560
|
+
added.forEach(node => {
|
|
561
|
+
if (node.from !== undefined && node.to !== undefined) {
|
|
562
|
+
tr.delete(node.from, node.to);
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
return tr;
|
|
566
|
+
}
|
|
420
567
|
}
|
|
421
568
|
}
|
|
422
569
|
return null;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
2
2
|
import { FLAG_ID } from '../../types';
|
|
3
3
|
import { syncedBlockPluginKey } from '../main';
|
|
4
|
+
import { deferDispatch } from './utils';
|
|
4
5
|
const onRetry = (api, resourceId) => {
|
|
5
6
|
return () => {
|
|
6
7
|
var _api$core, _api$core2;
|
|
@@ -77,7 +78,7 @@ export const handleBodiedSyncBlockCreation = (bodiedSyncBlockAdded, editorState,
|
|
|
77
78
|
to: node.to
|
|
78
79
|
};
|
|
79
80
|
const resourceId = node.attrs.resourceId;
|
|
80
|
-
|
|
81
|
+
deferDispatch(() => {
|
|
81
82
|
var _api$core3;
|
|
82
83
|
api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(({
|
|
83
84
|
tr
|
|
@@ -2,6 +2,19 @@ import { expandSelectionToBlockRange } from '@atlaskit/editor-common/selection';
|
|
|
2
2
|
import { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
3
3
|
import { ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
|
|
4
4
|
import { findParentNodeOfType, findParentNodeOfTypeClosestToPos, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
5
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Defers a callback to the next microtask (when gated) or next macrotask via setTimeout(0).
|
|
9
|
+
* Used to avoid re-entrant ProseMirror dispatch cycles.
|
|
10
|
+
*/
|
|
11
|
+
export const deferDispatch = fn => {
|
|
12
|
+
if (fg('platform_synced_block_patch_5')) {
|
|
13
|
+
queueMicrotask(fn);
|
|
14
|
+
} else {
|
|
15
|
+
setTimeout(fn, 0);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
5
18
|
export const findSyncBlock = (schema, selection) => {
|
|
6
19
|
const {
|
|
7
20
|
syncBlock
|
|
@@ -5,7 +5,7 @@ import { DOMSerializer, Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
|
5
5
|
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
6
6
|
import { findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode, safeInsert } from '@atlaskit/editor-prosemirror/utils';
|
|
7
7
|
import { syncedBlockPluginKey } from '../pm-plugins/main';
|
|
8
|
-
import { canBeConvertedToSyncBlock, findSyncBlock, findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
|
|
8
|
+
import { canBeConvertedToSyncBlock, deferDispatch, findSyncBlock, findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
|
|
9
9
|
import { FLAG_ID } from '../types';
|
|
10
10
|
import { pasteSyncBlockHTMLContent } from './utils';
|
|
11
11
|
export var createSyncedBlock = function createSyncedBlock(_ref) {
|
|
@@ -147,9 +147,7 @@ var copySyncedBlockReferenceToClipboardInternal = function copySyncedBlockRefere
|
|
|
147
147
|
}
|
|
148
148
|
var domNode = toDOM(referenceSyncBlockNode, schema);
|
|
149
149
|
copyDomNode(domNode, referenceSyncBlockNode.type, selection);
|
|
150
|
-
|
|
151
|
-
// Use setTimeout to dispatch transaction in next tick and avoid re-entrant dispatch
|
|
152
|
-
setTimeout(function () {
|
|
150
|
+
deferDispatch(function () {
|
|
153
151
|
api === null || api === void 0 || api.core.actions.execute(function (_ref3) {
|
|
154
152
|
var _api$analytics4;
|
|
155
153
|
var tr = _ref3.tr;
|
|
@@ -169,7 +167,7 @@ var copySyncedBlockReferenceToClipboardInternal = function copySyncedBlockRefere
|
|
|
169
167
|
}
|
|
170
168
|
});
|
|
171
169
|
});
|
|
172
|
-
}
|
|
170
|
+
});
|
|
173
171
|
return true;
|
|
174
172
|
};
|
|
175
173
|
export var editSyncedBlockSource = function editSyncedBlockSource(syncBlockStore, api) {
|