@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.
- package/CHANGELOG.md +8 -0
- package/afm-cc/tsconfig.json +2 -1
- package/afm-jira/tsconfig.json +2 -1
- package/afm-products/tsconfig.json +2 -1
- package/dist/cjs/pm-plugins/main.js +21 -34
- package/dist/cjs/pm-plugins/utils/handle-bodied-sync-block-removal.js +87 -0
- package/dist/cjs/syncedBlockPlugin.js +6 -4
- package/dist/cjs/types/index.js +1 -0
- package/dist/cjs/ui/DeleteConfirmationModal.js +53 -12
- package/dist/cjs/ui/Flag.js +44 -17
- package/dist/es2019/pm-plugins/main.js +21 -32
- package/dist/es2019/pm-plugins/utils/handle-bodied-sync-block-removal.js +74 -0
- package/dist/es2019/syncedBlockPlugin.js +6 -4
- package/dist/es2019/types/index.js +1 -0
- package/dist/es2019/ui/DeleteConfirmationModal.js +56 -12
- package/dist/es2019/ui/Flag.js +43 -13
- package/dist/esm/pm-plugins/main.js +21 -34
- package/dist/esm/pm-plugins/utils/handle-bodied-sync-block-removal.js +80 -0
- package/dist/esm/syncedBlockPlugin.js +6 -4
- package/dist/esm/types/index.js +1 -0
- package/dist/esm/ui/DeleteConfirmationModal.js +53 -12
- package/dist/esm/ui/Flag.js +44 -17
- package/dist/types/pm-plugins/main.d.ts +3 -2
- package/dist/types/pm-plugins/utils/handle-bodied-sync-block-removal.d.ts +9 -0
- package/dist/types/pm-plugins/utils/track-sync-blocks.d.ts +2 -11
- package/dist/types/syncedBlockPluginType.d.ts +0 -4
- package/dist/types/types/index.d.ts +28 -2
- package/dist/types/ui/CreateSyncedBlockButton.d.ts +1 -1
- package/dist/types/ui/OverflowMenuSection.d.ts +1 -1
- package/dist/types-ts4.5/pm-plugins/main.d.ts +3 -2
- package/dist/types-ts4.5/pm-plugins/utils/handle-bodied-sync-block-removal.d.ts +9 -0
- package/dist/types-ts4.5/pm-plugins/utils/track-sync-blocks.d.ts +2 -11
- package/dist/types-ts4.5/syncedBlockPluginType.d.ts +0 -4
- package/dist/types-ts4.5/types/index.d.ts +28 -2
- package/dist/types-ts4.5/ui/CreateSyncedBlockButton.d.ts +1 -1
- package/dist/types-ts4.5/ui/OverflowMenuSection.d.ts +1 -1
- 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
|
-
|
|
99
|
-
syncBlockStore: currentSyncBlockStore
|
|
98
|
+
activeFlag,
|
|
99
|
+
syncBlockStore: currentSyncBlockStore,
|
|
100
|
+
bodiedSyncBlockDeletionStatus
|
|
100
101
|
} = syncedBlockPluginKey.getState(editorState);
|
|
101
102
|
return {
|
|
102
|
-
|
|
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
|
-
|
|
17
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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:
|
|
101
|
+
onClick: handleClick(false)
|
|
59
102
|
}, formatMessage(messages.deleteConfirmationModalCancelButton)), /*#__PURE__*/React.createElement(Button, {
|
|
60
103
|
appearance: "warning",
|
|
61
|
-
onClick:
|
|
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
|
};
|
package/dist/es2019/ui/Flag.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
activeFlag,
|
|
32
|
+
mode
|
|
33
|
+
} = useSharedPluginStateWithSelector(api, ['syncedBlock', 'connectivity'], states => {
|
|
34
|
+
var _states$syncedBlockSt, _states$connectivityS;
|
|
30
35
|
return {
|
|
31
|
-
|
|
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 (!
|
|
43
|
+
if (!activeFlag) {
|
|
38
44
|
return;
|
|
39
45
|
}
|
|
40
46
|
const {
|
|
41
47
|
title,
|
|
42
48
|
description
|
|
43
|
-
} = flagMap[
|
|
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
|
-
|
|
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:
|
|
60
|
-
testId:
|
|
61
|
-
icon: /*#__PURE__*/React.createElement(
|
|
85
|
+
id: activeFlag.id,
|
|
86
|
+
testId: activeFlag.id,
|
|
87
|
+
icon: /*#__PURE__*/React.createElement(StatusWarningIcon, {
|
|
62
88
|
label: "",
|
|
63
|
-
color: "var(--ds-icon-
|
|
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
|
|
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
|
-
|
|
36
|
+
activeFlag: false,
|
|
34
37
|
syncBlockStore: syncBlockStore
|
|
35
38
|
};
|
|
36
39
|
},
|
|
37
40
|
apply: function apply(tr, currentPluginState, oldEditorState) {
|
|
38
|
-
var _meta$
|
|
41
|
+
var _meta$activeFlag, _meta$bodiedSyncBlock;
|
|
39
42
|
var meta = tr.getMeta(syncedBlockPluginKey);
|
|
40
|
-
var
|
|
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
|
-
|
|
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
|
-
|
|
119
|
-
|
|
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$
|
|
154
|
-
api === null || api === void 0 || (_api$
|
|
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
|
-
|
|
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 (
|
|
197
|
-
|
|
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
|
-
|
|
110
|
-
currentSyncBlockStore = _syncedBlockPluginKey.syncBlockStore
|
|
109
|
+
activeFlag = _syncedBlockPluginKey.activeFlag,
|
|
110
|
+
currentSyncBlockStore = _syncedBlockPluginKey.syncBlockStore,
|
|
111
|
+
bodiedSyncBlockDeletionStatus = _syncedBlockPluginKey.bodiedSyncBlockDeletionStatus;
|
|
111
112
|
return {
|
|
112
|
-
|
|
113
|
-
syncBlockStore: currentSyncBlockStore
|
|
113
|
+
activeFlag: activeFlag,
|
|
114
|
+
syncBlockStore: currentSyncBlockStore,
|
|
115
|
+
bodiedSyncBlockDeletionStatus: bodiedSyncBlockDeletionStatus
|
|
114
116
|
};
|
|
115
117
|
}
|
|
116
118
|
};
|
package/dist/esm/types/index.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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:
|
|
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:
|
|
104
|
+
onClick: handleClick(false)
|
|
65
105
|
}, formatMessage(messages.deleteConfirmationModalCancelButton)), /*#__PURE__*/React.createElement(Button, {
|
|
66
106
|
appearance: "warning",
|
|
67
|
-
onClick:
|
|
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
|
};
|