@atlaskit/editor-plugin-synced-block 5.3.13 → 5.3.14

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/cjs/editor-commands/index.js +7 -5
  3. package/dist/cjs/pm-plugins/main.js +87 -25
  4. package/dist/cjs/pm-plugins/utils/handle-bodied-sync-block-creation.js +141 -0
  5. package/dist/cjs/pm-plugins/utils/track-sync-blocks.js +1 -0
  6. package/dist/cjs/syncedBlockPlugin.js +4 -2
  7. package/dist/cjs/types/index.js +1 -0
  8. package/dist/cjs/ui/Flag.js +5 -1
  9. package/dist/cjs/ui/floating-toolbar.js +3 -0
  10. package/dist/es2019/editor-commands/index.js +7 -5
  11. package/dist/es2019/pm-plugins/main.js +67 -17
  12. package/dist/es2019/pm-plugins/utils/handle-bodied-sync-block-creation.js +134 -0
  13. package/dist/es2019/pm-plugins/utils/track-sync-blocks.js +1 -0
  14. package/dist/es2019/syncedBlockPlugin.js +4 -2
  15. package/dist/es2019/types/index.js +1 -0
  16. package/dist/es2019/ui/Flag.js +5 -0
  17. package/dist/es2019/ui/floating-toolbar.js +3 -0
  18. package/dist/esm/editor-commands/index.js +7 -5
  19. package/dist/esm/pm-plugins/main.js +87 -25
  20. package/dist/esm/pm-plugins/utils/handle-bodied-sync-block-creation.js +134 -0
  21. package/dist/esm/pm-plugins/utils/track-sync-blocks.js +1 -0
  22. package/dist/esm/syncedBlockPlugin.js +4 -2
  23. package/dist/esm/types/index.js +1 -0
  24. package/dist/esm/ui/Flag.js +5 -1
  25. package/dist/esm/ui/floating-toolbar.js +3 -0
  26. package/dist/types/pm-plugins/main.d.ts +2 -1
  27. package/dist/types/pm-plugins/utils/handle-bodied-sync-block-creation.d.ts +9 -0
  28. package/dist/types/pm-plugins/utils/handle-bodied-sync-block-removal.d.ts +2 -2
  29. package/dist/types/types/index.d.ts +19 -1
  30. package/dist/types-ts4.5/pm-plugins/main.d.ts +2 -1
  31. package/dist/types-ts4.5/pm-plugins/utils/handle-bodied-sync-block-creation.d.ts +9 -0
  32. package/dist/types-ts4.5/pm-plugins/utils/handle-bodied-sync-block-removal.d.ts +2 -2
  33. package/dist/types-ts4.5/types/index.d.ts +19 -1
  34. package/package.json +1 -1
@@ -7,15 +7,39 @@ import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
7
7
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
8
8
  import { DecorationSet, Decoration } from '@atlaskit/editor-prosemirror/view';
9
9
  import { convertPMNodesToSyncBlockNodes, rebaseTransaction } from '@atlaskit/editor-synced-block-provider';
10
+ import { fg } from '@atlaskit/platform-feature-flags';
10
11
  import { lazyBodiedSyncBlockView } from '../nodeviews/bodiedLazySyncedBlock';
11
12
  import { SyncBlock as SyncBlockView } from '../nodeviews/syncedBlock';
12
13
  import { FLAG_ID } from '../types';
14
+ import { handleBodiedSyncBlockCreation } from './utils/handle-bodied-sync-block-creation';
13
15
  import { handleBodiedSyncBlockRemoval } from './utils/handle-bodied-sync-block-removal';
14
16
  import { shouldIgnoreDomEvent } from './utils/ignore-dom-event';
15
17
  import { calculateDecorations } from './utils/selection-decorations';
16
18
  import { hasEditInSyncBlock, trackSyncBlocks } from './utils/track-sync-blocks';
17
19
  import { sliceFullyContainsNode } from './utils/utils';
18
20
  export const syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
21
+ const mapRetryCreationPosMap = (oldMap, newRetryCreationPos, mapPos) => {
22
+ const resourceId = newRetryCreationPos === null || newRetryCreationPos === void 0 ? void 0 : newRetryCreationPos.resourceId;
23
+ const newMap = new Map(oldMap);
24
+ if (resourceId) {
25
+ const pos = newRetryCreationPos.pos;
26
+ if (!pos) {
27
+ newMap.delete(resourceId);
28
+ } else {
29
+ newMap.set(resourceId, pos);
30
+ }
31
+ }
32
+ if (newMap.size === 0) {
33
+ return newMap;
34
+ }
35
+ for (const [id, pos] of newMap.entries()) {
36
+ newMap.set(id, {
37
+ from: mapPos(pos.from),
38
+ to: mapPos(pos.to)
39
+ });
40
+ }
41
+ return newMap;
42
+ };
19
43
  const showCopiedFlag = api => {
20
44
  // Use setTimeout to dispatch transaction in next tick and avoid re-entrant dispatch
21
45
  setTimeout(() => {
@@ -77,7 +101,8 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
77
101
  return {
78
102
  selectionDecorationSet: calculateDecorations(instance.doc, instance.selection, instance.schema),
79
103
  activeFlag: false,
80
- syncBlockStore: syncBlockStore
104
+ syncBlockStore: syncBlockStore,
105
+ retryCreationPosMap: new Map()
81
106
  };
82
107
  },
83
108
  apply: (tr, currentPluginState, oldEditorState) => {
@@ -86,16 +111,23 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
86
111
  const {
87
112
  activeFlag,
88
113
  selectionDecorationSet,
89
- bodiedSyncBlockDeletionStatus
114
+ bodiedSyncBlockDeletionStatus,
115
+ retryCreationPosMap
90
116
  } = currentPluginState;
91
117
  let newDecorationSet = selectionDecorationSet.map(tr.mapping, tr.doc);
92
118
  if (!tr.selection.eq(oldEditorState.selection)) {
93
119
  newDecorationSet = calculateDecorations(tr.doc, tr.selection, tr.doc.type.schema);
94
120
  }
121
+ let newRetryCreationPosMap = retryCreationPosMap;
122
+ if (fg('platform_synced_block_patch_1')) {
123
+ const newPosEntry = meta === null || meta === void 0 ? void 0 : meta.retryCreationPos;
124
+ newRetryCreationPosMap = mapRetryCreationPosMap(retryCreationPosMap, newPosEntry, tr.mapping.map.bind(tr.mapping));
125
+ }
95
126
  return {
96
127
  activeFlag: (_meta$activeFlag = meta === null || meta === void 0 ? void 0 : meta.activeFlag) !== null && _meta$activeFlag !== void 0 ? _meta$activeFlag : activeFlag,
97
128
  selectionDecorationSet: newDecorationSet,
98
129
  syncBlockStore: syncBlockStore,
130
+ retryCreationPosMap: newRetryCreationPosMap,
99
131
  bodiedSyncBlockDeletionStatus: (_meta$bodiedSyncBlock = meta === null || meta === void 0 ? void 0 : meta.bodiedSyncBlockDeletionStatus) !== null && _meta$bodiedSyncBlock !== void 0 ? _meta$bodiedSyncBlock : bodiedSyncBlockDeletionStatus
100
132
  };
101
133
  }
@@ -125,8 +157,10 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
125
157
  })
126
158
  },
127
159
  decorations: state => {
128
- var _syncedBlockPluginKey, _syncedBlockPluginKey2, _api$connectivity, _api$connectivity$sha, _api$editorViewMode, _api$editorViewMode$s;
129
- const selectionDecorationSet = (_syncedBlockPluginKey = (_syncedBlockPluginKey2 = syncedBlockPluginKey.getState(state)) === null || _syncedBlockPluginKey2 === void 0 ? void 0 : _syncedBlockPluginKey2.selectionDecorationSet) !== null && _syncedBlockPluginKey !== void 0 ? _syncedBlockPluginKey : DecorationSet.empty;
160
+ var _currentPluginState$s, _api$connectivity, _api$connectivity$sha, _api$editorViewMode, _api$editorViewMode$s;
161
+ const currentPluginState = syncedBlockPluginKey.getState(state);
162
+ const selectionDecorationSet = (_currentPluginState$s = currentPluginState === null || currentPluginState === void 0 ? void 0 : currentPluginState.selectionDecorationSet) !== null && _currentPluginState$s !== void 0 ? _currentPluginState$s : DecorationSet.empty;
163
+ const syncBlockStore = currentPluginState === null || currentPluginState === void 0 ? void 0 : currentPluginState.syncBlockStore;
130
164
  const {
131
165
  doc
132
166
  } = state;
@@ -134,6 +168,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
134
168
  const isViewMode = (api === null || api === void 0 ? void 0 : (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : (_api$editorViewMode$s = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode$s === void 0 ? void 0 : _api$editorViewMode$s.mode) === 'view';
135
169
  const offlineDecorations = [];
136
170
  const viewModeDecorations = [];
171
+ const loadingDecorations = [];
137
172
  state.doc.descendants((node, pos) => {
138
173
  if (node.type.name === 'bodiedSyncBlock' && isOffline) {
139
174
  offlineDecorations.push(Decoration.node(pos, pos + node.nodeSize, {
@@ -145,8 +180,13 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
145
180
  class: SyncBlockStateCssClassName.viewModeClassName
146
181
  }));
147
182
  }
183
+ if (node.type.name === 'bodiedSyncBlock' && syncBlockStore.sourceManager.isPendingCreation(node.attrs.resourceId) && fg('platform_synced_block_patch_1')) {
184
+ loadingDecorations.push(Decoration.node(pos, pos + node.nodeSize, {
185
+ class: SyncBlockStateCssClassName.creationLoadingClassName
186
+ }));
187
+ }
148
188
  });
149
- return selectionDecorationSet.add(doc, offlineDecorations).add(doc, viewModeDecorations);
189
+ return selectionDecorationSet.add(doc, offlineDecorations).add(doc, viewModeDecorations).add(doc, loadingDecorations);
150
190
  },
151
191
  handleClickOn: createSelectionClickHandler(['bodiedSyncBlock'], target => !!target.closest(`.${BodiedSyncBlockSharedCssClassName.prefix}`), {
152
192
  useLongPressSelection
@@ -209,6 +249,12 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
209
249
  var _api$connectivity2, _api$connectivity2$sh;
210
250
  const isOffline = isOfflineMode(api === null || api === void 0 ? void 0 : (_api$connectivity2 = api.connectivity) === null || _api$connectivity2 === void 0 ? void 0 : (_api$connectivity2$sh = _api$connectivity2.sharedState.currentState()) === null || _api$connectivity2$sh === void 0 ? void 0 : _api$connectivity2$sh.mode);
211
251
  const isConfirmedSyncBlockDeletion = Boolean(tr.getMeta('isConfirmedSyncBlockDeletion'));
252
+ const hasNoPendingRequest = fg('platform_synced_block_patch_1') ? false :
253
+ // requireConfirmationBeforeDelete is always true, so this evaluates to false and hence redundant
254
+ !(syncBlockStore !== null && syncBlockStore !== void 0 && syncBlockStore.sourceManager.requireConfirmationBeforeDelete()) && !syncBlockStore.sourceManager.hasPendingCreation();
255
+ const isCommitsCreation = fg('platform_synced_block_patch_1') ? false :
256
+ // For patch 1, we don't intercept the insert transaction, hence it's redundant
257
+ Boolean(tr.getMeta('isCommitSyncBlockCreation'));
212
258
 
213
259
  // Track newly added reference sync blocks before processing the transaction
214
260
  if (tr.docChanged && !tr.getMeta('isRemote')) {
@@ -228,7 +274,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
228
274
  // or are from remote (collab) or already confirmed sync block deletion
229
275
  // We only care about local changes that change the document
230
276
  // and are not yet confirmed for sync block deletion
231
- if (!tr.docChanged || !(syncBlockStore !== null && syncBlockStore !== void 0 && syncBlockStore.sourceManager.requireConfirmationBeforeDelete()) && !syncBlockStore.sourceManager.hasPendingCreation() || Boolean(tr.getMeta('isRemote')) || Boolean(tr.getMeta('isCommitSyncBlockCreation')) || !isOffline && isConfirmedSyncBlockDeletion) {
277
+ if (!tr.docChanged || hasNoPendingRequest || Boolean(tr.getMeta('isRemote')) || isCommitsCreation || !isOffline && isConfirmedSyncBlockDeletion) {
232
278
  return true;
233
279
  }
234
280
  const {
@@ -277,18 +323,22 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
277
323
  // After true is returned here and the node is created, we delete the node in the filterTransaction immediately, which cancels out the creation
278
324
  return true;
279
325
  }
280
-
281
- // If there is bodiedSyncBlock node addition and it's waiting for the result of saving the node to backend (syncBlockStore.hasPendingCreation()),
282
- // we need to intercept the transaction and save it in insert callback so that we only insert it to the document when backend call if backend call is successful
283
- // The callback will be evoked by in SourceSyncBlockStoreManager.commitPendingCreation
284
- syncBlockStore.sourceManager.registerCreationCallback(() => {
285
- var _api$core;
286
- api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(() => {
287
- return tr.setMeta('isCommitSyncBlockCreation', true);
326
+ if (fg('platform_synced_block_patch_1')) {
327
+ handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, state, api);
328
+ return true;
329
+ } else {
330
+ // If there is bodiedSyncBlock node addition and it's waiting for the result of saving the node to backend (syncBlockStore.hasPendingCreation()),
331
+ // we need to intercept the transaction and save it in insert callback so that we only insert it to the document when backend call if backend call is successful
332
+ // The callback will be evoked by in SourceSyncBlockStoreManager.commitPendingCreation
333
+ syncBlockStore.sourceManager.registerCreationCallback(() => {
334
+ var _api$core;
335
+ api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(() => {
336
+ return tr.setMeta('isCommitSyncBlockCreation', true);
337
+ });
338
+ api === null || api === void 0 ? void 0 : api.core.actions.focus();
288
339
  });
289
- api === null || api === void 0 ? void 0 : api.core.actions.focus();
290
- });
291
- return false;
340
+ return false;
341
+ }
292
342
  }
293
343
  } else {
294
344
  const {
@@ -0,0 +1,134 @@
1
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
2
+ import { FLAG_ID } from '../../types';
3
+ import { syncedBlockPluginKey } from '../main';
4
+ const onRetry = (api, resourceId) => {
5
+ return () => {
6
+ var _api$core, _api$core2;
7
+ api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.focus();
8
+ api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.execute(({
9
+ tr
10
+ }) => {
11
+ var _api$syncedBlock, _api$syncedBlock$shar, _api$syncedBlock$shar2, _api$syncedBlock2;
12
+ const pos = api === null || api === void 0 ? void 0 : (_api$syncedBlock = api.syncedBlock) === null || _api$syncedBlock === void 0 ? void 0 : (_api$syncedBlock$shar = _api$syncedBlock.sharedState.currentState()) === null || _api$syncedBlock$shar === void 0 ? void 0 : (_api$syncedBlock$shar2 = _api$syncedBlock$shar.retryCreationPosMap) === null || _api$syncedBlock$shar2 === void 0 ? void 0 : _api$syncedBlock$shar2.get(resourceId);
13
+ const from = pos === null || pos === void 0 ? void 0 : pos.from;
14
+ const to = pos === null || pos === void 0 ? void 0 : pos.to;
15
+ if (from === undefined || to === undefined) {
16
+ return tr;
17
+ }
18
+ tr.setSelection(TextSelection.create(tr.doc, from, to)).setMeta(syncedBlockPluginKey, {
19
+ activeFlag: false
20
+ });
21
+ api === null || api === void 0 ? void 0 : (_api$syncedBlock2 = api.syncedBlock) === null || _api$syncedBlock2 === void 0 ? void 0 : _api$syncedBlock2.commands.insertSyncedBlock()({
22
+ tr
23
+ });
24
+ return tr;
25
+ });
26
+ };
27
+ };
28
+ const getRevertCreationPos = (api, doc, resourceId) => {
29
+ var _api$syncedBlock3, _api$syncedBlock3$sha, _api$syncedBlock3$sha2;
30
+ const retryCreationPos = api === null || api === void 0 ? void 0 : (_api$syncedBlock3 = api.syncedBlock) === null || _api$syncedBlock3 === void 0 ? void 0 : (_api$syncedBlock3$sha = _api$syncedBlock3.sharedState.currentState()) === null || _api$syncedBlock3$sha === void 0 ? void 0 : (_api$syncedBlock3$sha2 = _api$syncedBlock3$sha.retryCreationPosMap) === null || _api$syncedBlock3$sha2 === void 0 ? void 0 : _api$syncedBlock3$sha2.get(resourceId);
31
+ if (retryCreationPos) {
32
+ return retryCreationPos;
33
+ }
34
+
35
+ // Fallback to find the positions in case BE call returns before plugin state becomes available
36
+ // which is highly unlikely
37
+ let currentPos;
38
+ doc.descendants((node, pos) => {
39
+ if (currentPos) {
40
+ return false;
41
+ }
42
+ if (node.type.name === 'bodiedSyncBlock' && resourceId === node.attrs.resourceId) {
43
+ currentPos = {
44
+ from: pos,
45
+ to: pos + node.nodeSize
46
+ };
47
+ return false;
48
+ }
49
+ });
50
+ return currentPos;
51
+ };
52
+ const buildRevertCreationTr = (tr, pos) => {
53
+ var _tr$doc$nodeAt;
54
+ const content = (_tr$doc$nodeAt = tr.doc.nodeAt(pos.from)) === null || _tr$doc$nodeAt === void 0 ? void 0 : _tr$doc$nodeAt.content;
55
+ if (content) {
56
+ tr.replaceWith(pos.from, pos.to, content);
57
+ const contentFrom = tr.mapping.map(pos.from);
58
+ tr.setSelection(TextSelection.create(tr.doc, contentFrom, contentFrom + content.size));
59
+ } else {
60
+ tr.delete(pos.from, pos.to);
61
+ }
62
+ return tr;
63
+ };
64
+
65
+ /**
66
+ *
67
+ * Save the new bodiedSyncBlock to backend with empty content and handles revert (if failed) and retry flow
68
+ */
69
+ export const handleBodiedSyncBlockCreation = (bodiedSyncBlockAdded, editorState, api) => {
70
+ const syncBlockStore = syncedBlockPluginKey.getState(editorState).syncBlockStore;
71
+ bodiedSyncBlockAdded.forEach(node => {
72
+ if (node.from === undefined || node.to === undefined) {
73
+ return;
74
+ }
75
+ const retryCreationPos = {
76
+ from: node.from,
77
+ to: node.to
78
+ };
79
+ const resourceId = node.attrs.resourceId;
80
+ setTimeout(() => {
81
+ var _api$core3;
82
+ api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(({
83
+ tr
84
+ }) => {
85
+ return tr.setMeta(syncedBlockPluginKey, {
86
+ retryCreationPos: {
87
+ resourceId,
88
+ pos: retryCreationPos
89
+ }
90
+ });
91
+ });
92
+ });
93
+ syncBlockStore.sourceManager.createBodiedSyncBlockNode(node.attrs, success => {
94
+ if (success) {
95
+ var _api$core4, _api$core5;
96
+ api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions.execute(({
97
+ tr
98
+ }) => {
99
+ return tr.setMeta(syncedBlockPluginKey, {
100
+ retryCreationPos: {
101
+ resourceId,
102
+ pos: undefined
103
+ }
104
+ });
105
+ });
106
+ api === null || api === void 0 ? void 0 : (_api$core5 = api.core) === null || _api$core5 === void 0 ? void 0 : _api$core5.actions.focus();
107
+ } else {
108
+ var _api$core6;
109
+ api === null || api === void 0 ? void 0 : (_api$core6 = api.core) === null || _api$core6 === void 0 ? void 0 : _api$core6.actions.execute(({
110
+ tr
111
+ }) => {
112
+ const revertCreationPos = getRevertCreationPos(api, tr.doc, resourceId);
113
+ if (!revertCreationPos) {
114
+ return tr;
115
+ }
116
+ const revertTr = buildRevertCreationTr(tr, revertCreationPos);
117
+ return revertTr.setMeta('isConfirmedSyncBlockDeletion', true).setMeta('addToHistory', false).setMeta(syncedBlockPluginKey, {
118
+ activeFlag: {
119
+ id: FLAG_ID.CANNOT_CREATE_SYNC_BLOCK,
120
+ onRetry: onRetry(api, resourceId),
121
+ onDismissed: tr => tr.setMeta(syncedBlockPluginKey, {
122
+ ...tr.getMeta(syncedBlockPluginKey),
123
+ retryCreationPos: {
124
+ resourceId,
125
+ pos: undefined
126
+ }
127
+ })
128
+ }
129
+ });
130
+ });
131
+ }
132
+ }, node.node);
133
+ });
134
+ };
@@ -66,6 +66,7 @@ export const trackSyncBlocks = (predicate, tr, state) => {
66
66
  const syncBlockAttr = node.attrs;
67
67
  syncBlockMapNew[syncBlockAttr.localId] = {
68
68
  attrs: syncBlockAttr,
69
+ node: node,
69
70
  from: offset,
70
71
  to: offset + node.nodeSize
71
72
  };
@@ -135,12 +135,14 @@ export const syncedBlockPlugin = ({
135
135
  const {
136
136
  activeFlag,
137
137
  syncBlockStore: currentSyncBlockStore,
138
- bodiedSyncBlockDeletionStatus
138
+ bodiedSyncBlockDeletionStatus,
139
+ retryCreationPosMap
139
140
  } = syncedBlockPluginKey.getState(editorState);
140
141
  return {
141
142
  activeFlag,
142
143
  syncBlockStore: currentSyncBlockStore,
143
- bodiedSyncBlockDeletionStatus
144
+ bodiedSyncBlockDeletionStatus,
145
+ retryCreationPosMap
144
146
  };
145
147
  }
146
148
  };
@@ -5,6 +5,7 @@ export let FLAG_ID = /*#__PURE__*/function (FLAG_ID) {
5
5
  FLAG_ID["FAIL_TO_DELETE"] = "fail-to-delete";
6
6
  FLAG_ID["SYNC_BLOCK_COPIED"] = "sync-block-copied";
7
7
  FLAG_ID["UNPUBLISHED_SYNC_BLOCK_PASTED"] = "unpublished-sync-block-pasted";
8
+ FLAG_ID["CANNOT_CREATE_SYNC_BLOCK"] = "cannot-create-sync-block";
8
9
  return FLAG_ID;
9
10
  }({});
10
11
  export const SYNCED_BLOCK_BUTTON_TEST_ID = {
@@ -37,6 +37,11 @@ const flagMap = {
37
37
  title: messages.unpublishedSyncBlockPastedTitle,
38
38
  description: messages.unpublishedSyncBlockPastedDescription,
39
39
  type: 'info'
40
+ },
41
+ [FLAG_ID.CANNOT_CREATE_SYNC_BLOCK]: {
42
+ title: messages.cannotCreateSyncBlockTitle,
43
+ description: messages.CannotCreateSyncBlockDescription,
44
+ type: 'error'
40
45
  }
41
46
  };
42
47
  export const Flag = ({
@@ -20,6 +20,9 @@ export const getToolbarConfig = (state, intl, api, syncBlockStore) => {
20
20
  if (!syncBlockObject) {
21
21
  return;
22
22
  }
23
+ if (syncBlockStore.sourceManager.isPendingCreation(syncBlockObject.node.attrs.resourceId) && fg('platform_synced_block_patch_1')) {
24
+ return;
25
+ }
23
26
  const syncBlockInstance = syncBlockStore.referenceManager.getFromCache(syncBlockObject.node.attrs.resourceId);
24
27
  const isUnsyncedBlock = (syncBlockInstance === null || syncBlockInstance === void 0 ? void 0 : (_syncBlockInstance$er = syncBlockInstance.error) === null || _syncBlockInstance$er === void 0 ? void 0 : _syncBlockInstance$er.type) === SyncBlockError.NotFound;
25
28
  const isErroredBlock = syncBlockInstance === null || syncBlockInstance === void 0 ? void 0 : syncBlockInstance.error;
@@ -38,7 +38,9 @@ export var createSyncedBlock = function createSyncedBlock(_ref) {
38
38
 
39
39
  // Save the new node with empty content to backend
40
40
  // This is so that the node can be copied and referenced without the source being saved/published
41
- syncBlockStore.sourceManager.createBodiedSyncBlockNode(attrs);
41
+ if (!fg('platform_synced_block_patch_1')) {
42
+ syncBlockStore.sourceManager.createBodiedSyncBlockNode(attrs, function () {});
43
+ }
42
44
  if (typeAheadInsert) {
43
45
  tr = typeAheadInsert(newBodiedSyncBlockNode);
44
46
  } else {
@@ -75,15 +77,15 @@ export var createSyncedBlock = function createSyncedBlock(_ref) {
75
77
 
76
78
  // Save the new node with empty content to backend
77
79
  // This is so that the node can be copied and referenced without the source being saved/published
78
- syncBlockStore.sourceManager.createBodiedSyncBlockNode(_attrs, _newBodiedSyncBlockNode);
80
+ if (!fg('platform_synced_block_patch_1')) {
81
+ // Moved to appendTransaction
82
+ syncBlockStore.sourceManager.createBodiedSyncBlockNode(_attrs, function () {}, _newBodiedSyncBlockNode);
83
+ }
79
84
  tr.replaceWith(conversionInfo.from, conversionInfo.to, _newBodiedSyncBlockNode).scrollIntoView();
80
85
 
81
86
  // set selection to the start of the previous selection for the position taken up by the start of the new synced block
82
87
  tr.setSelection(TextSelection.create(tr.doc, conversionInfo.from));
83
88
  }
84
-
85
- // This transaction will be intercepted in filterTransaction and dispatched when saving to backend succeeds
86
- // see filterTransaction for more details
87
89
  return tr;
88
90
  };
89
91
  export var copySyncedBlockReferenceToClipboardEditorCommand = function copySyncedBlockReferenceToClipboardEditorCommand(syncBlockStore, inputMethod, api) {
@@ -1,9 +1,10 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
+ 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; }
4
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
2
5
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
3
6
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
4
7
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
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; }
6
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7
8
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
8
9
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
9
10
  import { createSelectionClickHandler } from '@atlaskit/editor-common/selection';
@@ -13,15 +14,50 @@ import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
13
14
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
14
15
  import { DecorationSet, Decoration } from '@atlaskit/editor-prosemirror/view';
15
16
  import { convertPMNodesToSyncBlockNodes, rebaseTransaction } from '@atlaskit/editor-synced-block-provider';
17
+ import { fg } from '@atlaskit/platform-feature-flags';
16
18
  import { lazyBodiedSyncBlockView } from '../nodeviews/bodiedLazySyncedBlock';
17
19
  import { SyncBlock as SyncBlockView } from '../nodeviews/syncedBlock';
18
20
  import { FLAG_ID } from '../types';
21
+ import { handleBodiedSyncBlockCreation } from './utils/handle-bodied-sync-block-creation';
19
22
  import { handleBodiedSyncBlockRemoval } from './utils/handle-bodied-sync-block-removal';
20
23
  import { shouldIgnoreDomEvent } from './utils/ignore-dom-event';
21
24
  import { calculateDecorations } from './utils/selection-decorations';
22
25
  import { hasEditInSyncBlock, trackSyncBlocks } from './utils/track-sync-blocks';
23
26
  import { sliceFullyContainsNode } from './utils/utils';
24
27
  export var syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
28
+ var mapRetryCreationPosMap = function mapRetryCreationPosMap(oldMap, newRetryCreationPos, mapPos) {
29
+ var resourceId = newRetryCreationPos === null || newRetryCreationPos === void 0 ? void 0 : newRetryCreationPos.resourceId;
30
+ var newMap = new Map(oldMap);
31
+ if (resourceId) {
32
+ var pos = newRetryCreationPos.pos;
33
+ if (!pos) {
34
+ newMap.delete(resourceId);
35
+ } else {
36
+ newMap.set(resourceId, pos);
37
+ }
38
+ }
39
+ if (newMap.size === 0) {
40
+ return newMap;
41
+ }
42
+ var _iterator = _createForOfIteratorHelper(newMap.entries()),
43
+ _step;
44
+ try {
45
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
46
+ var _step$value = _slicedToArray(_step.value, 2),
47
+ id = _step$value[0],
48
+ _pos = _step$value[1];
49
+ newMap.set(id, {
50
+ from: mapPos(_pos.from),
51
+ to: mapPos(_pos.to)
52
+ });
53
+ }
54
+ } catch (err) {
55
+ _iterator.e(err);
56
+ } finally {
57
+ _iterator.f();
58
+ }
59
+ return newMap;
60
+ };
25
61
  var showCopiedFlag = function showCopiedFlag(api) {
26
62
  // Use setTimeout to dispatch transaction in next tick and avoid re-entrant dispatch
27
63
  setTimeout(function () {
@@ -83,7 +119,8 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
83
119
  return {
84
120
  selectionDecorationSet: calculateDecorations(instance.doc, instance.selection, instance.schema),
85
121
  activeFlag: false,
86
- syncBlockStore: syncBlockStore
122
+ syncBlockStore: syncBlockStore,
123
+ retryCreationPosMap: new Map()
87
124
  };
88
125
  },
89
126
  apply: function apply(tr, currentPluginState, oldEditorState) {
@@ -91,15 +128,22 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
91
128
  var meta = tr.getMeta(syncedBlockPluginKey);
92
129
  var activeFlag = currentPluginState.activeFlag,
93
130
  selectionDecorationSet = currentPluginState.selectionDecorationSet,
94
- bodiedSyncBlockDeletionStatus = currentPluginState.bodiedSyncBlockDeletionStatus;
131
+ bodiedSyncBlockDeletionStatus = currentPluginState.bodiedSyncBlockDeletionStatus,
132
+ retryCreationPosMap = currentPluginState.retryCreationPosMap;
95
133
  var newDecorationSet = selectionDecorationSet.map(tr.mapping, tr.doc);
96
134
  if (!tr.selection.eq(oldEditorState.selection)) {
97
135
  newDecorationSet = calculateDecorations(tr.doc, tr.selection, tr.doc.type.schema);
98
136
  }
137
+ var newRetryCreationPosMap = retryCreationPosMap;
138
+ if (fg('platform_synced_block_patch_1')) {
139
+ var newPosEntry = meta === null || meta === void 0 ? void 0 : meta.retryCreationPos;
140
+ newRetryCreationPosMap = mapRetryCreationPosMap(retryCreationPosMap, newPosEntry, tr.mapping.map.bind(tr.mapping));
141
+ }
99
142
  return {
100
143
  activeFlag: (_meta$activeFlag = meta === null || meta === void 0 ? void 0 : meta.activeFlag) !== null && _meta$activeFlag !== void 0 ? _meta$activeFlag : activeFlag,
101
144
  selectionDecorationSet: newDecorationSet,
102
145
  syncBlockStore: syncBlockStore,
146
+ retryCreationPosMap: newRetryCreationPosMap,
103
147
  bodiedSyncBlockDeletionStatus: (_meta$bodiedSyncBlock = meta === null || meta === void 0 ? void 0 : meta.bodiedSyncBlockDeletionStatus) !== null && _meta$bodiedSyncBlock !== void 0 ? _meta$bodiedSyncBlock : bodiedSyncBlockDeletionStatus
104
148
  };
105
149
  }
@@ -129,13 +173,16 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
129
173
  })
130
174
  },
131
175
  decorations: function decorations(state) {
132
- var _syncedBlockPluginKey, _syncedBlockPluginKey2, _api$connectivity, _api$editorViewMode;
133
- var selectionDecorationSet = (_syncedBlockPluginKey = (_syncedBlockPluginKey2 = syncedBlockPluginKey.getState(state)) === null || _syncedBlockPluginKey2 === void 0 ? void 0 : _syncedBlockPluginKey2.selectionDecorationSet) !== null && _syncedBlockPluginKey !== void 0 ? _syncedBlockPluginKey : DecorationSet.empty;
176
+ var _currentPluginState$s, _api$connectivity, _api$editorViewMode;
177
+ var currentPluginState = syncedBlockPluginKey.getState(state);
178
+ var selectionDecorationSet = (_currentPluginState$s = currentPluginState === null || currentPluginState === void 0 ? void 0 : currentPluginState.selectionDecorationSet) !== null && _currentPluginState$s !== void 0 ? _currentPluginState$s : DecorationSet.empty;
179
+ var syncBlockStore = currentPluginState === null || currentPluginState === void 0 ? void 0 : currentPluginState.syncBlockStore;
134
180
  var doc = state.doc;
135
181
  var isOffline = isOfflineMode(api === null || api === void 0 || (_api$connectivity = api.connectivity) === null || _api$connectivity === void 0 || (_api$connectivity = _api$connectivity.sharedState.currentState()) === null || _api$connectivity === void 0 ? void 0 : _api$connectivity.mode);
136
182
  var isViewMode = (api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 || (_api$editorViewMode = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.mode) === 'view';
137
183
  var offlineDecorations = [];
138
184
  var viewModeDecorations = [];
185
+ var loadingDecorations = [];
139
186
  state.doc.descendants(function (node, pos) {
140
187
  if (node.type.name === 'bodiedSyncBlock' && isOffline) {
141
188
  offlineDecorations.push(Decoration.node(pos, pos + node.nodeSize, {
@@ -147,8 +194,13 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
147
194
  class: SyncBlockStateCssClassName.viewModeClassName
148
195
  }));
149
196
  }
197
+ if (node.type.name === 'bodiedSyncBlock' && syncBlockStore.sourceManager.isPendingCreation(node.attrs.resourceId) && fg('platform_synced_block_patch_1')) {
198
+ loadingDecorations.push(Decoration.node(pos, pos + node.nodeSize, {
199
+ class: SyncBlockStateCssClassName.creationLoadingClassName
200
+ }));
201
+ }
150
202
  });
151
- return selectionDecorationSet.add(doc, offlineDecorations).add(doc, viewModeDecorations);
203
+ return selectionDecorationSet.add(doc, offlineDecorations).add(doc, viewModeDecorations).add(doc, loadingDecorations);
152
204
  },
153
205
  handleClickOn: createSelectionClickHandler(['bodiedSyncBlock'], function (target) {
154
206
  return !!target.closest(".".concat(BodiedSyncBlockSharedCssClassName.prefix));
@@ -209,6 +261,12 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
209
261
  var _api$connectivity2;
210
262
  var isOffline = isOfflineMode(api === null || api === void 0 || (_api$connectivity2 = api.connectivity) === null || _api$connectivity2 === void 0 || (_api$connectivity2 = _api$connectivity2.sharedState.currentState()) === null || _api$connectivity2 === void 0 ? void 0 : _api$connectivity2.mode);
211
263
  var isConfirmedSyncBlockDeletion = Boolean(tr.getMeta('isConfirmedSyncBlockDeletion'));
264
+ var hasNoPendingRequest = fg('platform_synced_block_patch_1') ? false :
265
+ // requireConfirmationBeforeDelete is always true, so this evaluates to false and hence redundant
266
+ !(syncBlockStore !== null && syncBlockStore !== void 0 && syncBlockStore.sourceManager.requireConfirmationBeforeDelete()) && !syncBlockStore.sourceManager.hasPendingCreation();
267
+ var isCommitsCreation = fg('platform_synced_block_patch_1') ? false :
268
+ // For patch 1, we don't intercept the insert transaction, hence it's redundant
269
+ Boolean(tr.getMeta('isCommitSyncBlockCreation'));
212
270
 
213
271
  // Track newly added reference sync blocks before processing the transaction
214
272
  if (tr.docChanged && !tr.getMeta('isRemote')) {
@@ -229,7 +287,7 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
229
287
  // or are from remote (collab) or already confirmed sync block deletion
230
288
  // We only care about local changes that change the document
231
289
  // and are not yet confirmed for sync block deletion
232
- if (!tr.docChanged || !(syncBlockStore !== null && syncBlockStore !== void 0 && syncBlockStore.sourceManager.requireConfirmationBeforeDelete()) && !syncBlockStore.sourceManager.hasPendingCreation() || Boolean(tr.getMeta('isRemote')) || Boolean(tr.getMeta('isCommitSyncBlockCreation')) || !isOffline && isConfirmedSyncBlockDeletion) {
290
+ if (!tr.docChanged || hasNoPendingRequest || Boolean(tr.getMeta('isRemote')) || isCommitsCreation || !isOffline && isConfirmedSyncBlockDeletion) {
233
291
  return true;
234
292
  }
235
293
  var _trackSyncBlocks2 = trackSyncBlocks(syncBlockStore.sourceManager.isSourceBlock, tr, state),
@@ -278,18 +336,22 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
278
336
  // After true is returned here and the node is created, we delete the node in the filterTransaction immediately, which cancels out the creation
279
337
  return true;
280
338
  }
281
-
282
- // If there is bodiedSyncBlock node addition and it's waiting for the result of saving the node to backend (syncBlockStore.hasPendingCreation()),
283
- // we need to intercept the transaction and save it in insert callback so that we only insert it to the document when backend call if backend call is successful
284
- // The callback will be evoked by in SourceSyncBlockStoreManager.commitPendingCreation
285
- syncBlockStore.sourceManager.registerCreationCallback(function () {
286
- var _api$core;
287
- api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function () {
288
- return tr.setMeta('isCommitSyncBlockCreation', true);
339
+ if (fg('platform_synced_block_patch_1')) {
340
+ handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, state, api);
341
+ return true;
342
+ } else {
343
+ // If there is bodiedSyncBlock node addition and it's waiting for the result of saving the node to backend (syncBlockStore.hasPendingCreation()),
344
+ // we need to intercept the transaction and save it in insert callback so that we only insert it to the document when backend call if backend call is successful
345
+ // The callback will be evoked by in SourceSyncBlockStoreManager.commitPendingCreation
346
+ syncBlockStore.sourceManager.registerCreationCallback(function () {
347
+ var _api$core;
348
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function () {
349
+ return tr.setMeta('isCommitSyncBlockCreation', true);
350
+ });
351
+ api === null || api === void 0 || api.core.actions.focus();
289
352
  });
290
- api === null || api === void 0 || api.core.actions.focus();
291
- });
292
- return false;
353
+ return false;
354
+ }
293
355
  }
294
356
  } else {
295
357
  var _trackSyncBlocks4 = trackSyncBlocks(function (node) {
@@ -332,11 +394,11 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
332
394
  confirmationTransactionRef.current = rebaseTransaction(confirmationTransactionRef.current, tr, newState);
333
395
  }
334
396
  });
335
- var _iterator = _createForOfIteratorHelper(trs),
336
- _step;
397
+ var _iterator2 = _createForOfIteratorHelper(trs),
398
+ _step2;
337
399
  try {
338
400
  var _loop = function _loop() {
339
- var tr = _step.value;
401
+ var tr = _step2.value;
340
402
  if (!tr.getMeta(pmHistoryPluginKey)) {
341
403
  return 0; // continue
342
404
  }
@@ -357,15 +419,15 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
357
419
  }
358
420
  },
359
421
  _ret;
360
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
422
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
361
423
  _ret = _loop();
362
424
  if (_ret === 0) continue;
363
425
  if (_ret) return _ret.v;
364
426
  }
365
427
  } catch (err) {
366
- _iterator.e(err);
428
+ _iterator2.e(err);
367
429
  } finally {
368
- _iterator.f();
430
+ _iterator2.f();
369
431
  }
370
432
  return null;
371
433
  }