@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.
@@ -12,6 +12,7 @@ import ReactNodeView from '@atlaskit/editor-common/react-node-view';
12
12
  import { BodiedSyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block';
13
13
  import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
14
14
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
15
+ import { fg } from '@atlaskit/platform-feature-flags';
15
16
  import { BodiedSyncBlockWrapper } from '../ui/BodiedSyncBlockWrapper';
16
17
  var toDOM = function toDOM() {
17
18
  return ['div', {
@@ -19,12 +20,13 @@ var toDOM = function toDOM() {
19
20
  contenteditable: true
20
21
  }, 0];
21
22
  };
22
- var BodiedSyncBlock = /*#__PURE__*/function (_ReactNodeView) {
23
+ export var BodiedSyncBlock = /*#__PURE__*/function (_ReactNodeView) {
23
24
  function BodiedSyncBlock(props) {
24
25
  var _this;
25
26
  _classCallCheck(this, BodiedSyncBlock);
26
27
  _this = _callSuper(this, BodiedSyncBlock, [props.node, props.view, props.getPos, props.portalProviderAPI, props.eventDispatcher, props]);
27
28
  _this.api = props.api;
29
+ _this.syncBlockStore = props.syncBlockStore;
28
30
  _this.handleConnectivityModeChange();
29
31
  _this.handleViewModeChange();
30
32
  return _this;
@@ -84,8 +86,9 @@ var BodiedSyncBlock = /*#__PURE__*/function (_ReactNodeView) {
84
86
  }, {
85
87
  key: "render",
86
88
  value: function render(_props, forwardRef) {
87
- var _this$api5, _this$api6;
88
- var syncBlockStore = (_this$api5 = this.api) === null || _this$api5 === void 0 || (_this$api5 = _this$api5.syncedBlock.sharedState) === null || _this$api5 === void 0 || (_this$api5 = _this$api5.currentState()) === null || _this$api5 === void 0 ? void 0 : _this$api5.syncBlockStore;
89
+ var _this$api$syncedBlock, _this$api5, _this$api6;
90
+ // Use passed syncBlockStore for SSR where sharedState.currentState() is delayed
91
+ var syncBlockStore = (_this$api$syncedBlock = (_this$api5 = this.api) === null || _this$api5 === void 0 || (_this$api5 = _this$api5.syncedBlock.sharedState) === null || _this$api5 === void 0 || (_this$api5 = _this$api5.currentState()) === null || _this$api5 === void 0 ? void 0 : _this$api5.syncBlockStore) !== null && _this$api$syncedBlock !== void 0 ? _this$api$syncedBlock : this.syncBlockStore;
89
92
  if (!syncBlockStore) {
90
93
  return null;
91
94
  }
@@ -105,10 +108,12 @@ var BodiedSyncBlock = /*#__PURE__*/function (_ReactNodeView) {
105
108
  var _DOMSerializer$render = DOMSerializer.renderSpec(document, toDOM()),
106
109
  dom = _DOMSerializer$render.dom,
107
110
  contentDOM = _DOMSerializer$render.contentDOM;
108
- if (dom instanceof HTMLElement) {
111
+ // In SSR, the first check won't work, so fallback to nodeType check
112
+ if (dom instanceof HTMLElement || dom.nodeType === 1 && fg('platform_synced_block_patch_5')) {
109
113
  this.updateContentEditable({
110
114
  contentDOM: contentDOM
111
115
  });
116
+ // eslint-disable-next-line @atlaskit/editor/no-as-casting
112
117
  return {
113
118
  dom: dom,
114
119
  contentDOM: contentDOM
@@ -131,7 +136,8 @@ var BodiedSyncBlock = /*#__PURE__*/function (_ReactNodeView) {
131
136
  export var bodiedSyncBlockNodeView = function bodiedSyncBlockNodeView(_ref4) {
132
137
  var pluginOptions = _ref4.pluginOptions,
133
138
  pmPluginFactoryParams = _ref4.pmPluginFactoryParams,
134
- api = _ref4.api;
139
+ api = _ref4.api,
140
+ syncBlockStore = _ref4.syncBlockStore;
135
141
  return function (node, view, getPos) {
136
142
  var portalProviderAPI = pmPluginFactoryParams.portalProviderAPI,
137
143
  eventDispatcher = pmPluginFactoryParams.eventDispatcher;
@@ -142,7 +148,8 @@ export var bodiedSyncBlockNodeView = function bodiedSyncBlockNodeView(_ref4) {
142
148
  view: view,
143
149
  getPos: getPos,
144
150
  portalProviderAPI: portalProviderAPI,
145
- eventDispatcher: eventDispatcher
151
+ eventDispatcher: eventDispatcher,
152
+ syncBlockStore: syncBlockStore
146
153
  }).init();
147
154
  };
148
155
  };
@@ -1,3 +1,5 @@
1
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
+ import _createClass from "@babel/runtime/helpers/createClass";
1
3
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
4
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
5
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
@@ -16,6 +18,7 @@ import { DecorationSet, Decoration } from '@atlaskit/editor-prosemirror/view';
16
18
  import { convertPMNodesToSyncBlockNodes, rebaseTransaction } from '@atlaskit/editor-synced-block-provider';
17
19
  import { fg } from '@atlaskit/platform-feature-flags';
18
20
  import { lazyBodiedSyncBlockView } from '../nodeviews/bodiedLazySyncedBlock';
21
+ import { bodiedSyncBlockNodeView } from '../nodeviews/bodiedSyncedBlock';
19
22
  import { SyncBlock as SyncBlockView } from '../nodeviews/syncedBlock';
20
23
  import { FLAG_ID } from '../types';
21
24
  import { handleBodiedSyncBlockCreation } from './utils/handle-bodied-sync-block-creation';
@@ -23,7 +26,7 @@ import { handleBodiedSyncBlockRemoval } from './utils/handle-bodied-sync-block-r
23
26
  import { shouldIgnoreDomEvent } from './utils/ignore-dom-event';
24
27
  import { calculateDecorations } from './utils/selection-decorations';
25
28
  import { hasEditInSyncBlock, trackSyncBlocks } from './utils/track-sync-blocks';
26
- import { wasInlineExtensionInsertedInBodiedSyncBlock, sliceFullyContainsNode } from './utils/utils';
29
+ import { deferDispatch, wasInlineExtensionInsertedInBodiedSyncBlock, sliceFullyContainsNode } from './utils/utils';
27
30
  export var syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
28
31
  var mapRetryCreationPosMap = function mapRetryCreationPosMap(oldMap, newRetryCreationPos, mapPos) {
29
32
  var resourceId = newRetryCreationPos === null || newRetryCreationPos === void 0 ? void 0 : newRetryCreationPos.resourceId;
@@ -59,8 +62,7 @@ var mapRetryCreationPosMap = function mapRetryCreationPosMap(oldMap, newRetryCre
59
62
  return newMap;
60
63
  };
61
64
  var showCopiedFlag = function showCopiedFlag(api) {
62
- // Use setTimeout to dispatch transaction in next tick and avoid re-entrant dispatch
63
- setTimeout(function () {
65
+ deferDispatch(function () {
64
66
  api === null || api === void 0 || api.core.actions.execute(function (_ref) {
65
67
  var tr = _ref.tr;
66
68
  return tr.setMeta(syncedBlockPluginKey, {
@@ -69,7 +71,7 @@ var showCopiedFlag = function showCopiedFlag(api) {
69
71
  }
70
72
  });
71
73
  });
72
- }, 0);
74
+ });
73
75
  };
74
76
  var showInlineExtensionInSyncBlockWarningIfNeeded = function showInlineExtensionInSyncBlockWarningIfNeeded(tr, state, api, inlineExtensionFlagShown) {
75
77
  var _api$connectivity;
@@ -83,8 +85,7 @@ var showInlineExtensionInSyncBlockWarningIfNeeded = function showInlineExtension
83
85
  // Only show the flag on the first instance per sync block (same as UNPUBLISHED_SYNC_BLOCK_PASTED)
84
86
  if (resourceId && !inlineExtensionFlagShown.has(resourceId)) {
85
87
  inlineExtensionFlagShown.add(resourceId);
86
- // Use setTimeout to dispatch in next tick and avoid re-entrant dispatch from filterTransaction
87
- setTimeout(function () {
88
+ deferDispatch(function () {
88
89
  api === null || api === void 0 || api.core.actions.execute(function (_ref2) {
89
90
  var tr = _ref2.tr;
90
91
  return tr.setMeta(syncedBlockPluginKey, {
@@ -93,7 +94,7 @@ var showInlineExtensionInSyncBlockWarningIfNeeded = function showInlineExtension
93
94
  }
94
95
  });
95
96
  });
96
- }, 0);
97
+ });
97
98
  }
98
99
  };
99
100
  var getDeleteReason = function getDeleteReason(tr) {
@@ -103,36 +104,176 @@ var getDeleteReason = function getDeleteReason(tr) {
103
104
  }
104
105
  return reason;
105
106
  };
107
+ var filterTransactionOnline = function filterTransactionOnline(_ref3) {
108
+ var tr = _ref3.tr,
109
+ state = _ref3.state,
110
+ syncBlockStore = _ref3.syncBlockStore,
111
+ api = _ref3.api,
112
+ confirmationTransactionRef = _ref3.confirmationTransactionRef,
113
+ bodiedSyncBlockRemoved = _ref3.bodiedSyncBlockRemoved,
114
+ bodiedSyncBlockAdded = _ref3.bodiedSyncBlockAdded,
115
+ inlineExtensionFlagShown = _ref3.inlineExtensionFlagShown;
116
+ var _trackSyncBlocks = trackSyncBlocks(function (node) {
117
+ return node.type.name === 'syncBlock';
118
+ }, tr, state),
119
+ syncBlockRemoved = _trackSyncBlocks.removed,
120
+ syncBlockAdded = _trackSyncBlocks.added;
121
+ syncBlockRemoved.forEach(function (syncBlock) {
122
+ var _api$analytics;
123
+ api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.fireAnalyticsEvent({
124
+ action: ACTION.DELETED,
125
+ actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
126
+ actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_DELETE,
127
+ attributes: {
128
+ resourceId: syncBlock.attrs.resourceId,
129
+ blockInstanceId: syncBlock.attrs.localId
130
+ },
131
+ eventType: EVENT_TYPE.OPERATIONAL
132
+ });
133
+ });
134
+ syncBlockAdded.forEach(function (syncBlock) {
135
+ if (fg('platform_synced_block_patch_3')) {
136
+ var _api$analytics2;
137
+ api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || (_api$analytics2 = _api$analytics2.actions) === null || _api$analytics2 === void 0 || _api$analytics2.fireAnalyticsEvent({
138
+ action: ACTION.INSERTED,
139
+ actionSubject: ACTION_SUBJECT.DOCUMENT,
140
+ actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK,
141
+ attributes: {
142
+ resourceId: syncBlock.attrs.resourceId,
143
+ blockInstanceId: syncBlock.attrs.localId
144
+ },
145
+ eventType: EVENT_TYPE.TRACK
146
+ });
147
+ } else {
148
+ var _api$analytics3;
149
+ api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 || (_api$analytics3 = _api$analytics3.actions) === null || _api$analytics3 === void 0 || _api$analytics3.fireAnalyticsEvent({
150
+ action: ACTION.INSERTED,
151
+ actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
152
+ actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_CREATE,
153
+ attributes: {
154
+ resourceId: syncBlock.attrs.resourceId,
155
+ blockInstanceId: syncBlock.attrs.localId
156
+ },
157
+ eventType: EVENT_TYPE.OPERATIONAL
158
+ });
159
+ }
160
+ });
161
+ if (bodiedSyncBlockRemoved.length > 0) {
162
+ // eslint-disable-next-line no-param-reassign
163
+ confirmationTransactionRef.current = tr;
164
+ return handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, getDeleteReason(tr));
165
+ }
166
+ if (bodiedSyncBlockAdded.length > 0) {
167
+ if (tr.getMeta(pmHistoryPluginKey)) {
168
+ // 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.
169
+ // 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.
170
+ // After true is returned here and the node is created, we delete the node in the filterTransaction immediately, which cancels out the creation
171
+ return true;
172
+ }
173
+ handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, state, api);
174
+ return true;
175
+ }
176
+ showInlineExtensionInSyncBlockWarningIfNeeded(tr, state, api, inlineExtensionFlagShown);
177
+ return true;
178
+ };
179
+ var filterTransactionOffline = function filterTransactionOffline(_ref4) {
180
+ var tr = _ref4.tr,
181
+ state = _ref4.state,
182
+ api = _ref4.api,
183
+ isConfirmedSyncBlockDeletion = _ref4.isConfirmedSyncBlockDeletion,
184
+ bodiedSyncBlockRemoved = _ref4.bodiedSyncBlockRemoved,
185
+ bodiedSyncBlockAdded = _ref4.bodiedSyncBlockAdded;
186
+ var _trackSyncBlocks2 = trackSyncBlocks(function (node) {
187
+ return node.type.name === 'syncBlock';
188
+ }, tr, state),
189
+ syncBlockRemoved = _trackSyncBlocks2.removed,
190
+ syncBlockAdded = _trackSyncBlocks2.added;
191
+ var errorFlag = false;
192
+ if (isConfirmedSyncBlockDeletion || bodiedSyncBlockRemoved.length > 0 || syncBlockRemoved.length > 0) {
193
+ errorFlag = FLAG_ID.CANNOT_DELETE_WHEN_OFFLINE;
194
+ } else if (bodiedSyncBlockAdded.length > 0 || syncBlockAdded.length > 0) {
195
+ errorFlag = FLAG_ID.CANNOT_CREATE_WHEN_OFFLINE;
196
+ } else if (hasEditInSyncBlock(tr, state)) {
197
+ errorFlag = FLAG_ID.CANNOT_EDIT_WHEN_OFFLINE;
198
+ }
199
+ if (errorFlag) {
200
+ deferDispatch(function () {
201
+ api === null || api === void 0 || api.core.actions.execute(function (_ref5) {
202
+ var tr = _ref5.tr;
203
+ return tr.setMeta(syncedBlockPluginKey, {
204
+ activeFlag: {
205
+ id: errorFlag
206
+ }
207
+ });
208
+ });
209
+ });
210
+ return false;
211
+ }
212
+ return true;
213
+ };
214
+
215
+ /**
216
+ * Encapsulates mutable state that persists across transactions in the
217
+ * synced block plugin. Replaces module-level closure variables so state
218
+ * is explicitly scoped to a single plugin instance.
219
+ */
220
+ var SyncedBlockPluginContext = /*#__PURE__*/function () {
221
+ function SyncedBlockPluginContext() {
222
+ _classCallCheck(this, SyncedBlockPluginContext);
223
+ _defineProperty(this, "confirmationTransactionRef", {
224
+ current: undefined
225
+ });
226
+ _defineProperty(this, "_isCopyEvent", false);
227
+ _defineProperty(this, "unpublishedFlagShown", new Set());
228
+ _defineProperty(this, "inlineExtensionFlagShown", new Set());
229
+ }
230
+ return _createClass(SyncedBlockPluginContext, [{
231
+ key: "isCopyEvent",
232
+ get: function get() {
233
+ return this._isCopyEvent;
234
+ }
235
+ }, {
236
+ key: "markCopyEvent",
237
+ value: function markCopyEvent() {
238
+ this._isCopyEvent = true;
239
+ }
240
+ }, {
241
+ key: "consumeCopyEvent",
242
+ value: function consumeCopyEvent() {
243
+ var was = this._isCopyEvent;
244
+ this._isCopyEvent = false;
245
+ return was;
246
+ }
247
+ }]);
248
+ }();
106
249
  export var createPlugin = function createPlugin(options, pmPluginFactoryParams, syncBlockStore, api) {
107
- var _ref3 = options || {},
108
- _ref3$useLongPressSel = _ref3.useLongPressSelection,
109
- useLongPressSelection = _ref3$useLongPressSel === void 0 ? false : _ref3$useLongPressSel;
110
- var confirmationTransactionRef = {
250
+ var _ctx$confirmationTran, _ctx$unpublishedFlagS, _ctx$inlineExtensionF;
251
+ var _ref6 = options || {},
252
+ _ref6$useLongPressSel = _ref6.useLongPressSelection,
253
+ useLongPressSelection = _ref6$useLongPressSel === void 0 ? false : _ref6$useLongPressSel;
254
+ var ctx = fg('platform_synced_block_patch_5') ? new SyncedBlockPluginContext() : undefined;
255
+ var confirmationTransactionRef = (_ctx$confirmationTran = ctx === null || ctx === void 0 ? void 0 : ctx.confirmationTransactionRef) !== null && _ctx$confirmationTran !== void 0 ? _ctx$confirmationTran : {
111
256
  current: undefined
112
257
  };
113
- // Track if a copy event occurred to distinguish copy from drag and drop
114
258
  var isCopyEvent = false;
115
- // Track which sync blocks have already triggered the unpublished flag
116
- var unpublishedFlagShown = new Set();
117
- // Track which sync blocks have already triggered the inline extension in sync block flag
118
- var inlineExtensionFlagShown = new Set();
259
+ var unpublishedFlagShown = (_ctx$unpublishedFlagS = ctx === null || ctx === void 0 ? void 0 : ctx.unpublishedFlagShown) !== null && _ctx$unpublishedFlagS !== void 0 ? _ctx$unpublishedFlagS : new Set();
260
+ var inlineExtensionFlagShown = (_ctx$inlineExtensionF = ctx === null || ctx === void 0 ? void 0 : ctx.inlineExtensionFlagShown) !== null && _ctx$inlineExtensionF !== void 0 ? _ctx$inlineExtensionF : new Set();
119
261
 
120
262
  // Set up callback to detect unpublished sync blocks when they're fetched
121
263
  syncBlockStore.referenceManager.setOnUnpublishedSyncBlockDetected(function (resourceId) {
122
264
  // Only show the flag once per sync block
123
265
  if (!unpublishedFlagShown.has(resourceId)) {
124
266
  unpublishedFlagShown.add(resourceId);
125
- // Use setTimeout to dispatch transaction in next tick and avoid re-entrant dispatch
126
- setTimeout(function () {
127
- api === null || api === void 0 || api.core.actions.execute(function (_ref4) {
128
- var tr = _ref4.tr;
267
+ deferDispatch(function () {
268
+ api === null || api === void 0 || api.core.actions.execute(function (_ref7) {
269
+ var tr = _ref7.tr;
129
270
  return tr.setMeta(syncedBlockPluginKey, {
130
271
  activeFlag: {
131
272
  id: FLAG_ID.UNPUBLISHED_SYNC_BLOCK_PASTED
132
273
  }
133
274
  });
134
275
  });
135
- }, 0);
276
+ });
136
277
  }
137
278
  });
138
279
  return new SafePlugin({
@@ -175,22 +316,29 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
175
316
  props: {
176
317
  nodeViews: {
177
318
  syncBlock: function syncBlock(node, view, getPos, _decorations) {
178
- // To support SSR, pass `syncBlockStore` here
179
- // and do not use lazy loading.
180
- // We cannot start rendering and then load `syncBlockStore` asynchronously,
181
- // because obtaining it is asynchronous (sharedPluginState.currentState() is delayed).
182
- return new SyncBlockView({
183
- api: api,
184
- options: options,
185
- node: node,
186
- view: view,
187
- getPos: getPos,
188
- portalProviderAPI: pmPluginFactoryParams.portalProviderAPI,
189
- eventDispatcher: pmPluginFactoryParams.eventDispatcher,
190
- syncBlockStore: syncBlockStore
191
- }).init();
319
+ return (
320
+ // To support SSR, pass `syncBlockStore` here
321
+ // and do not use lazy loading.
322
+ // We cannot start rendering and then load `syncBlockStore` asynchronously,
323
+ // because obtaining it is asynchronous (sharedPluginState.currentState() is delayed).
324
+ new SyncBlockView({
325
+ api: api,
326
+ options: options,
327
+ node: node,
328
+ view: view,
329
+ getPos: getPos,
330
+ portalProviderAPI: pmPluginFactoryParams.portalProviderAPI,
331
+ eventDispatcher: pmPluginFactoryParams.eventDispatcher,
332
+ syncBlockStore: syncBlockStore
333
+ }).init()
334
+ );
192
335
  },
193
- bodiedSyncBlock: lazyBodiedSyncBlockView({
336
+ bodiedSyncBlock: fg('platform_synced_block_patch_5') ? bodiedSyncBlockNodeView({
337
+ pluginOptions: options,
338
+ pmPluginFactoryParams: pmPluginFactoryParams,
339
+ api: api,
340
+ syncBlockStore: syncBlockStore
341
+ }) : lazyBodiedSyncBlockView({
194
342
  pluginOptions: options,
195
343
  pmPluginFactoryParams: pmPluginFactoryParams,
196
344
  api: api
@@ -239,17 +387,23 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
239
387
  return shouldIgnoreDomEvent(view, event, api);
240
388
  },
241
389
  copy: function copy() {
242
- isCopyEvent = true;
390
+ if (ctx) {
391
+ ctx.markCopyEvent();
392
+ } else {
393
+ isCopyEvent = true;
394
+ }
243
395
  return false;
244
396
  }
245
397
  },
246
- transformCopied: function transformCopied(slice, _ref5) {
247
- var state = _ref5.state;
398
+ transformCopied: function transformCopied(slice, _ref8) {
399
+ var state = _ref8.state;
248
400
  var pluginState = syncedBlockPluginKey.getState(state);
249
401
  var syncBlockStore = pluginState === null || pluginState === void 0 ? void 0 : pluginState.syncBlockStore;
250
402
  var schema = state.schema;
251
- var isCopy = isCopyEvent;
252
- isCopyEvent = false;
403
+ var isCopy = ctx ? ctx.consumeCopyEvent() : isCopyEvent;
404
+ if (!ctx) {
405
+ isCopyEvent = false;
406
+ }
253
407
  if (!syncBlockStore || !isCopy) {
254
408
  return slice;
255
409
  }
@@ -288,11 +442,10 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
288
442
 
289
443
  // Track newly added reference sync blocks before processing the transaction
290
444
  if (tr.docChanged && !tr.getMeta('isRemote')) {
291
- var _trackSyncBlocks = trackSyncBlocks(function (node) {
445
+ var _trackSyncBlocks3 = trackSyncBlocks(function (node) {
292
446
  return node.type.name === 'syncBlock';
293
447
  }, tr, state),
294
- added = _trackSyncBlocks.added;
295
- // Mark newly added sync blocks so we can detect unpublished status when data is fetched
448
+ added = _trackSyncBlocks3.added;
296
449
  added.forEach(function (nodeInfo) {
297
450
  var _nodeInfo$attrs;
298
451
  if ((_nodeInfo$attrs = nodeInfo.attrs) !== null && _nodeInfo$attrs !== void 0 && _nodeInfo$attrs.resourceId) {
@@ -300,26 +453,40 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
300
453
  }
301
454
  });
302
455
  }
303
-
304
- // Ignore transactions that don't change the document
305
- // or are from remote (collab) or already confirmed sync block deletion
306
- // We only care about local changes that change the document
307
- // and are not yet confirmed for sync block deletion
308
456
  if (!tr.docChanged || Boolean(tr.getMeta('isRemote')) || !isOffline && isConfirmedSyncBlockDeletion) {
309
457
  return true;
310
458
  }
311
- var _trackSyncBlocks2 = trackSyncBlocks(syncBlockStore.sourceManager.isSourceBlock, tr, state),
312
- bodiedSyncBlockRemoved = _trackSyncBlocks2.removed,
313
- bodiedSyncBlockAdded = _trackSyncBlocks2.added;
459
+ var _trackSyncBlocks4 = trackSyncBlocks(syncBlockStore.sourceManager.isSourceBlock, tr, state),
460
+ bodiedSyncBlockRemoved = _trackSyncBlocks4.removed,
461
+ bodiedSyncBlockAdded = _trackSyncBlocks4.added;
462
+ if (fg('platform_synced_block_patch_5')) {
463
+ return isOffline ? filterTransactionOffline({
464
+ tr: tr,
465
+ state: state,
466
+ api: api,
467
+ isConfirmedSyncBlockDeletion: isConfirmedSyncBlockDeletion,
468
+ bodiedSyncBlockRemoved: bodiedSyncBlockRemoved,
469
+ bodiedSyncBlockAdded: bodiedSyncBlockAdded
470
+ }) : filterTransactionOnline({
471
+ tr: tr,
472
+ state: state,
473
+ syncBlockStore: syncBlockStore,
474
+ api: api,
475
+ confirmationTransactionRef: confirmationTransactionRef,
476
+ bodiedSyncBlockRemoved: bodiedSyncBlockRemoved,
477
+ bodiedSyncBlockAdded: bodiedSyncBlockAdded,
478
+ inlineExtensionFlagShown: inlineExtensionFlagShown
479
+ });
480
+ }
314
481
  if (!isOffline) {
315
- var _trackSyncBlocks3 = trackSyncBlocks(function (node) {
482
+ var _trackSyncBlocks5 = trackSyncBlocks(function (node) {
316
483
  return node.type.name === 'syncBlock';
317
484
  }, tr, state),
318
- syncBlockRemoved = _trackSyncBlocks3.removed,
319
- syncBlockAdded = _trackSyncBlocks3.added;
320
- syncBlockRemoved.forEach(function (syncBlock) {
321
- var _api$analytics;
322
- api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.fireAnalyticsEvent({
485
+ _syncBlockRemoved = _trackSyncBlocks5.removed,
486
+ _syncBlockAdded = _trackSyncBlocks5.added;
487
+ _syncBlockRemoved.forEach(function (syncBlock) {
488
+ var _api$analytics4;
489
+ api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 || (_api$analytics4 = _api$analytics4.actions) === null || _api$analytics4 === void 0 || _api$analytics4.fireAnalyticsEvent({
323
490
  action: ACTION.DELETED,
324
491
  actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
325
492
  actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_DELETE,
@@ -330,10 +497,10 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
330
497
  eventType: EVENT_TYPE.OPERATIONAL
331
498
  });
332
499
  });
333
- syncBlockAdded.forEach(function (syncBlock) {
500
+ _syncBlockAdded.forEach(function (syncBlock) {
334
501
  if (fg('platform_synced_block_patch_3')) {
335
- var _api$analytics2;
336
- api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || (_api$analytics2 = _api$analytics2.actions) === null || _api$analytics2 === void 0 || _api$analytics2.fireAnalyticsEvent({
502
+ var _api$analytics5;
503
+ api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 || (_api$analytics5 = _api$analytics5.actions) === null || _api$analytics5 === void 0 || _api$analytics5.fireAnalyticsEvent({
337
504
  action: ACTION.INSERTED,
338
505
  actionSubject: ACTION_SUBJECT.DOCUMENT,
339
506
  actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK,
@@ -344,8 +511,8 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
344
511
  eventType: EVENT_TYPE.TRACK
345
512
  });
346
513
  } else {
347
- var _api$analytics3;
348
- api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 || (_api$analytics3 = _api$analytics3.actions) === null || _api$analytics3 === void 0 || _api$analytics3.fireAnalyticsEvent({
514
+ var _api$analytics6;
515
+ api === null || api === void 0 || (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 || (_api$analytics6 = _api$analytics6.actions) === null || _api$analytics6 === void 0 || _api$analytics6.fireAnalyticsEvent({
349
516
  action: ACTION.INSERTED,
350
517
  actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
351
518
  actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_CREATE,
@@ -362,7 +529,7 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
362
529
  return handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, getDeleteReason(tr));
363
530
  }
364
531
  if (bodiedSyncBlockAdded.length > 0) {
365
- if (Boolean(tr.getMeta(pmHistoryPluginKey))) {
532
+ if (tr.getMeta(pmHistoryPluginKey)) {
366
533
  // 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.
367
534
  // 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.
368
535
  // After true is returned here and the node is created, we delete the node in the filterTransaction immediately, which cancels out the creation
@@ -373,36 +540,34 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
373
540
  }
374
541
  showInlineExtensionInSyncBlockWarningIfNeeded(tr, state, api, inlineExtensionFlagShown);
375
542
  return true;
376
- } else {
377
- var _trackSyncBlocks4 = trackSyncBlocks(function (node) {
378
- return node.type.name === 'syncBlock';
379
- }, tr, state),
380
- _syncBlockRemoved = _trackSyncBlocks4.removed,
381
- _syncBlockAdded = _trackSyncBlocks4.added;
382
- var errorFlag = false;
543
+ }
544
+ var _trackSyncBlocks6 = trackSyncBlocks(function (node) {
545
+ return node.type.name === 'syncBlock';
546
+ }, tr, state),
547
+ syncBlockRemoved = _trackSyncBlocks6.removed,
548
+ syncBlockAdded = _trackSyncBlocks6.added;
549
+ var errorFlag = false;
383
550
 
384
- // Disable (bodied)syncBlock node deletion/creation/edition in offline mode and trigger an error flag instead
385
- if (isConfirmedSyncBlockDeletion || bodiedSyncBlockRemoved.length > 0 || _syncBlockRemoved.length > 0) {
386
- errorFlag = FLAG_ID.CANNOT_DELETE_WHEN_OFFLINE;
387
- } else if (bodiedSyncBlockAdded.length > 0 || _syncBlockAdded.length > 0) {
388
- errorFlag = FLAG_ID.CANNOT_CREATE_WHEN_OFFLINE;
389
- } else if (hasEditInSyncBlock(tr, state)) {
390
- errorFlag = FLAG_ID.CANNOT_EDIT_WHEN_OFFLINE;
391
- }
392
- if (errorFlag) {
393
- // Use setTimeout to dispatch transaction in next tick and avoid re-entrant dispatch
394
- setTimeout(function () {
395
- api === null || api === void 0 || api.core.actions.execute(function (_ref6) {
396
- var tr = _ref6.tr;
397
- return tr.setMeta(syncedBlockPluginKey, {
398
- activeFlag: {
399
- id: errorFlag
400
- }
401
- });
551
+ // Disable (bodied)syncBlock node deletion/creation/edition in offline mode and trigger an error flag instead
552
+ if (isConfirmedSyncBlockDeletion || bodiedSyncBlockRemoved.length > 0 || syncBlockRemoved.length > 0) {
553
+ errorFlag = FLAG_ID.CANNOT_DELETE_WHEN_OFFLINE;
554
+ } else if (bodiedSyncBlockAdded.length > 0 || syncBlockAdded.length > 0) {
555
+ errorFlag = FLAG_ID.CANNOT_CREATE_WHEN_OFFLINE;
556
+ } else if (hasEditInSyncBlock(tr, state)) {
557
+ errorFlag = FLAG_ID.CANNOT_EDIT_WHEN_OFFLINE;
558
+ }
559
+ if (errorFlag) {
560
+ deferDispatch(function () {
561
+ api === null || api === void 0 || api.core.actions.execute(function (_ref9) {
562
+ var tr = _ref9.tr;
563
+ return tr.setMeta(syncedBlockPluginKey, {
564
+ activeFlag: {
565
+ id: errorFlag
566
+ }
402
567
  });
403
- }, 0);
404
- return false;
405
- }
568
+ });
569
+ });
570
+ return false;
406
571
  }
407
572
  return true;
408
573
  },
@@ -419,29 +584,27 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
419
584
  try {
420
585
  var _loop = function _loop() {
421
586
  var tr = _step2.value;
422
- if (!tr.getMeta(pmHistoryPluginKey)) {
423
- return 0; // continue
424
- }
425
- var _trackSyncBlocks5 = trackSyncBlocks(syncBlockStore.sourceManager.isSourceBlock, tr, oldState),
426
- added = _trackSyncBlocks5.added;
427
- if (added.length > 0) {
428
- // Delete bodiedSyncBlock if it's originated from history, i.e. redo creation
429
- // See filterTransaction above for more details
430
- var _tr = newState.tr;
431
- added.forEach(function (node) {
432
- if (node.from !== undefined && node.to !== undefined) {
433
- _tr.delete(node.from, node.to);
434
- }
435
- });
436
- return {
437
- v: _tr
438
- };
587
+ if (tr.getMeta(pmHistoryPluginKey)) {
588
+ var _trackSyncBlocks7 = trackSyncBlocks(syncBlockStore.sourceManager.isSourceBlock, tr, oldState),
589
+ added = _trackSyncBlocks7.added;
590
+ if (added.length > 0) {
591
+ // Delete bodiedSyncBlock if it's originated from history, i.e. redo creation
592
+ // See filterTransaction above for more details
593
+ var _tr = newState.tr;
594
+ added.forEach(function (node) {
595
+ if (node.from !== undefined && node.to !== undefined) {
596
+ _tr.delete(node.from, node.to);
597
+ }
598
+ });
599
+ return {
600
+ v: _tr
601
+ };
602
+ }
439
603
  }
440
604
  },
441
605
  _ret;
442
606
  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
443
607
  _ret = _loop();
444
- if (_ret === 0) continue;
445
608
  if (_ret) return _ret.v;
446
609
  }
447
610
  } catch (err) {
@@ -4,6 +4,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
4
4
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
5
5
  import { FLAG_ID } from '../../types';
6
6
  import { syncedBlockPluginKey } from '../main';
7
+ import { deferDispatch } from './utils';
7
8
  var onRetry = function onRetry(api, resourceId) {
8
9
  return function () {
9
10
  var _api$core, _api$core2;
@@ -79,7 +80,7 @@ export var handleBodiedSyncBlockCreation = function handleBodiedSyncBlockCreatio
79
80
  to: node.to
80
81
  };
81
82
  var resourceId = node.attrs.resourceId;
82
- setTimeout(function () {
83
+ deferDispatch(function () {
83
84
  var _api$core3;
84
85
  api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(function (_ref2) {
85
86
  var tr = _ref2.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 var deferDispatch = function deferDispatch(fn) {
12
+ if (fg('platform_synced_block_patch_5')) {
13
+ queueMicrotask(fn);
14
+ } else {
15
+ setTimeout(fn, 0);
16
+ }
17
+ };
5
18
  export var findSyncBlock = function findSyncBlock(schema, selection) {
6
19
  var syncBlock = schema.nodes.syncBlock;
7
20
  return findSelectedNodeOfType(syncBlock)(selection);