@atlaskit/editor-plugin-synced-block 5.3.12 → 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 (57) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/afm-cc/tsconfig.json +0 -3
  3. package/afm-jira/tsconfig.json +0 -3
  4. package/afm-products/tsconfig.json +0 -3
  5. package/dist/cjs/editor-commands/index.js +34 -38
  6. package/dist/cjs/nodeviews/bodiedSyncedBlock.js +2 -7
  7. package/dist/cjs/nodeviews/syncedBlock.js +4 -18
  8. package/dist/cjs/pm-plugins/main.js +120 -67
  9. package/dist/cjs/pm-plugins/utils/handle-bodied-sync-block-creation.js +141 -0
  10. package/dist/cjs/pm-plugins/utils/track-sync-blocks.js +2 -2
  11. package/dist/cjs/pm-plugins/utils/utils.js +1 -46
  12. package/dist/cjs/syncedBlockPlugin.js +7 -7
  13. package/dist/cjs/types/index.js +1 -0
  14. package/dist/cjs/ui/DeleteConfirmationModal.js +4 -20
  15. package/dist/cjs/ui/Flag.js +5 -1
  16. package/dist/cjs/ui/SyncBlockLabel.js +4 -10
  17. package/dist/cjs/ui/SyncBlockRefresher.js +7 -9
  18. package/dist/cjs/ui/floating-toolbar.js +6 -3
  19. package/dist/es2019/editor-commands/index.js +34 -38
  20. package/dist/es2019/nodeviews/bodiedSyncedBlock.js +2 -7
  21. package/dist/es2019/nodeviews/syncedBlock.js +4 -15
  22. package/dist/es2019/pm-plugins/main.js +99 -58
  23. package/dist/es2019/pm-plugins/utils/handle-bodied-sync-block-creation.js +134 -0
  24. package/dist/es2019/pm-plugins/utils/track-sync-blocks.js +2 -2
  25. package/dist/es2019/pm-plugins/utils/utils.js +0 -47
  26. package/dist/es2019/syncedBlockPlugin.js +7 -6
  27. package/dist/es2019/types/index.js +1 -0
  28. package/dist/es2019/ui/DeleteConfirmationModal.js +4 -20
  29. package/dist/es2019/ui/Flag.js +5 -0
  30. package/dist/es2019/ui/SyncBlockLabel.js +4 -10
  31. package/dist/es2019/ui/SyncBlockRefresher.js +7 -9
  32. package/dist/es2019/ui/floating-toolbar.js +6 -3
  33. package/dist/esm/editor-commands/index.js +34 -38
  34. package/dist/esm/nodeviews/bodiedSyncedBlock.js +2 -7
  35. package/dist/esm/nodeviews/syncedBlock.js +4 -18
  36. package/dist/esm/pm-plugins/main.js +120 -67
  37. package/dist/esm/pm-plugins/utils/handle-bodied-sync-block-creation.js +134 -0
  38. package/dist/esm/pm-plugins/utils/track-sync-blocks.js +2 -2
  39. package/dist/esm/pm-plugins/utils/utils.js +0 -45
  40. package/dist/esm/syncedBlockPlugin.js +7 -7
  41. package/dist/esm/types/index.js +1 -0
  42. package/dist/esm/ui/DeleteConfirmationModal.js +4 -20
  43. package/dist/esm/ui/Flag.js +5 -1
  44. package/dist/esm/ui/SyncBlockLabel.js +4 -10
  45. package/dist/esm/ui/SyncBlockRefresher.js +7 -9
  46. package/dist/esm/ui/floating-toolbar.js +6 -3
  47. package/dist/types/pm-plugins/main.d.ts +2 -1
  48. package/dist/types/pm-plugins/utils/handle-bodied-sync-block-creation.d.ts +9 -0
  49. package/dist/types/pm-plugins/utils/handle-bodied-sync-block-removal.d.ts +2 -2
  50. package/dist/types/pm-plugins/utils/utils.d.ts +1 -3
  51. package/dist/types/types/index.d.ts +19 -1
  52. package/dist/types-ts4.5/pm-plugins/main.d.ts +2 -1
  53. package/dist/types-ts4.5/pm-plugins/utils/handle-bodied-sync-block-creation.d.ts +9 -0
  54. package/dist/types-ts4.5/pm-plugins/utils/handle-bodied-sync-block-removal.d.ts +2 -2
  55. package/dist/types-ts4.5/pm-plugins/utils/utils.d.ts +1 -3
  56. package/dist/types-ts4.5/types/index.d.ts +19 -1
  57. package/package.json +1 -5
@@ -4,7 +4,6 @@ import { ErrorBoundary } from '@atlaskit/editor-common/error-boundary';
4
4
  import ReactNodeView from '@atlaskit/editor-common/react-node-view';
5
5
  import { SyncBlockSharedCssClassName, SyncBlockActionsProvider } from '@atlaskit/editor-common/sync-block';
6
6
  import { useFetchSyncBlockData, useFetchSyncBlockTitle } from '@atlaskit/editor-synced-block-provider';
7
- import { fg } from '@atlaskit/platform-feature-flags';
8
7
  import { removeSyncedBlockAtPos } from '../editor-commands';
9
8
  import { SyncBlockRendererWrapper } from '../ui/SyncBlockRendererWrapper';
10
9
  export class SyncBlock extends ReactNodeView {
@@ -22,7 +21,7 @@ export class SyncBlock extends ReactNodeView {
22
21
  render({
23
22
  getPos
24
23
  }) {
25
- var _this$options, _this$api$syncedBlock, _this$api, _this$api$syncedBlock2, _this$api$syncedBlock3, _this$api2, _this$api2$analytics, _this$options2, _this$options3;
24
+ var _this$options, _this$api$syncedBlock, _this$api, _this$api$syncedBlock2, _this$api$syncedBlock3, _this$api2, _this$api2$analytics, _this$options2;
26
25
  if (!((_this$options = this.options) !== null && _this$options !== void 0 && _this$options.syncedBlockRenderer)) {
27
26
  return null;
28
27
  }
@@ -33,14 +32,13 @@ export class SyncBlock extends ReactNodeView {
33
32
  if (!resourceId || !localId) {
34
33
  return null;
35
34
  }
36
- const initialSyncBlockStore = fg('platform_synced_block_dogfooding') ? this.syncBlockStore : undefined;
37
- const syncBlockStore = (_this$api$syncedBlock = (_this$api = this.api) === null || _this$api === void 0 ? void 0 : (_this$api$syncedBlock2 = _this$api.syncedBlock) === null || _this$api$syncedBlock2 === void 0 ? void 0 : (_this$api$syncedBlock3 = _this$api$syncedBlock2.sharedState.currentState()) === null || _this$api$syncedBlock3 === void 0 ? void 0 : _this$api$syncedBlock3.syncBlockStore) !== null && _this$api$syncedBlock !== void 0 ? _this$api$syncedBlock : initialSyncBlockStore;
35
+ const syncBlockStore = (_this$api$syncedBlock = (_this$api = this.api) === null || _this$api === void 0 ? void 0 : (_this$api$syncedBlock2 = _this$api.syncedBlock) === null || _this$api$syncedBlock2 === void 0 ? void 0 : (_this$api$syncedBlock3 = _this$api$syncedBlock2.sharedState.currentState()) === null || _this$api$syncedBlock3 === void 0 ? void 0 : _this$api$syncedBlock3.syncBlockStore) !== null && _this$api$syncedBlock !== void 0 ? _this$api$syncedBlock : this.syncBlockStore;
38
36
  if (!syncBlockStore) {
39
37
  return null;
40
38
  }
41
39
 
42
40
  // get document node from data provider
43
- return fg('platform_synced_block_dogfooding') ? /*#__PURE__*/React.createElement(ErrorBoundary, {
41
+ return /*#__PURE__*/React.createElement(ErrorBoundary, {
44
42
  component: ACTION_SUBJECT.SYNCED_BLOCK,
45
43
  dispatchAnalyticsEvent: (_this$api2 = this.api) === null || _this$api2 === void 0 ? void 0 : (_this$api2$analytics = _this$api2.analytics) === null || _this$api2$analytics === void 0 ? void 0 : _this$api2$analytics.actions.fireAnalyticsEvent,
46
44
  fallbackComponent: null
@@ -61,16 +59,7 @@ export class SyncBlock extends ReactNodeView {
61
59
  return useFetchSyncBlockData(syncBlockStore, resourceId, localId, (_this$api3 = this.api) === null || _this$api3 === void 0 ? void 0 : (_this$api3$analytics = _this$api3.analytics) === null || _this$api3$analytics === void 0 ? void 0 : (_this$api3$analytics$ = _this$api3$analytics.actions) === null || _this$api3$analytics$ === void 0 ? void 0 : _this$api3$analytics$.fireAnalyticsEvent);
62
60
  },
63
61
  api: this.api
64
- }))) : /*#__PURE__*/React.createElement(SyncBlockRendererWrapper, {
65
- localId: this.node.attrs.localId,
66
- syncedBlockRenderer: (_this$options3 = this.options) === null || _this$options3 === void 0 ? void 0 : _this$options3.syncedBlockRenderer,
67
- useFetchSyncBlockTitle: () => useFetchSyncBlockTitle(syncBlockStore, this.node),
68
- useFetchSyncBlockData: () => {
69
- var _this$api4, _this$api4$analytics, _this$api4$analytics$;
70
- return useFetchSyncBlockData(syncBlockStore, resourceId, localId, (_this$api4 = this.api) === null || _this$api4 === void 0 ? void 0 : (_this$api4$analytics = _this$api4.analytics) === null || _this$api4$analytics === void 0 ? void 0 : (_this$api4$analytics$ = _this$api4$analytics.actions) === null || _this$api4$analytics$ === void 0 ? void 0 : _this$api4$analytics$.fireAnalyticsEvent);
71
- },
72
- api: this.api
73
- });
62
+ })));
74
63
  }
75
64
  destroy() {
76
65
  var _this$unsubscribe;
@@ -9,15 +9,37 @@ import { DecorationSet, Decoration } from '@atlaskit/editor-prosemirror/view';
9
9
  import { convertPMNodesToSyncBlockNodes, rebaseTransaction } from '@atlaskit/editor-synced-block-provider';
10
10
  import { fg } from '@atlaskit/platform-feature-flags';
11
11
  import { lazyBodiedSyncBlockView } from '../nodeviews/bodiedLazySyncedBlock';
12
- import { lazySyncBlockView } from '../nodeviews/lazySyncedBlock';
13
12
  import { SyncBlock as SyncBlockView } from '../nodeviews/syncedBlock';
14
13
  import { FLAG_ID } from '../types';
14
+ import { handleBodiedSyncBlockCreation } from './utils/handle-bodied-sync-block-creation';
15
15
  import { handleBodiedSyncBlockRemoval } from './utils/handle-bodied-sync-block-removal';
16
16
  import { shouldIgnoreDomEvent } from './utils/ignore-dom-event';
17
17
  import { calculateDecorations } from './utils/selection-decorations';
18
18
  import { hasEditInSyncBlock, trackSyncBlocks } from './utils/track-sync-blocks';
19
19
  import { sliceFullyContainsNode } from './utils/utils';
20
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
+ };
21
43
  const showCopiedFlag = api => {
22
44
  // Use setTimeout to dispatch transaction in next tick and avoid re-entrant dispatch
23
45
  setTimeout(() => {
@@ -52,7 +74,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
52
74
  const unpublishedFlagShown = new Set();
53
75
 
54
76
  // Set up callback to detect unpublished sync blocks when they're fetched
55
- fg('platform_synced_block_dogfooding') && syncBlockStore.referenceManager.setOnUnpublishedSyncBlockDetected(resourceId => {
77
+ syncBlockStore.referenceManager.setOnUnpublishedSyncBlockDetected(resourceId => {
56
78
  // Only show the flag once per sync block
57
79
  if (!unpublishedFlagShown.has(resourceId)) {
58
80
  unpublishedFlagShown.add(resourceId);
@@ -79,7 +101,8 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
79
101
  return {
80
102
  selectionDecorationSet: calculateDecorations(instance.doc, instance.selection, instance.schema),
81
103
  activeFlag: false,
82
- syncBlockStore: syncBlockStore
104
+ syncBlockStore: syncBlockStore,
105
+ retryCreationPosMap: new Map()
83
106
  };
84
107
  },
85
108
  apply: (tr, currentPluginState, oldEditorState) => {
@@ -88,23 +111,30 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
88
111
  const {
89
112
  activeFlag,
90
113
  selectionDecorationSet,
91
- bodiedSyncBlockDeletionStatus
114
+ bodiedSyncBlockDeletionStatus,
115
+ retryCreationPosMap
92
116
  } = currentPluginState;
93
117
  let newDecorationSet = selectionDecorationSet.map(tr.mapping, tr.doc);
94
118
  if (!tr.selection.eq(oldEditorState.selection)) {
95
119
  newDecorationSet = calculateDecorations(tr.doc, tr.selection, tr.doc.type.schema);
96
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
+ }
97
126
  return {
98
127
  activeFlag: (_meta$activeFlag = meta === null || meta === void 0 ? void 0 : meta.activeFlag) !== null && _meta$activeFlag !== void 0 ? _meta$activeFlag : activeFlag,
99
128
  selectionDecorationSet: newDecorationSet,
100
129
  syncBlockStore: syncBlockStore,
130
+ retryCreationPosMap: newRetryCreationPosMap,
101
131
  bodiedSyncBlockDeletionStatus: (_meta$bodiedSyncBlock = meta === null || meta === void 0 ? void 0 : meta.bodiedSyncBlockDeletionStatus) !== null && _meta$bodiedSyncBlock !== void 0 ? _meta$bodiedSyncBlock : bodiedSyncBlockDeletionStatus
102
132
  };
103
133
  }
104
134
  },
105
135
  props: {
106
136
  nodeViews: {
107
- syncBlock: fg('platform_synced_block_dogfooding') ? (node, view, getPos, _decorations) => {
137
+ syncBlock: (node, view, getPos, _decorations) => {
108
138
  // To support SSR, pass `syncBlockStore` here
109
139
  // and do not use lazy loading.
110
140
  // We cannot start rendering and then load `syncBlockStore` asynchronously,
@@ -119,11 +149,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
119
149
  eventDispatcher: pmPluginFactoryParams.eventDispatcher,
120
150
  syncBlockStore: syncBlockStore
121
151
  }).init();
122
- } : lazySyncBlockView({
123
- options,
124
- pmPluginFactoryParams,
125
- api
126
- }),
152
+ },
127
153
  bodiedSyncBlock: lazyBodiedSyncBlockView({
128
154
  pluginOptions: options,
129
155
  pmPluginFactoryParams,
@@ -131,8 +157,10 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
131
157
  })
132
158
  },
133
159
  decorations: state => {
134
- var _syncedBlockPluginKey, _syncedBlockPluginKey2, _api$connectivity, _api$connectivity$sha, _api$editorViewMode, _api$editorViewMode$s;
135
- 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;
136
164
  const {
137
165
  doc
138
166
  } = state;
@@ -140,6 +168,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
140
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';
141
169
  const offlineDecorations = [];
142
170
  const viewModeDecorations = [];
171
+ const loadingDecorations = [];
143
172
  state.doc.descendants((node, pos) => {
144
173
  if (node.type.name === 'bodiedSyncBlock' && isOffline) {
145
174
  offlineDecorations.push(Decoration.node(pos, pos + node.nodeSize, {
@@ -151,8 +180,13 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
151
180
  class: SyncBlockStateCssClassName.viewModeClassName
152
181
  }));
153
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
+ }
154
188
  });
155
- return selectionDecorationSet.add(doc, offlineDecorations).add(doc, viewModeDecorations);
189
+ return selectionDecorationSet.add(doc, offlineDecorations).add(doc, viewModeDecorations).add(doc, loadingDecorations);
156
190
  },
157
191
  handleClickOn: createSelectionClickHandler(['bodiedSyncBlock'], target => !!target.closest(`.${BodiedSyncBlockSharedCssClassName.prefix}`), {
158
192
  useLongPressSelection
@@ -215,9 +249,15 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
215
249
  var _api$connectivity2, _api$connectivity2$sh;
216
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);
217
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'));
218
258
 
219
259
  // Track newly added reference sync blocks before processing the transaction
220
- if (tr.docChanged && !tr.getMeta('isRemote') && fg('platform_synced_block_dogfooding')) {
260
+ if (tr.docChanged && !tr.getMeta('isRemote')) {
221
261
  const {
222
262
  added
223
263
  } = trackSyncBlocks(node => node.type.name === 'syncBlock', tr, state);
@@ -234,7 +274,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
234
274
  // or are from remote (collab) or already confirmed sync block deletion
235
275
  // We only care about local changes that change the document
236
276
  // and are not yet confirmed for sync block deletion
237
- 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) {
238
278
  return true;
239
279
  }
240
280
  const {
@@ -242,42 +282,39 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
242
282
  added: bodiedSyncBlockAdded
243
283
  } = trackSyncBlocks(syncBlockStore.sourceManager.isSourceBlock, tr, state);
244
284
  if (!isOffline) {
245
- if (fg('platform_synced_block_dogfooding')) {
246
- const {
247
- removed: syncBlockRemoved,
248
- added: syncBlockAdded
249
- } = trackSyncBlocks(node => node.type.name === 'syncBlock', tr, state);
250
- syncBlockRemoved.forEach(syncBlock => {
251
- var _api$analytics, _api$analytics$action;
252
- 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({
253
- action: ACTION.DELETED,
254
- actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
255
- actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_DELETE,
256
- attributes: {
257
- resourceId: syncBlock.attrs.resourceId,
258
- blockInstanceId: syncBlock.attrs.localId
259
- },
260
- eventType: EVENT_TYPE.OPERATIONAL
261
- });
285
+ const {
286
+ removed: syncBlockRemoved,
287
+ added: syncBlockAdded
288
+ } = trackSyncBlocks(node => node.type.name === 'syncBlock', tr, state);
289
+ syncBlockRemoved.forEach(syncBlock => {
290
+ var _api$analytics, _api$analytics$action;
291
+ 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({
292
+ action: ACTION.DELETED,
293
+ actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
294
+ actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_DELETE,
295
+ attributes: {
296
+ resourceId: syncBlock.attrs.resourceId,
297
+ blockInstanceId: syncBlock.attrs.localId
298
+ },
299
+ eventType: EVENT_TYPE.OPERATIONAL
262
300
  });
263
- syncBlockAdded.forEach(syncBlock => {
264
- var _api$analytics2, _api$analytics2$actio;
265
- 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({
266
- action: ACTION.INSERTED,
267
- actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
268
- actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_CREATE,
269
- attributes: {
270
- resourceId: syncBlock.attrs.resourceId,
271
- blockInstanceId: syncBlock.attrs.localId
272
- },
273
- eventType: EVENT_TYPE.OPERATIONAL
274
- });
301
+ });
302
+ syncBlockAdded.forEach(syncBlock => {
303
+ var _api$analytics2, _api$analytics2$actio;
304
+ 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({
305
+ action: ACTION.INSERTED,
306
+ actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
307
+ actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_CREATE,
308
+ attributes: {
309
+ resourceId: syncBlock.attrs.resourceId,
310
+ blockInstanceId: syncBlock.attrs.localId
311
+ },
312
+ eventType: EVENT_TYPE.OPERATIONAL
275
313
  });
276
- }
277
- ;
314
+ });
278
315
  if (bodiedSyncBlockRemoved.length > 0) {
279
316
  confirmationTransactionRef.current = tr;
280
- return handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, fg('platform_synced_block_dogfooding') ? getDeleteReason(tr) : undefined);
317
+ return handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, getDeleteReason(tr));
281
318
  }
282
319
  if (bodiedSyncBlockAdded.length > 0) {
283
320
  if (Boolean(tr.getMeta(pmHistoryPluginKey))) {
@@ -286,18 +323,22 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
286
323
  // After true is returned here and the node is created, we delete the node in the filterTransaction immediately, which cancels out the creation
287
324
  return true;
288
325
  }
289
-
290
- // If there is bodiedSyncBlock node addition and it's waiting for the result of saving the node to backend (syncBlockStore.hasPendingCreation()),
291
- // 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
292
- // The callback will be evoked by in SourceSyncBlockStoreManager.commitPendingCreation
293
- syncBlockStore.sourceManager.registerCreationCallback(() => {
294
- var _api$core;
295
- api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(() => {
296
- 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();
297
339
  });
298
- api === null || api === void 0 ? void 0 : api.core.actions.focus();
299
- });
300
- return false;
340
+ return false;
341
+ }
301
342
  }
302
343
  } else {
303
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
+ };
@@ -1,6 +1,5 @@
1
1
  import { ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
2
2
  import { findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
3
- import { fg } from '@atlaskit/platform-feature-flags';
4
3
  export const trackSyncBlocks = (predicate, tr, state) => {
5
4
  const removed = {};
6
5
  const added = {};
@@ -20,7 +19,7 @@ export const trackSyncBlocks = (predicate, tr, state) => {
20
19
  from,
21
20
  to
22
21
  } = step;
23
- const docAtStep = fg('platform_synced_block_dogfooding') ? tr.docs[idx] : state.doc;
22
+ const docAtStep = tr.docs[idx];
24
23
  let hasChange = false;
25
24
  if (from !== to) {
26
25
  step.getMap().forEach((oldStart, oldEnd) => {
@@ -67,6 +66,7 @@ export const trackSyncBlocks = (predicate, tr, state) => {
67
66
  const syncBlockAttr = node.attrs;
68
67
  syncBlockMapNew[syncBlockAttr.localId] = {
69
68
  attrs: syncBlockAttr,
69
+ node: node,
70
70
  from: offset,
71
71
  to: offset + node.nodeSize
72
72
  };
@@ -1,9 +1,6 @@
1
1
  import { expandSelectionToBlockRange } from '@atlaskit/editor-common/selection';
2
2
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
3
- import { TextSelection } from '@atlaskit/editor-prosemirror/state';
4
3
  import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
5
- import { CellSelection, findTable } from '@atlaskit/editor-tables';
6
- import { fg } from '@atlaskit/platform-feature-flags';
7
4
  export const findSyncBlock = (schema, selection) => {
8
5
  const {
9
6
  syncBlock
@@ -28,50 +25,6 @@ const UNSUPPORTED_NODE_TYPES = new Set(['inlineExtension', 'extension', 'bodiedE
28
25
  * or false if conversion is not possible
29
26
  */
30
27
  export const canBeConvertedToSyncBlock = selection => {
31
- return fg('platform_synced_block_dogfooding') ? canBeConvertedToSyncBlockNew(selection) : canBeConvertedToSyncBlockOld(selection);
32
- };
33
- export const canBeConvertedToSyncBlockOld = selection => {
34
- const schema = selection.$from.doc.type.schema;
35
- const {
36
- nodes
37
- } = schema;
38
- let from = selection.from;
39
- let to = selection.to;
40
- let contentToInclude = selection.content().content;
41
- if (selection instanceof CellSelection) {
42
- const table = findTable(selection);
43
- if (!table) {
44
- return false;
45
- }
46
- contentToInclude = Fragment.from([table.node]);
47
- from = table.pos;
48
- to = table.pos + table.node.nodeSize;
49
- } else if (selection instanceof TextSelection) {
50
- const trueParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.blockquote])(selection);
51
- if (trueParent) {
52
- contentToInclude = Fragment.from([trueParent.node]);
53
- from = trueParent.pos;
54
- to = trueParent.pos + trueParent.node.nodeSize;
55
- }
56
- }
57
- let canBeConverted = true;
58
- selection.$from.doc.nodesBetween(from, to, node => {
59
- if (UNSUPPORTED_NODE_TYPES.has(node.type.name)) {
60
- canBeConverted = false;
61
- return false;
62
- }
63
- });
64
- if (!canBeConverted) {
65
- return false;
66
- }
67
- contentToInclude = removeBreakoutMarks(contentToInclude);
68
- return {
69
- contentToInclude,
70
- from,
71
- to
72
- };
73
- };
74
- export const canBeConvertedToSyncBlockNew = selection => {
75
28
  const {
76
29
  $from,
77
30
  range
@@ -4,7 +4,6 @@ import { blockTypeMessages } from '@atlaskit/editor-common/messages';
4
4
  import { IconSyncBlock } from '@atlaskit/editor-common/quick-insert';
5
5
  import { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
6
6
  import Lozenge from '@atlaskit/lozenge';
7
- import { fg } from '@atlaskit/platform-feature-flags';
8
7
  import { flushBodiedSyncBlocks, flushSyncBlocks } from './editor-actions';
9
8
  import { copySyncedBlockReferenceToClipboardEditorCommand, createSyncedBlock } from './editor-commands';
10
9
  import { createPlugin, syncedBlockPluginKey } from './pm-plugins/main';
@@ -41,7 +40,7 @@ export const syncedBlockPlugin = ({
41
40
  return [{
42
41
  name: 'syncedBlockPlugin',
43
42
  plugin: params => createPlugin(config, params, syncBlockStore, api)
44
- }, ...(fg('platform_synced_block_dogfooding') ? [{
43
+ }, {
45
44
  name: 'menuAndToolbarExperiencesPlugin',
46
45
  plugin: () => getMenuAndToolbarExperiencesPlugin({
47
46
  refs,
@@ -50,7 +49,7 @@ export const syncedBlockPlugin = ({
50
49
  return 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(payload);
51
50
  }
52
51
  })
53
- }] : [])];
52
+ }];
54
53
  },
55
54
  commands: {
56
55
  copySyncedBlockReferenceToClipboard: inputMethod => copySyncedBlockReferenceToClipboardEditorCommand(syncBlockStore, inputMethod, api),
@@ -106,7 +105,7 @@ export const syncedBlockPlugin = ({
106
105
  fireAnalyticsEvent: api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions.fireAnalyticsEvent
107
106
  });
108
107
  },
109
- testId: fg('platform_synced_block_dogfooding') ? SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate : undefined
108
+ testId: SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate
110
109
  }];
111
110
  },
112
111
  floatingToolbar: (state, intl) => getToolbarConfig(state, intl, api, syncBlockStore)
@@ -136,12 +135,14 @@ export const syncedBlockPlugin = ({
136
135
  const {
137
136
  activeFlag,
138
137
  syncBlockStore: currentSyncBlockStore,
139
- bodiedSyncBlockDeletionStatus
138
+ bodiedSyncBlockDeletionStatus,
139
+ retryCreationPosMap
140
140
  } = syncedBlockPluginKey.getState(editorState);
141
141
  return {
142
142
  activeFlag,
143
143
  syncBlockStore: currentSyncBlockStore,
144
- bodiedSyncBlockDeletionStatus
144
+ bodiedSyncBlockDeletionStatus,
145
+ retryCreationPosMap
145
146
  };
146
147
  }
147
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 = {