@atlaskit/editor-plugin-synced-block 5.3.1 → 5.3.2
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 +13 -0
- package/afm-cc/tsconfig.json +4 -1
- package/dist/cjs/editor-commands/index.js +4 -0
- package/dist/cjs/pm-plugins/main.js +8 -1
- package/dist/cjs/pm-plugins/utils/handle-bodied-sync-block-removal.js +2 -2
- package/dist/cjs/ui/DeleteConfirmationModal.compiled.css +2 -0
- package/dist/cjs/ui/DeleteConfirmationModal.js +151 -13
- package/dist/cjs/ui/SyncBlockLabel.js +44 -94
- package/dist/cjs/ui/SyncBlockRendererWrapper.js +2 -1
- package/dist/cjs/ui/SyncedLocationDropdown.js +9 -3
- package/dist/cjs/ui/floating-toolbar.js +2 -2
- package/dist/cjs/ui/utils/time.js +63 -0
- package/dist/es2019/editor-commands/index.js +4 -0
- package/dist/es2019/pm-plugins/main.js +8 -1
- package/dist/es2019/pm-plugins/utils/handle-bodied-sync-block-removal.js +2 -2
- package/dist/es2019/ui/DeleteConfirmationModal.compiled.css +2 -0
- package/dist/es2019/ui/DeleteConfirmationModal.js +121 -12
- package/dist/es2019/ui/SyncBlockLabel.js +18 -72
- package/dist/es2019/ui/SyncBlockRendererWrapper.js +2 -1
- package/dist/es2019/ui/SyncedLocationDropdown.js +9 -3
- package/dist/es2019/ui/floating-toolbar.js +2 -2
- package/dist/es2019/ui/utils/time.js +56 -0
- package/dist/esm/editor-commands/index.js +4 -0
- package/dist/esm/pm-plugins/main.js +8 -1
- package/dist/esm/pm-plugins/utils/handle-bodied-sync-block-removal.js +2 -2
- package/dist/esm/ui/DeleteConfirmationModal.compiled.css +2 -0
- package/dist/esm/ui/DeleteConfirmationModal.js +152 -14
- package/dist/esm/ui/SyncBlockLabel.js +42 -92
- package/dist/esm/ui/SyncBlockRendererWrapper.js +2 -1
- package/dist/esm/ui/SyncedLocationDropdown.js +9 -3
- package/dist/esm/ui/floating-toolbar.js +2 -2
- package/dist/esm/ui/utils/time.js +56 -0
- package/dist/types/pm-plugins/utils/handle-bodied-sync-block-removal.d.ts +2 -2
- package/dist/types/ui/SyncBlockLabel.d.ts +2 -4
- package/dist/types/ui/SyncBlockRendererWrapper.d.ts +1 -1
- package/dist/types/ui/utils/time.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/utils/handle-bodied-sync-block-removal.d.ts +2 -2
- package/dist/types-ts4.5/ui/SyncBlockLabel.d.ts +2 -4
- package/dist/types-ts4.5/ui/SyncBlockRendererWrapper.d.ts +1 -1
- package/dist/types-ts4.5/ui/utils/time.d.ts +2 -0
- package/package.json +3 -3
|
@@ -197,6 +197,10 @@ export const unsync = (storeManager, isBodiedSyncBlock, view) => {
|
|
|
197
197
|
return false;
|
|
198
198
|
}
|
|
199
199
|
if (isBodiedSyncBlock) {
|
|
200
|
+
const content = syncBlock === null || syncBlock === void 0 ? void 0 : syncBlock.node.content;
|
|
201
|
+
const tr = state.tr;
|
|
202
|
+
tr.replaceWith(syncBlock.pos, syncBlock.pos + syncBlock.node.nodeSize, content).setMeta('deletionReason', 'source-block-unsynced');
|
|
203
|
+
view.dispatch(tr);
|
|
200
204
|
return true;
|
|
201
205
|
}
|
|
202
206
|
|
|
@@ -31,6 +31,13 @@ const showCopiedFlag = api => {
|
|
|
31
31
|
});
|
|
32
32
|
}, 0);
|
|
33
33
|
};
|
|
34
|
+
const getDeleteReason = tr => {
|
|
35
|
+
const reason = tr.getMeta('deletionReason');
|
|
36
|
+
if (!reason) {
|
|
37
|
+
return 'source-block-deleted';
|
|
38
|
+
}
|
|
39
|
+
return reason;
|
|
40
|
+
};
|
|
34
41
|
export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api) => {
|
|
35
42
|
const {
|
|
36
43
|
useLongPressSelection = false
|
|
@@ -199,7 +206,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
|
|
|
199
206
|
if (!isOffline) {
|
|
200
207
|
if (bodiedSyncBlockRemoved.length > 0) {
|
|
201
208
|
confirmationTransactionRef.current = tr;
|
|
202
|
-
return handleBodiedSyncBlockRemoval(
|
|
209
|
+
return handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, fg('platform_synced_block_dogfooding') ? getDeleteReason(tr) : undefined);
|
|
203
210
|
}
|
|
204
211
|
if (bodiedSyncBlockAdded.length > 0) {
|
|
205
212
|
if (Boolean(tr.getMeta(pmHistoryPluginKey))) {
|
|
@@ -19,7 +19,7 @@ const onDismissed = syncBlockStore => tr => {
|
|
|
19
19
|
bodiedSyncBlockDeletionStatus: 'none'
|
|
20
20
|
});
|
|
21
21
|
};
|
|
22
|
-
export const handleBodiedSyncBlockRemoval = (
|
|
22
|
+
export const handleBodiedSyncBlockRemoval = (bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, deletionReason) => {
|
|
23
23
|
// Clear potential old pending deletion to retreat the deletion as first attempt
|
|
24
24
|
syncBlockStore.sourceManager.clearPendingDeletion();
|
|
25
25
|
|
|
@@ -27,7 +27,7 @@ export const handleBodiedSyncBlockRemoval = (tr, bodiedSyncBlockRemoved, syncBlo
|
|
|
27
27
|
// we block the transaction here, and wait for user confirmation to proceed with deletion.
|
|
28
28
|
// See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
|
|
29
29
|
// proceed with deletion.
|
|
30
|
-
syncBlockStore.sourceManager.deleteSyncBlocksWithConfirmation(bodiedSyncBlockRemoved.map(node => node.attrs), () => {
|
|
30
|
+
syncBlockStore.sourceManager.deleteSyncBlocksWithConfirmation(bodiedSyncBlockRemoved.map(node => node.attrs), deletionReason, () => {
|
|
31
31
|
var _api$core2;
|
|
32
32
|
const confirmationTransaction = confirmationTransactionRef.current;
|
|
33
33
|
if (!confirmationTransaction) {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/* DeleteConfirmationModal.tsx generated by @compiled/babel-plugin v0.38.1 */
|
|
2
|
+
import "./DeleteConfirmationModal.compiled.css";
|
|
3
|
+
import { ax, ix } from "@compiled/react/runtime";
|
|
1
4
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
2
5
|
import { useIntl } from 'react-intl-next';
|
|
3
6
|
import Button from '@atlaskit/button/new';
|
|
@@ -6,15 +9,38 @@ import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages'
|
|
|
6
9
|
import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
7
10
|
import ModalDialog, { ModalBody, ModalFooter, ModalHeader, ModalTitle, ModalTransition } from '@atlaskit/modal-dialog';
|
|
8
11
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
9
|
-
import { Text } from '@atlaskit/primitives/compiled';
|
|
12
|
+
import { Text, Box } from '@atlaskit/primitives/compiled';
|
|
13
|
+
import Spinner from '@atlaskit/spinner';
|
|
10
14
|
import { syncedBlockPluginKey } from '../pm-plugins/main';
|
|
15
|
+
const modalContentMap = {
|
|
16
|
+
'source-block-deleted': {
|
|
17
|
+
titleMultiple: messages.deleteConfirmationModalTitleSingle,
|
|
18
|
+
titleSingle: messages.deleteConfirmationModalTitleSingle,
|
|
19
|
+
descriptionSingle: messages.deleteConfirmationModalDescriptionNoRef,
|
|
20
|
+
descriptionMultiple: messages.deleteConfirmationModalDescription,
|
|
21
|
+
confirmButtonLabel: messages.deleteConfirmationModalDeleteButton
|
|
22
|
+
},
|
|
23
|
+
'source-block-unsynced': {
|
|
24
|
+
titleMultiple: messages.unsyncConfirmationModalTitle,
|
|
25
|
+
titleSingle: messages.unsyncConfirmationModalTitle,
|
|
26
|
+
descriptionSingle: messages.unsyncConfirmationModalDescriptionSingle,
|
|
27
|
+
descriptionMultiple: messages.unsyncConfirmationModalDescriptionMultiple,
|
|
28
|
+
confirmButtonLabel: messages.deleteConfirmationModalUnsyncButton
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const styles = {
|
|
32
|
+
spinner: "_1mou1wug _195g1wug"
|
|
33
|
+
};
|
|
11
34
|
export const DeleteConfirmationModal = ({
|
|
12
35
|
syncBlockStoreManager,
|
|
13
36
|
api
|
|
14
37
|
}) => {
|
|
15
|
-
var _api$core2, _api$core4, _api$core6;
|
|
38
|
+
var _api$core2, _api$core4, _api$core6, _syncBlockIds$length;
|
|
16
39
|
const [isOpen, setIsOpen] = useState(false);
|
|
17
|
-
const [
|
|
40
|
+
const [syncBlockIds, setSyncBlockIds] = useState(undefined);
|
|
41
|
+
const [referenceCount, setReferenceCount] = useState(undefined);
|
|
42
|
+
const [deleteReason, setDeleteReason] = useState('source-block-deleted');
|
|
43
|
+
const [fetchStatus, setFetchStatus] = useState('none');
|
|
18
44
|
const {
|
|
19
45
|
mode,
|
|
20
46
|
bodiedSyncBlockDeletionStatus,
|
|
@@ -39,6 +65,8 @@ export const DeleteConfirmationModal = ({
|
|
|
39
65
|
}
|
|
40
66
|
if (!confirm) {
|
|
41
67
|
setIsOpen(false);
|
|
68
|
+
setFetchStatus('none');
|
|
69
|
+
setReferenceCount(undefined);
|
|
42
70
|
}
|
|
43
71
|
api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
|
|
44
72
|
tr
|
|
@@ -49,9 +77,12 @@ export const DeleteConfirmationModal = ({
|
|
|
49
77
|
});
|
|
50
78
|
});
|
|
51
79
|
}, [api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions]);
|
|
52
|
-
const confirmationCallback = useCallback(
|
|
80
|
+
const confirmationCallback = useCallback((syncBlockIds, deleteReason) => {
|
|
53
81
|
setIsOpen(true);
|
|
54
|
-
|
|
82
|
+
setSyncBlockIds(syncBlockIds);
|
|
83
|
+
if (deleteReason) {
|
|
84
|
+
setDeleteReason(deleteReason);
|
|
85
|
+
}
|
|
55
86
|
const confirmedPromise = new Promise(resolve => {
|
|
56
87
|
resolverRef.current = resolve;
|
|
57
88
|
});
|
|
@@ -79,6 +110,7 @@ export const DeleteConfirmationModal = ({
|
|
|
79
110
|
var _api$core5;
|
|
80
111
|
// auto close modal once deletion is successful
|
|
81
112
|
setIsOpen(false);
|
|
113
|
+
setFetchStatus('none');
|
|
82
114
|
api === null || api === void 0 ? void 0 : (_api$core5 = api.core) === null || _api$core5 === void 0 ? void 0 : _api$core5.actions.execute(({
|
|
83
115
|
tr
|
|
84
116
|
}) => {
|
|
@@ -89,15 +121,55 @@ export const DeleteConfirmationModal = ({
|
|
|
89
121
|
});
|
|
90
122
|
}
|
|
91
123
|
}, [api === null || api === void 0 ? void 0 : (_api$core6 = api.core) === null || _api$core6 === void 0 ? void 0 : _api$core6.actions, bodiedSyncBlockDeletionStatus, isOpen]);
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (isOpen && syncBlockIds !== undefined && fg('platform_synced_block_dogfooding')) {
|
|
126
|
+
let referenceCount = 0;
|
|
127
|
+
setFetchStatus('loading');
|
|
128
|
+
let fetchFailed = false;
|
|
129
|
+
syncBlockIds.forEach(async syncBlockId => {
|
|
130
|
+
if (fetchFailed) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const references = await syncBlockStoreManager.sourceManager.fetchReferences(syncBlockId.resourceId);
|
|
134
|
+
if (references.error) {
|
|
135
|
+
// Consider fetch fails as soon as one of the fetches fails
|
|
136
|
+
setFetchStatus('error');
|
|
137
|
+
fetchFailed = true;
|
|
138
|
+
return;
|
|
139
|
+
} else {
|
|
140
|
+
var _references$reference, _references$reference2;
|
|
141
|
+
referenceCount += (_references$reference = (_references$reference2 = references.references) === null || _references$reference2 === void 0 ? void 0 : _references$reference2.length) !== null && _references$reference !== void 0 ? _references$reference : 0;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
if (!fetchFailed) {
|
|
145
|
+
setReferenceCount(referenceCount);
|
|
146
|
+
setFetchStatus('success');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}, [isOpen, syncBlockIds, syncBlockStoreManager.sourceManager]);
|
|
92
150
|
return /*#__PURE__*/React.createElement(ModalTransition, null, isOpen && /*#__PURE__*/React.createElement(ModalDialog, {
|
|
93
151
|
onClose: handleClick(false),
|
|
94
|
-
testId: "sync-block-delete-confirmation"
|
|
95
|
-
|
|
152
|
+
testId: "sync-block-delete-confirmation",
|
|
153
|
+
height: 184
|
|
154
|
+
}, fg('platform_synced_block_dogfooding') ? /*#__PURE__*/React.createElement(React.Fragment, null, referenceCount === undefined ? /*#__PURE__*/React.createElement(Box, {
|
|
155
|
+
xcss: styles.spinner
|
|
156
|
+
}, /*#__PURE__*/React.createElement(Spinner, {
|
|
157
|
+
size: "large"
|
|
158
|
+
})) : /*#__PURE__*/React.createElement(ModalContent, {
|
|
159
|
+
content: modalContentMap[deleteReason],
|
|
160
|
+
referenceCount: referenceCount,
|
|
161
|
+
handleClick: handleClick,
|
|
162
|
+
formatMessage: formatMessage,
|
|
163
|
+
isDeleting: bodiedSyncBlockDeletionStatus === 'processing',
|
|
164
|
+
isDisabled: isOfflineMode(mode),
|
|
165
|
+
deleteReason: deleteReason,
|
|
166
|
+
failToFetch: fetchStatus === 'error'
|
|
167
|
+
})) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ModalHeader, {
|
|
96
168
|
hasCloseButton: true
|
|
97
169
|
}, /*#__PURE__*/React.createElement(ModalTitle, {
|
|
98
170
|
appearance: "warning"
|
|
99
|
-
}, formatMessage(messages.
|
|
100
|
-
syncBlockCount
|
|
171
|
+
}, formatMessage(messages.deleteConfirmationModalTitleSingle))), /*#__PURE__*/React.createElement(ModalBody, null, /*#__PURE__*/React.createElement(Text, null, formatMessage(messages.deleteConfirmationModalDescription, {
|
|
172
|
+
syncBlockCount: (_syncBlockIds$length = syncBlockIds === null || syncBlockIds === void 0 ? void 0 : syncBlockIds.length) !== null && _syncBlockIds$length !== void 0 ? _syncBlockIds$length : 1
|
|
101
173
|
}))), /*#__PURE__*/React.createElement(ModalFooter, null, /*#__PURE__*/React.createElement(Button, {
|
|
102
174
|
appearance: "subtle",
|
|
103
175
|
onClick: handleClick(false)
|
|
@@ -106,7 +178,44 @@ export const DeleteConfirmationModal = ({
|
|
|
106
178
|
onClick: handleClick(true),
|
|
107
179
|
autoFocus: true,
|
|
108
180
|
isDisabled: isOfflineMode(mode),
|
|
109
|
-
isLoading: bodiedSyncBlockDeletionStatus === 'processing'
|
|
110
|
-
|
|
111
|
-
|
|
181
|
+
isLoading: bodiedSyncBlockDeletionStatus === 'processing'
|
|
182
|
+
}, formatMessage(messages.deleteConfirmationModalDeleteButton))))));
|
|
183
|
+
};
|
|
184
|
+
const ModalContent = ({
|
|
185
|
+
content,
|
|
186
|
+
referenceCount,
|
|
187
|
+
handleClick,
|
|
188
|
+
formatMessage,
|
|
189
|
+
isDeleting,
|
|
190
|
+
isDisabled,
|
|
191
|
+
deleteReason,
|
|
192
|
+
failToFetch
|
|
193
|
+
}) => {
|
|
194
|
+
const {
|
|
195
|
+
titleMultiple,
|
|
196
|
+
titleSingle,
|
|
197
|
+
descriptionSingle,
|
|
198
|
+
descriptionMultiple,
|
|
199
|
+
confirmButtonLabel
|
|
200
|
+
} = content;
|
|
201
|
+
const hasNoReferenceOrFailToFetch = referenceCount === 0 || failToFetch;
|
|
202
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ModalHeader, {
|
|
203
|
+
hasCloseButton: true
|
|
204
|
+
}, /*#__PURE__*/React.createElement(ModalTitle, {
|
|
205
|
+
appearance: "warning"
|
|
206
|
+
}, hasNoReferenceOrFailToFetch ? formatMessage(titleSingle) : formatMessage(titleMultiple, {
|
|
207
|
+
count: referenceCount
|
|
208
|
+
}))), /*#__PURE__*/React.createElement(ModalBody, null, /*#__PURE__*/React.createElement(Text, null, hasNoReferenceOrFailToFetch ? formatMessage(descriptionSingle) : formatMessage(descriptionMultiple, {
|
|
209
|
+
syncBlockCount: referenceCount
|
|
210
|
+
}))), /*#__PURE__*/React.createElement(ModalFooter, null, /*#__PURE__*/React.createElement(Button, {
|
|
211
|
+
appearance: "subtle",
|
|
212
|
+
onClick: handleClick(false)
|
|
213
|
+
}, formatMessage(messages.deleteConfirmationModalCancelButton)), /*#__PURE__*/React.createElement(Button, {
|
|
214
|
+
appearance: "warning",
|
|
215
|
+
onClick: handleClick(true),
|
|
216
|
+
autoFocus: true,
|
|
217
|
+
isDisabled: isDisabled,
|
|
218
|
+
isLoading: isDeleting,
|
|
219
|
+
testId: `synced-block-delete-confirmation-modal-${deleteReason}-button`
|
|
220
|
+
}, formatMessage(confirmButtonLabel))));
|
|
112
221
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React, { useCallback, useState } from 'react';
|
|
2
|
-
import isYesterday from 'date-fns/isYesterday';
|
|
3
2
|
import { useIntl } from 'react-intl-next';
|
|
4
3
|
import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
|
|
5
4
|
import { SyncBlockLabelSharedCssClassName } from '@atlaskit/editor-common/sync-block';
|
|
@@ -8,76 +7,21 @@ import { fg } from '@atlaskit/platform-feature-flags';
|
|
|
8
7
|
import { Text } from '@atlaskit/primitives/compiled';
|
|
9
8
|
import Tooltip from '@atlaskit/tooltip';
|
|
10
9
|
import VisuallyHidden from '@atlaskit/visually-hidden';
|
|
10
|
+
import { formatElapsedTime } from './utils/time';
|
|
11
11
|
const SyncBlockLabelDataId = 'sync-block-label';
|
|
12
|
-
const SECONDS_IN_MINUTE = 60;
|
|
13
|
-
const SECONDS_IN_HOUR = SECONDS_IN_MINUTE * 60;
|
|
14
|
-
const SECONDS_IN_DAY = SECONDS_IN_HOUR * 24;
|
|
15
|
-
const SECONDS_IN_WEEK = SECONDS_IN_DAY * 7;
|
|
16
|
-
const SECONDS_IN_MONTH = SECONDS_IN_DAY * 30;
|
|
17
|
-
const SECONDS_IN_YEAR = SECONDS_IN_DAY * 365;
|
|
18
|
-
export const formatElapsedTime = (isoDate, intl) => {
|
|
19
|
-
const now = Date.now();
|
|
20
|
-
const date = new Date(isoDate).getTime();
|
|
21
|
-
const diffInSeconds = Math.floor((now - date) / 1000);
|
|
22
|
-
const dateObj = new Date(isoDate);
|
|
23
|
-
|
|
24
|
-
// Show "yesterday" when timestamp is from the previous calendar day
|
|
25
|
-
if (isYesterday(dateObj) && diffInSeconds >= SECONDS_IN_DAY) {
|
|
26
|
-
return intl.formatRelativeTime(-1, 'day', {
|
|
27
|
-
numeric: 'auto',
|
|
28
|
-
style: 'long'
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
if (diffInSeconds < SECONDS_IN_MINUTE) {
|
|
32
|
-
return intl.formatRelativeTime(-Math.max(diffInSeconds, 1), 'second', {
|
|
33
|
-
style: 'long'
|
|
34
|
-
});
|
|
35
|
-
} else if (diffInSeconds < SECONDS_IN_HOUR) {
|
|
36
|
-
const minutes = Math.floor(diffInSeconds / SECONDS_IN_MINUTE);
|
|
37
|
-
return intl.formatRelativeTime(-minutes, 'minute', {
|
|
38
|
-
style: 'long'
|
|
39
|
-
});
|
|
40
|
-
} else if (diffInSeconds < SECONDS_IN_DAY) {
|
|
41
|
-
const hours = Math.floor(diffInSeconds / SECONDS_IN_HOUR);
|
|
42
|
-
return intl.formatRelativeTime(-hours, 'hour', {
|
|
43
|
-
style: 'long'
|
|
44
|
-
});
|
|
45
|
-
} else if (diffInSeconds < SECONDS_IN_WEEK) {
|
|
46
|
-
const days = Math.floor(diffInSeconds / SECONDS_IN_DAY);
|
|
47
|
-
return intl.formatRelativeTime(-days, 'day', {
|
|
48
|
-
style: 'long'
|
|
49
|
-
});
|
|
50
|
-
} else if (diffInSeconds < SECONDS_IN_MONTH) {
|
|
51
|
-
const weeks = Math.floor(diffInSeconds / SECONDS_IN_WEEK);
|
|
52
|
-
return intl.formatRelativeTime(-weeks, 'week', {
|
|
53
|
-
style: 'long'
|
|
54
|
-
});
|
|
55
|
-
} else if (diffInSeconds < SECONDS_IN_YEAR) {
|
|
56
|
-
const months = Math.floor(diffInSeconds / SECONDS_IN_MONTH);
|
|
57
|
-
return intl.formatRelativeTime(-months, 'month', {
|
|
58
|
-
style: 'long'
|
|
59
|
-
});
|
|
60
|
-
} else {
|
|
61
|
-
const years = Math.floor(diffInSeconds / SECONDS_IN_YEAR);
|
|
62
|
-
return intl.formatRelativeTime(-years, 'year', {
|
|
63
|
-
style: 'long'
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
12
|
const SyncBlockLabelComponent = ({
|
|
13
|
+
contentUpdatedAt,
|
|
68
14
|
isSource,
|
|
69
|
-
useFetchSyncBlockTitle,
|
|
70
15
|
localId,
|
|
71
|
-
|
|
16
|
+
title
|
|
72
17
|
}) => {
|
|
73
18
|
const intl = useIntl();
|
|
74
19
|
const {
|
|
75
20
|
formatMessage
|
|
76
21
|
} = intl;
|
|
77
|
-
const title = useFetchSyncBlockTitle === null || useFetchSyncBlockTitle === void 0 ? void 0 : useFetchSyncBlockTitle();
|
|
78
22
|
const [tooltipContent, setTooltipContent] = useState(formatMessage(messages.defaultSyncBlockTooltip));
|
|
79
23
|
let tooltipMessage = formatMessage(messages.defaultSyncBlockTooltip);
|
|
80
|
-
if (isSource) {
|
|
24
|
+
if (isSource && !fg('platform_synced_block_dogfooding')) {
|
|
81
25
|
tooltipMessage = formatMessage(messages.sourceSyncBlockTooltip);
|
|
82
26
|
} else if (title) {
|
|
83
27
|
tooltipMessage = formatMessage(messages.referenceSyncBlockTooltip, {
|
|
@@ -106,18 +50,7 @@ const SyncBlockLabelComponent = ({
|
|
|
106
50
|
setTooltipContent(tooltipContent);
|
|
107
51
|
}, [contentUpdatedAt, formatMessage, intl, tooltipMessage]);
|
|
108
52
|
const ariaDescribedById = `sync-block-label-description-${localId}`;
|
|
109
|
-
|
|
110
|
-
position: "top",
|
|
111
|
-
content: fg('platform_synced_block_dogfooding') ? tooltipContent : tooltipMessage
|
|
112
|
-
// workaround because tooltip adds aria-describedby with a new id every time the tooltip is opened
|
|
113
|
-
// this causes an infinite rerender loop because of the forwardRef from the node view we are inside in bodiedSyncBlock
|
|
114
|
-
// tooltip content is available for screen readers in visually hidden content after the label
|
|
115
|
-
,
|
|
116
|
-
isScreenReaderAnnouncementDisabled: true
|
|
117
|
-
// using this to ensure that the 'last edited' time is updated when the tooltip is opened
|
|
118
|
-
,
|
|
119
|
-
onShow: updateTooltipContent
|
|
120
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
53
|
+
const LabelComponent = () => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
121
54
|
"data-testid": SyncBlockLabelDataId
|
|
122
55
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
|
|
123
56
|
,
|
|
@@ -137,5 +70,18 @@ const SyncBlockLabelComponent = ({
|
|
|
137
70
|
}, title)), /*#__PURE__*/React.createElement(VisuallyHidden, {
|
|
138
71
|
id: ariaDescribedById
|
|
139
72
|
}, tooltipContent));
|
|
73
|
+
const LabelWithTooltip = () => /*#__PURE__*/React.createElement(Tooltip, {
|
|
74
|
+
position: "top",
|
|
75
|
+
content: fg('platform_synced_block_dogfooding') ? tooltipContent : tooltipMessage
|
|
76
|
+
// workaround because tooltip adds aria-describedby with a new id every time the tooltip is opened
|
|
77
|
+
// this causes an infinite rerender loop because of the forwardRef from the node view we are inside in bodiedSyncBlock
|
|
78
|
+
// tooltip content is available for screen readers in visually hidden content after the label
|
|
79
|
+
,
|
|
80
|
+
isScreenReaderAnnouncementDisabled: true
|
|
81
|
+
// using this to ensure that the 'last edited' time is updated when the tooltip is opened
|
|
82
|
+
,
|
|
83
|
+
onShow: updateTooltipContent
|
|
84
|
+
}, /*#__PURE__*/React.createElement(LabelComponent, null));
|
|
85
|
+
return fg('platform_synced_block_dogfooding') ? isSource ? /*#__PURE__*/React.createElement(LabelComponent, null) : /*#__PURE__*/React.createElement(LabelWithTooltip, null) : /*#__PURE__*/React.createElement(LabelWithTooltip, null);
|
|
140
86
|
};
|
|
141
87
|
export const SyncBlockLabel = /*#__PURE__*/React.memo(SyncBlockLabelComponent);
|
|
@@ -11,6 +11,7 @@ const SyncBlockRendererWrapperComponent = ({
|
|
|
11
11
|
}) => {
|
|
12
12
|
var _syncBlockFetchResult, _syncBlockFetchResult2;
|
|
13
13
|
const syncBlockFetchResult = useFetchSyncBlockData();
|
|
14
|
+
const title = useFetchSyncBlockTitle === null || useFetchSyncBlockTitle === void 0 ? void 0 : useFetchSyncBlockTitle();
|
|
14
15
|
const contentUpdatedAt = syncBlockFetchResult === null || syncBlockFetchResult === void 0 ? void 0 : (_syncBlockFetchResult = syncBlockFetchResult.syncBlockInstance) === null || _syncBlockFetchResult === void 0 ? void 0 : (_syncBlockFetchResult2 = _syncBlockFetchResult.data) === null || _syncBlockFetchResult2 === void 0 ? void 0 : _syncBlockFetchResult2.contentUpdatedAt;
|
|
15
16
|
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
|
16
17
|
"data-testid": SyncBlockRendererWrapperDataId
|
|
@@ -22,7 +23,7 @@ const SyncBlockRendererWrapperComponent = ({
|
|
|
22
23
|
api
|
|
23
24
|
})), /*#__PURE__*/React.createElement(SyncBlockLabel, {
|
|
24
25
|
isSource: false,
|
|
25
|
-
|
|
26
|
+
title: title,
|
|
26
27
|
contentUpdatedAt: contentUpdatedAt,
|
|
27
28
|
localId: localId
|
|
28
29
|
}));
|
|
@@ -4,6 +4,7 @@ import "./SyncedLocationDropdown.compiled.css";
|
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { ax, ix } from "@compiled/react/runtime";
|
|
6
6
|
import { useState, useEffect } from 'react';
|
|
7
|
+
import { cx } from '@compiled/react';
|
|
7
8
|
import DropdownMenu, { DropdownItem, DropdownItemGroup } from '@atlaskit/dropdown-menu';
|
|
8
9
|
import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
|
|
9
10
|
import { FloatingToolbarButton as Button } from '@atlaskit/editor-common/ui';
|
|
@@ -29,13 +30,18 @@ const styles = {
|
|
|
29
30
|
note: "_syaz1rpy _o5721q9c",
|
|
30
31
|
lozenge: "_ahbq12x7 _1ul91wqb",
|
|
31
32
|
noResultsContainer: "_1bsbo8uj _y3gn1h6o",
|
|
32
|
-
dropdownContent: "_1rjcv77o _1bsbsmdz
|
|
33
|
+
dropdownContent: "_1rjcv77o _1bsbsmdz _c71lko4j _1e0c1txw _1bah1h6o _4cvr1h6o",
|
|
34
|
+
containerWithMinHeight: "_1tkeqkoa",
|
|
33
35
|
contentContainer: "_y44vfmxe _1bsb1osq _1wpz1fhb _18m91wug",
|
|
34
36
|
errorContainer: "_1bsbo8uj _1e0c1txw",
|
|
35
37
|
errorIcon: "_1mour5cr",
|
|
36
38
|
learnMoreLink: "_4bfu1r31 _1hmsglyw _ajmmnqa1",
|
|
37
39
|
requestAccess: "_1bsb19n7 _o5721q9c _ahbq12x7 _syaz1rpy"
|
|
38
40
|
};
|
|
41
|
+
const shouldApplyMinHeight = (fetchStatus, itemCount) => {
|
|
42
|
+
// When there are 1/2 items, dropdown height is less than minHeight 144px
|
|
43
|
+
return !(fetchStatus === 'success' && itemCount > 0);
|
|
44
|
+
};
|
|
39
45
|
const ItemTitle = ({
|
|
40
46
|
title,
|
|
41
47
|
formatMessage,
|
|
@@ -50,7 +56,7 @@ const ItemTitle = ({
|
|
|
50
56
|
}, title), onSameDocument && /*#__PURE__*/React.createElement(Box, {
|
|
51
57
|
as: "span",
|
|
52
58
|
xcss: styles.note
|
|
53
|
-
}, "\xA0-
|
|
59
|
+
}, "\xA0-", ' ', formatMessage(productType === 'confluence-page' ? messages.syncedLocationDropdownTitleNoteForConfluencePage : messages.syncedLocationDropdownTitleNoteForJiraWorkItem)), isSource && /*#__PURE__*/React.createElement(Box, {
|
|
54
60
|
as: "span",
|
|
55
61
|
xcss: styles.lozenge
|
|
56
62
|
}, /*#__PURE__*/React.createElement(Lozenge, null, formatMessage(messages.syncedLocationDropdownSourceLozenge))), !hasAccess && /*#__PURE__*/React.createElement(Box, {
|
|
@@ -245,7 +251,7 @@ const DropdownContent = ({
|
|
|
245
251
|
}
|
|
246
252
|
};
|
|
247
253
|
return /*#__PURE__*/React.createElement(Box, {
|
|
248
|
-
xcss: styles.dropdownContent
|
|
254
|
+
xcss: cx(styles.dropdownContent, shouldApplyMinHeight(fetchStatus, referenceData.length) && styles.containerWithMinHeight)
|
|
249
255
|
}, content());
|
|
250
256
|
};
|
|
251
257
|
const LoadingScreen = () => {
|
|
@@ -14,13 +14,13 @@ import { findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plu
|
|
|
14
14
|
import { SYNCED_BLOCK_BUTTON_TEST_ID } from '../types';
|
|
15
15
|
import { SyncedLocationDropdown } from './SyncedLocationDropdown';
|
|
16
16
|
export const getToolbarConfig = (state, intl, api, syncBlockStore) => {
|
|
17
|
-
var _api$decorations, _api$connectivity, _api$connectivity$sha;
|
|
17
|
+
var _syncBlockInstance$er, _api$decorations, _api$connectivity, _api$connectivity$sha;
|
|
18
18
|
const syncBlockObject = findSyncBlockOrBodiedSyncBlock(state.schema, state.selection);
|
|
19
19
|
if (!syncBlockObject) {
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
const syncBlockInstance = syncBlockStore.referenceManager.getFromCache(syncBlockObject.node.attrs.resourceId);
|
|
23
|
-
const isUnsyncedBlock = (syncBlockInstance === null || syncBlockInstance === void 0 ? void 0 : syncBlockInstance.error) === SyncBlockError.NotFound;
|
|
23
|
+
const isUnsyncedBlock = (syncBlockInstance === null || syncBlockInstance === void 0 ? void 0 : (_syncBlockInstance$er = syncBlockInstance.error) === null || _syncBlockInstance$er === void 0 ? void 0 : _syncBlockInstance$er.type) === SyncBlockError.NotFound;
|
|
24
24
|
const isErroredBlock = syncBlockInstance === null || syncBlockInstance === void 0 ? void 0 : syncBlockInstance.error;
|
|
25
25
|
const {
|
|
26
26
|
schema: {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import isYesterday from 'date-fns/isYesterday';
|
|
2
|
+
const SECONDS_IN_MINUTE = 60;
|
|
3
|
+
const SECONDS_IN_HOUR = SECONDS_IN_MINUTE * 60;
|
|
4
|
+
const SECONDS_IN_DAY = SECONDS_IN_HOUR * 24;
|
|
5
|
+
const SECONDS_IN_WEEK = SECONDS_IN_DAY * 7;
|
|
6
|
+
const SECONDS_IN_MONTH = SECONDS_IN_DAY * 30;
|
|
7
|
+
const SECONDS_IN_YEAR = SECONDS_IN_DAY * 365;
|
|
8
|
+
export const formatElapsedTime = (isoDate, intl) => {
|
|
9
|
+
const now = Date.now();
|
|
10
|
+
const date = new Date(isoDate).getTime();
|
|
11
|
+
const diffInSeconds = Math.floor((now - date) / 1000);
|
|
12
|
+
const dateObj = new Date(isoDate);
|
|
13
|
+
|
|
14
|
+
// Show "yesterday" when timestamp is from the previous calendar day
|
|
15
|
+
if (isYesterday(dateObj) && diffInSeconds >= SECONDS_IN_DAY) {
|
|
16
|
+
return intl.formatRelativeTime(-1, 'day', {
|
|
17
|
+
numeric: 'auto',
|
|
18
|
+
style: 'long'
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
if (diffInSeconds < SECONDS_IN_MINUTE) {
|
|
22
|
+
return intl.formatRelativeTime(-Math.max(diffInSeconds, 1), 'second', {
|
|
23
|
+
style: 'long'
|
|
24
|
+
});
|
|
25
|
+
} else if (diffInSeconds < SECONDS_IN_HOUR) {
|
|
26
|
+
const minutes = Math.floor(diffInSeconds / SECONDS_IN_MINUTE);
|
|
27
|
+
return intl.formatRelativeTime(-minutes, 'minute', {
|
|
28
|
+
style: 'long'
|
|
29
|
+
});
|
|
30
|
+
} else if (diffInSeconds < SECONDS_IN_DAY) {
|
|
31
|
+
const hours = Math.floor(diffInSeconds / SECONDS_IN_HOUR);
|
|
32
|
+
return intl.formatRelativeTime(-hours, 'hour', {
|
|
33
|
+
style: 'long'
|
|
34
|
+
});
|
|
35
|
+
} else if (diffInSeconds < SECONDS_IN_WEEK) {
|
|
36
|
+
const days = Math.floor(diffInSeconds / SECONDS_IN_DAY);
|
|
37
|
+
return intl.formatRelativeTime(-days, 'day', {
|
|
38
|
+
style: 'long'
|
|
39
|
+
});
|
|
40
|
+
} else if (diffInSeconds < SECONDS_IN_MONTH) {
|
|
41
|
+
const weeks = Math.floor(diffInSeconds / SECONDS_IN_WEEK);
|
|
42
|
+
return intl.formatRelativeTime(-weeks, 'week', {
|
|
43
|
+
style: 'long'
|
|
44
|
+
});
|
|
45
|
+
} else if (diffInSeconds < SECONDS_IN_YEAR) {
|
|
46
|
+
const months = Math.floor(diffInSeconds / SECONDS_IN_MONTH);
|
|
47
|
+
return intl.formatRelativeTime(-months, 'month', {
|
|
48
|
+
style: 'long'
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
const years = Math.floor(diffInSeconds / SECONDS_IN_YEAR);
|
|
52
|
+
return intl.formatRelativeTime(-years, 'year', {
|
|
53
|
+
style: 'long'
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
@@ -187,6 +187,10 @@ export var unsync = function unsync(storeManager, isBodiedSyncBlock, view) {
|
|
|
187
187
|
return false;
|
|
188
188
|
}
|
|
189
189
|
if (isBodiedSyncBlock) {
|
|
190
|
+
var content = syncBlock === null || syncBlock === void 0 ? void 0 : syncBlock.node.content;
|
|
191
|
+
var tr = state.tr;
|
|
192
|
+
tr.replaceWith(syncBlock.pos, syncBlock.pos + syncBlock.node.nodeSize, content).setMeta('deletionReason', 'source-block-unsynced');
|
|
193
|
+
view.dispatch(tr);
|
|
190
194
|
return true;
|
|
191
195
|
}
|
|
192
196
|
|
|
@@ -36,6 +36,13 @@ var showCopiedFlag = function showCopiedFlag(api) {
|
|
|
36
36
|
});
|
|
37
37
|
}, 0);
|
|
38
38
|
};
|
|
39
|
+
var getDeleteReason = function getDeleteReason(tr) {
|
|
40
|
+
var reason = tr.getMeta('deletionReason');
|
|
41
|
+
if (!reason) {
|
|
42
|
+
return 'source-block-deleted';
|
|
43
|
+
}
|
|
44
|
+
return reason;
|
|
45
|
+
};
|
|
39
46
|
export var createPlugin = function createPlugin(options, pmPluginFactoryParams, syncBlockStore, api) {
|
|
40
47
|
var _ref2 = options || {},
|
|
41
48
|
_ref2$useLongPressSel = _ref2.useLongPressSelection,
|
|
@@ -199,7 +206,7 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
|
|
|
199
206
|
if (!isOffline) {
|
|
200
207
|
if (bodiedSyncBlockRemoved.length > 0) {
|
|
201
208
|
confirmationTransactionRef.current = tr;
|
|
202
|
-
return handleBodiedSyncBlockRemoval(
|
|
209
|
+
return handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, fg('platform_synced_block_dogfooding') ? getDeleteReason(tr) : undefined);
|
|
203
210
|
}
|
|
204
211
|
if (bodiedSyncBlockAdded.length > 0) {
|
|
205
212
|
if (Boolean(tr.getMeta(pmHistoryPluginKey))) {
|
|
@@ -25,7 +25,7 @@ var onDismissed = function onDismissed(syncBlockStore) {
|
|
|
25
25
|
});
|
|
26
26
|
};
|
|
27
27
|
};
|
|
28
|
-
export var handleBodiedSyncBlockRemoval = function handleBodiedSyncBlockRemoval(
|
|
28
|
+
export var handleBodiedSyncBlockRemoval = function handleBodiedSyncBlockRemoval(bodiedSyncBlockRemoved, syncBlockStore, api, confirmationTransactionRef, deletionReason) {
|
|
29
29
|
// Clear potential old pending deletion to retreat the deletion as first attempt
|
|
30
30
|
syncBlockStore.sourceManager.clearPendingDeletion();
|
|
31
31
|
|
|
@@ -35,7 +35,7 @@ export var handleBodiedSyncBlockRemoval = function handleBodiedSyncBlockRemoval(
|
|
|
35
35
|
// proceed with deletion.
|
|
36
36
|
syncBlockStore.sourceManager.deleteSyncBlocksWithConfirmation(bodiedSyncBlockRemoved.map(function (node) {
|
|
37
37
|
return node.attrs;
|
|
38
|
-
}), function () {
|
|
38
|
+
}), deletionReason, function () {
|
|
39
39
|
var _api$core2;
|
|
40
40
|
var confirmationTransaction = confirmationTransactionRef.current;
|
|
41
41
|
if (!confirmationTransaction) {
|