@atlaskit/editor-plugin-synced-block 4.4.0 → 4.4.1

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 (37) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/afm-cc/tsconfig.json +2 -1
  3. package/afm-jira/tsconfig.json +2 -1
  4. package/afm-products/tsconfig.json +2 -1
  5. package/dist/cjs/pm-plugins/main.js +21 -34
  6. package/dist/cjs/pm-plugins/utils/handle-bodied-sync-block-removal.js +87 -0
  7. package/dist/cjs/syncedBlockPlugin.js +6 -4
  8. package/dist/cjs/types/index.js +1 -0
  9. package/dist/cjs/ui/DeleteConfirmationModal.js +53 -12
  10. package/dist/cjs/ui/Flag.js +44 -17
  11. package/dist/es2019/pm-plugins/main.js +21 -32
  12. package/dist/es2019/pm-plugins/utils/handle-bodied-sync-block-removal.js +74 -0
  13. package/dist/es2019/syncedBlockPlugin.js +6 -4
  14. package/dist/es2019/types/index.js +1 -0
  15. package/dist/es2019/ui/DeleteConfirmationModal.js +56 -12
  16. package/dist/es2019/ui/Flag.js +43 -13
  17. package/dist/esm/pm-plugins/main.js +21 -34
  18. package/dist/esm/pm-plugins/utils/handle-bodied-sync-block-removal.js +80 -0
  19. package/dist/esm/syncedBlockPlugin.js +6 -4
  20. package/dist/esm/types/index.js +1 -0
  21. package/dist/esm/ui/DeleteConfirmationModal.js +53 -12
  22. package/dist/esm/ui/Flag.js +44 -17
  23. package/dist/types/pm-plugins/main.d.ts +3 -2
  24. package/dist/types/pm-plugins/utils/handle-bodied-sync-block-removal.d.ts +9 -0
  25. package/dist/types/pm-plugins/utils/track-sync-blocks.d.ts +2 -11
  26. package/dist/types/syncedBlockPluginType.d.ts +0 -4
  27. package/dist/types/types/index.d.ts +28 -2
  28. package/dist/types/ui/CreateSyncedBlockButton.d.ts +1 -1
  29. package/dist/types/ui/OverflowMenuSection.d.ts +1 -1
  30. package/dist/types-ts4.5/pm-plugins/main.d.ts +3 -2
  31. package/dist/types-ts4.5/pm-plugins/utils/handle-bodied-sync-block-removal.d.ts +9 -0
  32. package/dist/types-ts4.5/pm-plugins/utils/track-sync-blocks.d.ts +2 -11
  33. package/dist/types-ts4.5/syncedBlockPluginType.d.ts +0 -4
  34. package/dist/types-ts4.5/types/index.d.ts +28 -2
  35. package/dist/types-ts4.5/ui/CreateSyncedBlockButton.d.ts +1 -1
  36. package/dist/types-ts4.5/ui/OverflowMenuSection.d.ts +1 -1
  37. package/package.json +7 -7
@@ -0,0 +1,74 @@
1
+ import { pmHistoryPluginKey } from '@atlaskit/editor-common/utils';
2
+ import { FLAG_ID } from '../../types';
3
+ import { syncedBlockPluginKey } from '../main';
4
+ const onRetry = (api, syncBlockStore) => () => {
5
+ var _api$core;
6
+ api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
7
+ tr
8
+ }) => {
9
+ return tr.setMeta(syncedBlockPluginKey, {
10
+ bodiedSyncBlockDeletionStatus: 'processing',
11
+ activeFlag: false
12
+ });
13
+ });
14
+ syncBlockStore.sourceManager.retryDeletion();
15
+ };
16
+ const onDismissed = syncBlockStore => tr => {
17
+ syncBlockStore.sourceManager.clearPendingDeletion();
18
+ return tr.setMeta(syncedBlockPluginKey, {
19
+ bodiedSyncBlockDeletionStatus: 'none'
20
+ });
21
+ };
22
+ export const handleBodiedSyncBlockRemoval = (tr, bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef) => {
23
+ // Clear potential old pending deletion to retreat the deletion as first attempt
24
+ syncBlockStore.sourceManager.clearPendingDeletion();
25
+
26
+ // If there are source sync blocks being removed, and we need to confirm with user before deleting,
27
+ // we block the transaction here, and wait for user confirmation to proceed with deletion.
28
+ // See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
29
+ // proceed with deletion.
30
+ syncBlockStore.sourceManager.deleteSyncBlocksWithConfirmation(bodiedSyncBlockRemoved.map(node => node.attrs), () => {
31
+ var _api$core2;
32
+ const confirmationTransaction = confirmationTransactionRef.current;
33
+ if (!confirmationTransaction) {
34
+ return;
35
+ }
36
+ api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.execute(() => {
37
+ const trToDispatch = confirmationTransaction.setMeta('isConfirmedSyncBlockDeletion', true);
38
+ if (!trToDispatch.getMeta(pmHistoryPluginKey)) {
39
+ // bodiedSyncBlock deletion is expected to be permanent (cannot undo)
40
+ // For a normal deletion (not triggered by undo), remove it from history so that it cannot be undone
41
+ trToDispatch.setMeta('addToHistory', false);
42
+ }
43
+ return trToDispatch;
44
+ });
45
+ }, success => {
46
+ var _api$core3;
47
+ api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(({
48
+ tr
49
+ }) => {
50
+ let newState;
51
+ if (!success) {
52
+ newState = {
53
+ activeFlag: {
54
+ id: FLAG_ID.FAIL_TO_DELETE,
55
+ onRetry: onRetry(api, syncBlockStore),
56
+ onDismissed: onDismissed(syncBlockStore)
57
+ }
58
+ };
59
+ }
60
+ newState = {
61
+ ...newState,
62
+ bodiedSyncBlockDeletionStatus: syncBlockStore.sourceManager.isRetryingDeletion() ?
63
+ // For retry, reset to none directly to clean up the status
64
+ 'none' :
65
+ // For the first attempt, set to completed for deletion modal can close the modal
66
+ 'completed'
67
+ };
68
+ return tr.setMeta(syncedBlockPluginKey, newState);
69
+ });
70
+ }, () => {
71
+ confirmationTransactionRef.current = undefined;
72
+ });
73
+ return false;
74
+ };
@@ -95,12 +95,14 @@ export const syncedBlockPlugin = ({
95
95
  return;
96
96
  }
97
97
  const {
98
- showFlag,
99
- syncBlockStore: currentSyncBlockStore
98
+ activeFlag,
99
+ syncBlockStore: currentSyncBlockStore,
100
+ bodiedSyncBlockDeletionStatus
100
101
  } = syncedBlockPluginKey.getState(editorState);
101
102
  return {
102
- showFlag,
103
- syncBlockStore: currentSyncBlockStore
103
+ activeFlag,
104
+ syncBlockStore: currentSyncBlockStore,
105
+ bodiedSyncBlockDeletionStatus
104
106
  };
105
107
  }
106
108
  };
@@ -2,5 +2,6 @@ export let FLAG_ID = /*#__PURE__*/function (FLAG_ID) {
2
2
  FLAG_ID["CANNOT_DELETE_WHEN_OFFLINE"] = "cannot-delete-when-offline";
3
3
  FLAG_ID["CANNOT_EDIT_WHEN_OFFLINE"] = "cannot-edit-when-offline";
4
4
  FLAG_ID["CANNOT_CREATE_WHEN_OFFLINE"] = "cannot-create-when-offline";
5
+ FLAG_ID["FAIL_TO_DELETE"] = "fail-to-delete";
5
6
  return FLAG_ID;
6
7
  }({});
@@ -5,47 +5,90 @@ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'
5
5
  import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
6
6
  import ModalDialog, { ModalBody, ModalFooter, ModalHeader, ModalTitle, ModalTransition } from '@atlaskit/modal-dialog';
7
7
  import { Text } from '@atlaskit/primitives/compiled';
8
+ import { syncedBlockPluginKey } from '../pm-plugins/main';
8
9
  export const DeleteConfirmationModal = ({
9
10
  syncBlockStoreManager,
10
11
  api
11
12
  }) => {
13
+ var _api$core2, _api$core4, _api$core6;
12
14
  const [isOpen, setIsOpen] = useState(false);
13
15
  const [syncBlockCount, setSyncBlockCount] = useState(1);
14
16
  const {
15
- mode
16
- } = useSharedPluginStateWithSelector(api, ['connectivity'], states => {
17
- var _states$connectivityS;
17
+ mode,
18
+ bodiedSyncBlockDeletionStatus,
19
+ activeFlag
20
+ } = useSharedPluginStateWithSelector(api, ['connectivity', 'syncedBlock'], states => {
21
+ var _states$connectivityS, _states$syncedBlockSt, _states$syncedBlockSt2;
18
22
  return {
19
- mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
23
+ mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode,
24
+ bodiedSyncBlockDeletionStatus: (_states$syncedBlockSt = states.syncedBlockState) === null || _states$syncedBlockSt === void 0 ? void 0 : _states$syncedBlockSt.bodiedSyncBlockDeletionStatus,
25
+ activeFlag: (_states$syncedBlockSt2 = states.syncedBlockState) === null || _states$syncedBlockSt2 === void 0 ? void 0 : _states$syncedBlockSt2.activeFlag
20
26
  };
21
27
  });
22
28
  const {
23
29
  formatMessage
24
30
  } = useIntl();
25
31
  const resolverRef = React.useRef(undefined);
26
- const handleClose = useCallback(confirm => () => {
32
+ const handleClick = useCallback(confirm => () => {
33
+ var _api$core;
27
34
  if (resolverRef.current) {
28
35
  resolverRef.current(confirm);
29
36
  resolverRef.current = undefined;
30
37
  }
31
- setIsOpen(false);
32
- }, []);
38
+ if (!confirm) {
39
+ setIsOpen(false);
40
+ }
41
+ api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
42
+ tr
43
+ }) => {
44
+ return tr.setMeta(syncedBlockPluginKey, {
45
+ bodiedSyncBlockDeletionStatus: confirm ? 'processing' : 'none',
46
+ activeFlag: false
47
+ });
48
+ });
49
+ }, [api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions]);
33
50
  const confirmationCallback = useCallback(syncBlockCount => {
34
51
  setIsOpen(true);
35
52
  setSyncBlockCount(syncBlockCount);
36
53
  const confirmedPromise = new Promise(resolve => {
37
54
  resolverRef.current = resolve;
38
55
  });
56
+ if (activeFlag) {
57
+ var _api$core3;
58
+ api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(({
59
+ tr
60
+ }) => {
61
+ return tr.setMeta(syncedBlockPluginKey, {
62
+ // Clear flag to avoid potential retry deletion of different blocks
63
+ activeFlag: false
64
+ });
65
+ });
66
+ }
39
67
  return confirmedPromise;
40
- }, []);
68
+ }, [activeFlag, api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions]);
41
69
  useEffect(() => {
42
70
  const unregister = syncBlockStoreManager.sourceManager.registerConfirmationCallback(confirmationCallback);
43
71
  return () => {
44
72
  unregister();
45
73
  };
46
74
  }, [syncBlockStoreManager, confirmationCallback]);
75
+ useEffect(() => {
76
+ if (bodiedSyncBlockDeletionStatus === 'completed' && isOpen) {
77
+ var _api$core5;
78
+ // auto close modal once deletion is successful
79
+ setIsOpen(false);
80
+ api === null || api === void 0 ? void 0 : (_api$core5 = api.core) === null || _api$core5 === void 0 ? void 0 : _api$core5.actions.execute(({
81
+ tr
82
+ }) => {
83
+ return tr.setMeta(syncedBlockPluginKey, {
84
+ // Reset deletion status to have a clean state for next deletion
85
+ bodiedSyncBlockDeletionStatus: 'none'
86
+ });
87
+ });
88
+ }
89
+ }, [api === null || api === void 0 ? void 0 : (_api$core6 = api.core) === null || _api$core6 === void 0 ? void 0 : _api$core6.actions, bodiedSyncBlockDeletionStatus, isOpen]);
47
90
  return /*#__PURE__*/React.createElement(ModalTransition, null, isOpen && /*#__PURE__*/React.createElement(ModalDialog, {
48
- onClose: handleClose(false),
91
+ onClose: handleClick(false),
49
92
  testId: "sync-block-delete-confirmation"
50
93
  }, /*#__PURE__*/React.createElement(ModalHeader, {
51
94
  hasCloseButton: true
@@ -55,11 +98,12 @@ export const DeleteConfirmationModal = ({
55
98
  syncBlockCount
56
99
  }))), /*#__PURE__*/React.createElement(ModalFooter, null, /*#__PURE__*/React.createElement(Button, {
57
100
  appearance: "subtle",
58
- onClick: handleClose(false)
101
+ onClick: handleClick(false)
59
102
  }, formatMessage(messages.deleteConfirmationModalCancelButton)), /*#__PURE__*/React.createElement(Button, {
60
103
  appearance: "warning",
61
- onClick: handleClose(true),
104
+ onClick: handleClick(true),
62
105
  autoFocus: true,
63
- isDisabled: mode === 'offline'
106
+ isDisabled: mode === 'offline',
107
+ isLoading: bodiedSyncBlockDeletionStatus === 'processing'
64
108
  }, formatMessage(messages.deleteConfirmationModalDeleteButton)))));
65
109
  };
@@ -3,7 +3,7 @@ import { useIntl } from 'react-intl-next';
3
3
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
4
4
  import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
5
5
  import AkFlag, { FlagGroup } from '@atlaskit/flag';
6
- import StatusErrorIcon from '@atlaskit/icon/core/status-error';
6
+ import StatusWarningIcon from '@atlaskit/icon/core/status-warning';
7
7
  import { syncedBlockPluginKey } from '../pm-plugins/main';
8
8
  import { FLAG_ID } from '../types';
9
9
  const flagMap = {
@@ -18,35 +18,61 @@ const flagMap = {
18
18
  [FLAG_ID.CANNOT_CREATE_WHEN_OFFLINE]: {
19
19
  title: messages.failToCreateTitle,
20
20
  description: messages.failToCreateWhenOfflineDescription
21
+ },
22
+ [FLAG_ID.FAIL_TO_DELETE]: {
23
+ title: messages.cannotDeleteTitle,
24
+ description: messages.cannotDeleteDescription
21
25
  }
22
26
  };
23
27
  export const Flag = ({
24
28
  api
25
29
  }) => {
26
30
  const {
27
- showFlag
28
- } = useSharedPluginStateWithSelector(api, ['syncedBlock'], states => {
29
- var _states$syncedBlockSt;
31
+ activeFlag,
32
+ mode
33
+ } = useSharedPluginStateWithSelector(api, ['syncedBlock', 'connectivity'], states => {
34
+ var _states$syncedBlockSt, _states$connectivityS;
30
35
  return {
31
- showFlag: (_states$syncedBlockSt = states.syncedBlockState) === null || _states$syncedBlockSt === void 0 ? void 0 : _states$syncedBlockSt.showFlag
36
+ activeFlag: (_states$syncedBlockSt = states.syncedBlockState) === null || _states$syncedBlockSt === void 0 ? void 0 : _states$syncedBlockSt.activeFlag,
37
+ mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
32
38
  };
33
39
  });
34
40
  const {
35
41
  formatMessage
36
42
  } = useIntl();
37
- if (!showFlag) {
43
+ if (!activeFlag) {
38
44
  return;
39
45
  }
40
46
  const {
41
47
  title,
42
48
  description
43
- } = flagMap[showFlag];
49
+ } = flagMap[activeFlag.id];
50
+ const {
51
+ onRetry,
52
+ onDismissed: onDismissedCallback
53
+ } = activeFlag;
54
+
55
+ // Retry button often involves network request, hence we dismiss the flag in offline mode to avoid retry
56
+ if (mode === 'offline' && !!onRetry) {
57
+ api === null || api === void 0 ? void 0 : api.core.actions.execute(({
58
+ tr
59
+ }) => {
60
+ tr.setMeta(syncedBlockPluginKey, {
61
+ activeFlag: false
62
+ });
63
+ return tr;
64
+ });
65
+ return;
66
+ }
44
67
  const onDismissed = () => {
45
68
  api === null || api === void 0 ? void 0 : api.core.actions.execute(({
46
69
  tr
47
70
  }) => {
71
+ onDismissedCallback === null || onDismissedCallback === void 0 ? void 0 : onDismissedCallback(tr);
72
+ const oldMeta = tr.getMeta(syncedBlockPluginKey);
48
73
  tr.setMeta(syncedBlockPluginKey, {
49
- showFlag: false
74
+ ...oldMeta,
75
+ activeFlag: false
50
76
  });
51
77
  return tr;
52
78
  });
@@ -56,11 +82,15 @@ export const Flag = ({
56
82
  onDismissed: onDismissed,
57
83
  title: formatMessage(title),
58
84
  description: formatMessage(description),
59
- id: showFlag,
60
- testId: showFlag,
61
- icon: /*#__PURE__*/React.createElement(StatusErrorIcon, {
85
+ id: activeFlag.id,
86
+ testId: activeFlag.id,
87
+ icon: /*#__PURE__*/React.createElement(StatusWarningIcon, {
62
88
  label: "",
63
- color: "var(--ds-icon-danger, #C9372C)"
64
- })
89
+ color: "var(--ds-icon-warning, #E06C00)"
90
+ }),
91
+ actions: onRetry ? [{
92
+ content: formatMessage(messages.deleteRetryButton),
93
+ onClick: onRetry
94
+ }] : undefined
65
95
  }));
66
96
  };
@@ -11,6 +11,7 @@ import { convertPMNodesToSyncBlockNodes, rebaseTransaction } from '@atlaskit/edi
11
11
  import { lazyBodiedSyncBlockView } from '../nodeviews/bodiedLazySyncedBlock';
12
12
  import { lazySyncBlockView } from '../nodeviews/lazySyncedBlock';
13
13
  import { FLAG_ID } from '../types';
14
+ import { handleBodiedSyncBlockRemoval } from './utils/handle-bodied-sync-block-removal';
14
15
  import { shouldIgnoreDomEvent } from './utils/ignore-dom-event';
15
16
  import { calculateDecorations } from './utils/selection-decorations';
16
17
  import { hasEditInSyncBlock, trackSyncBlocks } from './utils/track-sync-blocks';
@@ -19,7 +20,9 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
19
20
  var _ref = options || {},
20
21
  _ref$useLongPressSele = _ref.useLongPressSelection,
21
22
  useLongPressSelection = _ref$useLongPressSele === void 0 ? false : _ref$useLongPressSele;
22
- var confirmationTransaction;
23
+ var confirmationTransactionRef = {
24
+ current: undefined
25
+ };
23
26
  return new SafePlugin({
24
27
  key: syncedBlockPluginKey,
25
28
  state: {
@@ -30,23 +33,25 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
30
33
  syncBlockStore.referenceManager.fetchSyncBlocksData(convertPMNodesToSyncBlockNodes(syncBlockNodes));
31
34
  return {
32
35
  selectionDecorationSet: calculateDecorations(instance.doc, instance.selection, instance.schema),
33
- showFlag: false,
36
+ activeFlag: false,
34
37
  syncBlockStore: syncBlockStore
35
38
  };
36
39
  },
37
40
  apply: function apply(tr, currentPluginState, oldEditorState) {
38
- var _meta$showFlag;
41
+ var _meta$activeFlag, _meta$bodiedSyncBlock;
39
42
  var meta = tr.getMeta(syncedBlockPluginKey);
40
- var showFlag = currentPluginState.showFlag,
41
- selectionDecorationSet = currentPluginState.selectionDecorationSet;
43
+ var activeFlag = currentPluginState.activeFlag,
44
+ selectionDecorationSet = currentPluginState.selectionDecorationSet,
45
+ bodiedSyncBlockDeletionStatus = currentPluginState.bodiedSyncBlockDeletionStatus;
42
46
  var newDecorationSet = selectionDecorationSet.map(tr.mapping, tr.doc);
43
47
  if (!tr.selection.eq(oldEditorState.selection)) {
44
48
  newDecorationSet = calculateDecorations(tr.doc, tr.selection, tr.doc.type.schema);
45
49
  }
46
50
  return {
47
- showFlag: (_meta$showFlag = meta === null || meta === void 0 ? void 0 : meta.showFlag) !== null && _meta$showFlag !== void 0 ? _meta$showFlag : showFlag,
51
+ activeFlag: (_meta$activeFlag = meta === null || meta === void 0 ? void 0 : meta.activeFlag) !== null && _meta$activeFlag !== void 0 ? _meta$activeFlag : activeFlag,
48
52
  selectionDecorationSet: newDecorationSet,
49
- syncBlockStore: syncBlockStore
53
+ syncBlockStore: syncBlockStore,
54
+ bodiedSyncBlockDeletionStatus: (_meta$bodiedSyncBlock = meta === null || meta === void 0 ? void 0 : meta.bodiedSyncBlockDeletionStatus) !== null && _meta$bodiedSyncBlock !== void 0 ? _meta$bodiedSyncBlock : bodiedSyncBlockDeletionStatus
50
55
  };
51
56
  }
52
57
  },
@@ -115,28 +120,8 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
115
120
  bodiedSyncBlockAdded = _trackSyncBlocks.added;
116
121
  if (!isOffline) {
117
122
  if (bodiedSyncBlockRemoved.length > 0) {
118
- // If there are source sync blocks being removed, and we need to confirm with user before deleting,
119
- // we block the transaction here, and wait for user confirmation to proceed with deletion.
120
- // See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
121
- // proceed with deletion.
122
- confirmationTransaction = tr;
123
- syncBlockStore.sourceManager.deleteSyncBlocksWithConfirmation(bodiedSyncBlockRemoved.map(function (node) {
124
- return node.attrs;
125
- }), function () {
126
- var _api$core;
127
- api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function () {
128
- var trToDispatch = tr.setMeta('isConfirmedSyncBlockDeletion', true);
129
- if (!trToDispatch.getMeta(pmHistoryPluginKey)) {
130
- // bodiedSyncBlock deletion is expected to be permanent (cannot undo)
131
- // For a normal deletion (not triggered by undo), remove it from history so that it cannot be undone
132
- trToDispatch.setMeta('addToHistory', false);
133
- }
134
- return trToDispatch;
135
- });
136
- }).finally(function () {
137
- confirmationTransaction = undefined;
138
- });
139
- return false;
123
+ confirmationTransactionRef.current = tr;
124
+ return handleBodiedSyncBlockRemoval(tr, bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef);
140
125
  }
141
126
  if (bodiedSyncBlockAdded.length > 0) {
142
127
  if (Boolean(tr.getMeta(pmHistoryPluginKey))) {
@@ -150,8 +135,8 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
150
135
  // 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
151
136
  // The callback will be evoked by in SourceSyncBlockStoreManager.commitPendingCreation
152
137
  syncBlockStore.sourceManager.registerCreationCallback(function () {
153
- var _api$core2;
154
- api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function () {
138
+ var _api$core;
139
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function () {
155
140
  return tr.setMeta('isCommitSyncBlockCreation', true);
156
141
  });
157
142
  api === null || api === void 0 || api.core.actions.focus();
@@ -180,7 +165,9 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
180
165
  api === null || api === void 0 || api.core.actions.execute(function (_ref2) {
181
166
  var tr = _ref2.tr;
182
167
  return tr.setMeta(syncedBlockPluginKey, {
183
- showFlag: errorFlag
168
+ activeFlag: {
169
+ id: errorFlag
170
+ }
184
171
  });
185
172
  });
186
173
  }, 0);
@@ -193,8 +180,8 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
193
180
  trs.filter(function (tr) {
194
181
  return tr.docChanged;
195
182
  }).forEach(function (tr) {
196
- if (confirmationTransaction) {
197
- confirmationTransaction = rebaseTransaction(confirmationTransaction, tr, newState);
183
+ if (confirmationTransactionRef.current) {
184
+ confirmationTransactionRef.current = rebaseTransaction(confirmationTransactionRef.current, tr, newState);
198
185
  }
199
186
  });
200
187
  var _iterator = _createForOfIteratorHelper(trs),
@@ -0,0 +1,80 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ 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; }
3
+ 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; }
4
+ import { pmHistoryPluginKey } from '@atlaskit/editor-common/utils';
5
+ import { FLAG_ID } from '../../types';
6
+ import { syncedBlockPluginKey } from '../main';
7
+ var onRetry = function onRetry(api, syncBlockStore) {
8
+ return function () {
9
+ var _api$core;
10
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref) {
11
+ var tr = _ref.tr;
12
+ return tr.setMeta(syncedBlockPluginKey, {
13
+ bodiedSyncBlockDeletionStatus: 'processing',
14
+ activeFlag: false
15
+ });
16
+ });
17
+ syncBlockStore.sourceManager.retryDeletion();
18
+ };
19
+ };
20
+ var onDismissed = function onDismissed(syncBlockStore) {
21
+ return function (tr) {
22
+ syncBlockStore.sourceManager.clearPendingDeletion();
23
+ return tr.setMeta(syncedBlockPluginKey, {
24
+ bodiedSyncBlockDeletionStatus: 'none'
25
+ });
26
+ };
27
+ };
28
+ export var handleBodiedSyncBlockRemoval = function handleBodiedSyncBlockRemoval(tr, bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef) {
29
+ // Clear potential old pending deletion to retreat the deletion as first attempt
30
+ syncBlockStore.sourceManager.clearPendingDeletion();
31
+
32
+ // If there are source sync blocks being removed, and we need to confirm with user before deleting,
33
+ // we block the transaction here, and wait for user confirmation to proceed with deletion.
34
+ // See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
35
+ // proceed with deletion.
36
+ syncBlockStore.sourceManager.deleteSyncBlocksWithConfirmation(bodiedSyncBlockRemoved.map(function (node) {
37
+ return node.attrs;
38
+ }), function () {
39
+ var _api$core2;
40
+ var confirmationTransaction = confirmationTransactionRef.current;
41
+ if (!confirmationTransaction) {
42
+ return;
43
+ }
44
+ api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function () {
45
+ var trToDispatch = confirmationTransaction.setMeta('isConfirmedSyncBlockDeletion', true);
46
+ if (!trToDispatch.getMeta(pmHistoryPluginKey)) {
47
+ // bodiedSyncBlock deletion is expected to be permanent (cannot undo)
48
+ // For a normal deletion (not triggered by undo), remove it from history so that it cannot be undone
49
+ trToDispatch.setMeta('addToHistory', false);
50
+ }
51
+ return trToDispatch;
52
+ });
53
+ }, function (success) {
54
+ var _api$core3;
55
+ api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(function (_ref2) {
56
+ var tr = _ref2.tr;
57
+ var newState;
58
+ if (!success) {
59
+ newState = {
60
+ activeFlag: {
61
+ id: FLAG_ID.FAIL_TO_DELETE,
62
+ onRetry: onRetry(api, syncBlockStore),
63
+ onDismissed: onDismissed(syncBlockStore)
64
+ }
65
+ };
66
+ }
67
+ newState = _objectSpread(_objectSpread({}, newState), {}, {
68
+ bodiedSyncBlockDeletionStatus: syncBlockStore.sourceManager.isRetryingDeletion() ?
69
+ // For retry, reset to none directly to clean up the status
70
+ 'none' :
71
+ // For the first attempt, set to completed for deletion modal can close the modal
72
+ 'completed'
73
+ });
74
+ return tr.setMeta(syncedBlockPluginKey, newState);
75
+ });
76
+ }, function () {
77
+ confirmationTransactionRef.current = undefined;
78
+ });
79
+ return false;
80
+ };
@@ -106,11 +106,13 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
106
106
  return;
107
107
  }
108
108
  var _syncedBlockPluginKey = syncedBlockPluginKey.getState(editorState),
109
- showFlag = _syncedBlockPluginKey.showFlag,
110
- currentSyncBlockStore = _syncedBlockPluginKey.syncBlockStore;
109
+ activeFlag = _syncedBlockPluginKey.activeFlag,
110
+ currentSyncBlockStore = _syncedBlockPluginKey.syncBlockStore,
111
+ bodiedSyncBlockDeletionStatus = _syncedBlockPluginKey.bodiedSyncBlockDeletionStatus;
111
112
  return {
112
- showFlag: showFlag,
113
- syncBlockStore: currentSyncBlockStore
113
+ activeFlag: activeFlag,
114
+ syncBlockStore: currentSyncBlockStore,
115
+ bodiedSyncBlockDeletionStatus: bodiedSyncBlockDeletionStatus
114
116
  };
115
117
  }
116
118
  };
@@ -2,5 +2,6 @@ export var FLAG_ID = /*#__PURE__*/function (FLAG_ID) {
2
2
  FLAG_ID["CANNOT_DELETE_WHEN_OFFLINE"] = "cannot-delete-when-offline";
3
3
  FLAG_ID["CANNOT_EDIT_WHEN_OFFLINE"] = "cannot-edit-when-offline";
4
4
  FLAG_ID["CANNOT_CREATE_WHEN_OFFLINE"] = "cannot-create-when-offline";
5
+ FLAG_ID["FAIL_TO_DELETE"] = "fail-to-delete";
5
6
  return FLAG_ID;
6
7
  }({});
@@ -6,7 +6,9 @@ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'
6
6
  import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
7
7
  import ModalDialog, { ModalBody, ModalFooter, ModalHeader, ModalTitle, ModalTransition } from '@atlaskit/modal-dialog';
8
8
  import { Text } from '@atlaskit/primitives/compiled';
9
+ import { syncedBlockPluginKey } from '../pm-plugins/main';
9
10
  export var DeleteConfirmationModal = function DeleteConfirmationModal(_ref) {
11
+ var _api$core2, _api$core4, _api$core6;
10
12
  var syncBlockStoreManager = _ref.syncBlockStoreManager,
11
13
  api = _ref.api;
12
14
  var _useState = useState(false),
@@ -17,41 +19,79 @@ export var DeleteConfirmationModal = function DeleteConfirmationModal(_ref) {
17
19
  _useState4 = _slicedToArray(_useState3, 2),
18
20
  syncBlockCount = _useState4[0],
19
21
  setSyncBlockCount = _useState4[1];
20
- var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['connectivity'], function (states) {
21
- var _states$connectivityS;
22
+ var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['connectivity', 'syncedBlock'], function (states) {
23
+ var _states$connectivityS, _states$syncedBlockSt, _states$syncedBlockSt2;
22
24
  return {
23
- mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
25
+ mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode,
26
+ bodiedSyncBlockDeletionStatus: (_states$syncedBlockSt = states.syncedBlockState) === null || _states$syncedBlockSt === void 0 ? void 0 : _states$syncedBlockSt.bodiedSyncBlockDeletionStatus,
27
+ activeFlag: (_states$syncedBlockSt2 = states.syncedBlockState) === null || _states$syncedBlockSt2 === void 0 ? void 0 : _states$syncedBlockSt2.activeFlag
24
28
  };
25
29
  }),
26
- mode = _useSharedPluginState.mode;
30
+ mode = _useSharedPluginState.mode,
31
+ bodiedSyncBlockDeletionStatus = _useSharedPluginState.bodiedSyncBlockDeletionStatus,
32
+ activeFlag = _useSharedPluginState.activeFlag;
27
33
  var _useIntl = useIntl(),
28
34
  formatMessage = _useIntl.formatMessage;
29
35
  var resolverRef = React.useRef(undefined);
30
- var handleClose = useCallback(function (confirm) {
36
+ var handleClick = useCallback(function (confirm) {
31
37
  return function () {
38
+ var _api$core;
32
39
  if (resolverRef.current) {
33
40
  resolverRef.current(confirm);
34
41
  resolverRef.current = undefined;
35
42
  }
36
- setIsOpen(false);
43
+ if (!confirm) {
44
+ setIsOpen(false);
45
+ }
46
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref2) {
47
+ var tr = _ref2.tr;
48
+ return tr.setMeta(syncedBlockPluginKey, {
49
+ bodiedSyncBlockDeletionStatus: confirm ? 'processing' : 'none',
50
+ activeFlag: false
51
+ });
52
+ });
37
53
  };
38
- }, []);
54
+ }, [api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions]);
39
55
  var confirmationCallback = useCallback(function (syncBlockCount) {
40
56
  setIsOpen(true);
41
57
  setSyncBlockCount(syncBlockCount);
42
58
  var confirmedPromise = new Promise(function (resolve) {
43
59
  resolverRef.current = resolve;
44
60
  });
61
+ if (activeFlag) {
62
+ var _api$core3;
63
+ api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(function (_ref3) {
64
+ var tr = _ref3.tr;
65
+ return tr.setMeta(syncedBlockPluginKey, {
66
+ // Clear flag to avoid potential retry deletion of different blocks
67
+ activeFlag: false
68
+ });
69
+ });
70
+ }
45
71
  return confirmedPromise;
46
- }, []);
72
+ }, [activeFlag, api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions]);
47
73
  useEffect(function () {
48
74
  var unregister = syncBlockStoreManager.sourceManager.registerConfirmationCallback(confirmationCallback);
49
75
  return function () {
50
76
  unregister();
51
77
  };
52
78
  }, [syncBlockStoreManager, confirmationCallback]);
79
+ useEffect(function () {
80
+ if (bodiedSyncBlockDeletionStatus === 'completed' && isOpen) {
81
+ var _api$core5;
82
+ // auto close modal once deletion is successful
83
+ setIsOpen(false);
84
+ api === null || api === void 0 || (_api$core5 = api.core) === null || _api$core5 === void 0 || _api$core5.actions.execute(function (_ref4) {
85
+ var tr = _ref4.tr;
86
+ return tr.setMeta(syncedBlockPluginKey, {
87
+ // Reset deletion status to have a clean state for next deletion
88
+ bodiedSyncBlockDeletionStatus: 'none'
89
+ });
90
+ });
91
+ }
92
+ }, [api === null || api === void 0 || (_api$core6 = api.core) === null || _api$core6 === void 0 ? void 0 : _api$core6.actions, bodiedSyncBlockDeletionStatus, isOpen]);
53
93
  return /*#__PURE__*/React.createElement(ModalTransition, null, isOpen && /*#__PURE__*/React.createElement(ModalDialog, {
54
- onClose: handleClose(false),
94
+ onClose: handleClick(false),
55
95
  testId: "sync-block-delete-confirmation"
56
96
  }, /*#__PURE__*/React.createElement(ModalHeader, {
57
97
  hasCloseButton: true
@@ -61,11 +101,12 @@ export var DeleteConfirmationModal = function DeleteConfirmationModal(_ref) {
61
101
  syncBlockCount: syncBlockCount
62
102
  }))), /*#__PURE__*/React.createElement(ModalFooter, null, /*#__PURE__*/React.createElement(Button, {
63
103
  appearance: "subtle",
64
- onClick: handleClose(false)
104
+ onClick: handleClick(false)
65
105
  }, formatMessage(messages.deleteConfirmationModalCancelButton)), /*#__PURE__*/React.createElement(Button, {
66
106
  appearance: "warning",
67
- onClick: handleClose(true),
107
+ onClick: handleClick(true),
68
108
  autoFocus: true,
69
- isDisabled: mode === 'offline'
109
+ isDisabled: mode === 'offline',
110
+ isLoading: bodiedSyncBlockDeletionStatus === 'processing'
70
111
  }, formatMessage(messages.deleteConfirmationModalDeleteButton)))));
71
112
  };