@atlaskit/editor-synced-block-provider 2.2.0 → 2.2.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 +15 -0
- package/dist/cjs/common/types.js +7 -24
- package/dist/cjs/hooks/useFetchSyncBlockData.js +49 -0
- package/dist/cjs/hooks/useHandleContentChanges.js +13 -0
- package/dist/cjs/index.js +11 -9
- package/dist/cjs/providers/{confluenceContentAPI.js → confluence/confluenceContentAPI.js} +19 -16
- package/dist/cjs/providers/{inMemory.js → in-memory/inMemory.js} +4 -1
- package/dist/cjs/{common → providers}/syncBlockProvider.js +1 -1
- package/dist/cjs/providers/types.js +23 -0
- package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +123 -0
- package/dist/cjs/store-manager/sourceSyncBlockStoreManager.js +191 -0
- package/dist/cjs/store-manager/syncBlockStoreManager.js +111 -0
- package/dist/cjs/utils/utils.js +1 -17
- package/dist/es2019/common/types.js +6 -8
- package/dist/es2019/hooks/useFetchSyncBlockData.js +38 -0
- package/dist/es2019/hooks/useHandleContentChanges.js +7 -0
- package/dist/es2019/index.js +8 -6
- package/dist/es2019/providers/{confluenceContentAPI.js → confluence/confluenceContentAPI.js} +19 -16
- package/dist/es2019/providers/{inMemory.js → in-memory/inMemory.js} +4 -1
- package/dist/es2019/{common → providers}/syncBlockProvider.js +1 -1
- package/dist/es2019/providers/types.js +2 -0
- package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +83 -0
- package/dist/es2019/store-manager/sourceSyncBlockStoreManager.js +115 -0
- package/dist/es2019/store-manager/syncBlockStoreManager.js +79 -0
- package/dist/es2019/utils/utils.js +0 -13
- package/dist/esm/common/types.js +6 -22
- package/dist/esm/hooks/useFetchSyncBlockData.js +42 -0
- package/dist/esm/hooks/useHandleContentChanges.js +7 -0
- package/dist/esm/index.js +8 -6
- package/dist/esm/providers/{confluenceContentAPI.js → confluence/confluenceContentAPI.js} +19 -16
- package/dist/esm/providers/{inMemory.js → in-memory/inMemory.js} +4 -1
- package/dist/esm/{common → providers}/syncBlockProvider.js +1 -1
- package/dist/esm/providers/types.js +16 -0
- package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +116 -0
- package/dist/esm/store-manager/sourceSyncBlockStoreManager.js +184 -0
- package/dist/esm/store-manager/syncBlockStoreManager.js +105 -0
- package/dist/esm/utils/utils.js +0 -16
- package/dist/types/common/schema.d.ts +1 -1
- package/dist/types/common/types.d.ts +13 -26
- package/dist/types/hooks/useFetchSyncBlockData.d.ts +4 -0
- package/dist/types/hooks/useHandleContentChanges.d.ts +3 -0
- package/dist/types/index.d.ts +10 -7
- package/dist/{types-ts4.5/providers → types/providers/confluence}/confluenceContentAPI.d.ts +2 -1
- package/dist/types/providers/{inMemory.d.ts → in-memory/inMemory.d.ts} +1 -1
- package/dist/types/{common → providers}/syncBlockProvider.d.ts +2 -1
- package/dist/types/providers/types.d.ts +18 -0
- package/dist/types/store-manager/referenceSyncBlockStoreManager.d.ts +23 -0
- package/dist/types/store-manager/sourceSyncBlockStoreManager.d.ts +31 -0
- package/dist/types/{common → store-manager}/syncBlockStoreManager.d.ts +7 -25
- package/dist/types/utils/utils.d.ts +1 -2
- package/dist/types-ts4.5/common/schema.d.ts +1 -1
- package/dist/types-ts4.5/common/types.d.ts +13 -26
- package/dist/types-ts4.5/hooks/useFetchSyncBlockData.d.ts +4 -0
- package/dist/types-ts4.5/hooks/useHandleContentChanges.d.ts +3 -0
- package/dist/types-ts4.5/index.d.ts +10 -7
- package/dist/{types/providers → types-ts4.5/providers/confluence}/confluenceContentAPI.d.ts +2 -1
- package/dist/types-ts4.5/providers/{inMemory.d.ts → in-memory/inMemory.d.ts} +1 -1
- package/dist/types-ts4.5/{common → providers}/syncBlockProvider.d.ts +2 -1
- package/dist/types-ts4.5/providers/types.d.ts +18 -0
- package/dist/types-ts4.5/store-manager/referenceSyncBlockStoreManager.d.ts +23 -0
- package/dist/types-ts4.5/store-manager/sourceSyncBlockStoreManager.d.ts +31 -0
- package/dist/types-ts4.5/{common → store-manager}/syncBlockStoreManager.d.ts +7 -25
- package/dist/types-ts4.5/utils/utils.d.ts +1 -2
- package/package.json +2 -2
- package/dist/cjs/common/syncBlockStoreManager.js +0 -377
- package/dist/es2019/common/syncBlockStoreManager.js +0 -265
- package/dist/esm/common/syncBlockStoreManager.js +0 -370
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.SyncBlockStoreManager = void 0;
|
|
8
|
+
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
9
|
+
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
10
|
+
var _referenceSyncBlockStoreManager = require("./referenceSyncBlockStoreManager");
|
|
11
|
+
var _sourceSyncBlockStoreManager = require("./sourceSyncBlockStoreManager");
|
|
12
|
+
// A store manager responsible for the lifecycle and state management of sync blocks in an editor instance.
|
|
13
|
+
// Supports create, read, update, and delete operations for sync blocks.
|
|
14
|
+
// Designed to manage local in-memory state and synchronize with an external data provider.
|
|
15
|
+
// Handles caching, debouncing updates, and publish/subscribe for local changes.
|
|
16
|
+
// Ensures consistency between local and remote state, and can be used in both editor and renderer contexts.
|
|
17
|
+
var SyncBlockStoreManager = exports.SyncBlockStoreManager = /*#__PURE__*/function () {
|
|
18
|
+
function SyncBlockStoreManager(dataProvider) {
|
|
19
|
+
(0, _classCallCheck2.default)(this, SyncBlockStoreManager);
|
|
20
|
+
this.referenceSyncBlockStoreManager = new _referenceSyncBlockStoreManager.ReferenceSyncBlockStoreManager(dataProvider);
|
|
21
|
+
this.sourceSyncBlockStoreManager = new _sourceSyncBlockStoreManager.SourceSyncBlockStoreManager(dataProvider);
|
|
22
|
+
}
|
|
23
|
+
return (0, _createClass2.default)(SyncBlockStoreManager, [{
|
|
24
|
+
key: "fetchSyncBlockData",
|
|
25
|
+
value: function fetchSyncBlockData(syncBlockNode) {
|
|
26
|
+
if (this.isSourceBlock(syncBlockNode)) {
|
|
27
|
+
return Promise.reject(new Error('Invalid sync block node type provided for fetchSyncBlockData'));
|
|
28
|
+
}
|
|
29
|
+
return this.referenceSyncBlockStoreManager.fetchSyncBlockData(syncBlockNode);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Add/update a sync block node to/from the local cache
|
|
34
|
+
* @param syncBlockNode - The sync block node to update
|
|
35
|
+
*/
|
|
36
|
+
}, {
|
|
37
|
+
key: "updateSyncBlockData",
|
|
38
|
+
value: function updateSyncBlockData(syncBlockNode) {
|
|
39
|
+
if (this.isSourceBlock(syncBlockNode)) {
|
|
40
|
+
this.sourceSyncBlockStoreManager.updateSyncBlockData(syncBlockNode);
|
|
41
|
+
} else {
|
|
42
|
+
throw new Error('Invalid sync block node type provided for updateSyncBlockData');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Save content of bodiedSyncBlock nodes in local cache to backend
|
|
48
|
+
*
|
|
49
|
+
* @returns true if saving all nodes successfully, false if fail to save some/all nodes
|
|
50
|
+
*/
|
|
51
|
+
}, {
|
|
52
|
+
key: "flushBodiedSyncBlocks",
|
|
53
|
+
value: function flushBodiedSyncBlocks() {
|
|
54
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
55
|
+
return this.sourceSyncBlockStoreManager.flushBodiedSyncBlocks();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the URL for a sync block.
|
|
60
|
+
* @param localId - The local ID of the sync block to get the URL for
|
|
61
|
+
* @returns
|
|
62
|
+
*/
|
|
63
|
+
}, {
|
|
64
|
+
key: "getSyncBlockURL",
|
|
65
|
+
value: function getSyncBlockURL(localId) {
|
|
66
|
+
// only applicable to reference sync block, for now (will be refactored further)
|
|
67
|
+
return this.referenceSyncBlockStoreManager.getSyncBlockURL(localId);
|
|
68
|
+
}
|
|
69
|
+
}, {
|
|
70
|
+
key: "setEditorView",
|
|
71
|
+
value: function setEditorView(editorView) {
|
|
72
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
73
|
+
this.sourceSyncBlockStoreManager.setEditorView(editorView);
|
|
74
|
+
}
|
|
75
|
+
}, {
|
|
76
|
+
key: "isSourceBlock",
|
|
77
|
+
value: function isSourceBlock(node) {
|
|
78
|
+
return node.type.name === 'bodiedSyncBlock';
|
|
79
|
+
}
|
|
80
|
+
}, {
|
|
81
|
+
key: "registerConfirmationCallback",
|
|
82
|
+
value: function registerConfirmationCallback(callback) {
|
|
83
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
84
|
+
return this.sourceSyncBlockStoreManager.registerConfirmationCallback(callback);
|
|
85
|
+
}
|
|
86
|
+
}, {
|
|
87
|
+
key: "requireConfirmationBeforeDelete",
|
|
88
|
+
value: function requireConfirmationBeforeDelete() {
|
|
89
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
90
|
+
return this.sourceSyncBlockStoreManager.requireConfirmationBeforeDelete();
|
|
91
|
+
}
|
|
92
|
+
}, {
|
|
93
|
+
key: "createSyncBlockNode",
|
|
94
|
+
value: function createSyncBlockNode() {
|
|
95
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
96
|
+
return this.sourceSyncBlockStoreManager.createSyncBlockNode();
|
|
97
|
+
}
|
|
98
|
+
}, {
|
|
99
|
+
key: "deleteSyncBlocksWithConfirmation",
|
|
100
|
+
value: function deleteSyncBlocksWithConfirmation(tr, syncBlockIds) {
|
|
101
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
102
|
+
return this.sourceSyncBlockStoreManager.deleteSyncBlocksWithConfirmation(tr, syncBlockIds);
|
|
103
|
+
}
|
|
104
|
+
}, {
|
|
105
|
+
key: "rebaseTransaction",
|
|
106
|
+
value: function rebaseTransaction(incomingTr, state) {
|
|
107
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
108
|
+
this.sourceSyncBlockStoreManager.rebaseTransaction(incomingTr, state);
|
|
109
|
+
}
|
|
110
|
+
}]);
|
|
111
|
+
}();
|
package/dist/cjs/utils/utils.js
CHANGED
|
@@ -3,23 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.isBlogPageType = exports.
|
|
7
|
-
var _editorJsonTransformer = require("@atlaskit/editor-json-transformer");
|
|
8
|
-
var convertSyncBlockPMNodeToSyncBlockNode = exports.convertSyncBlockPMNodeToSyncBlockNode = function convertSyncBlockPMNodeToSyncBlockNode(node) {
|
|
9
|
-
var includeContent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
10
|
-
var transformer = new _editorJsonTransformer.JSONTransformer();
|
|
11
|
-
var toJSON = function toJSON(node) {
|
|
12
|
-
return transformer.encodeNode(node);
|
|
13
|
-
};
|
|
14
|
-
return {
|
|
15
|
-
type: node.type.name === 'bodiedSyncBlock' ? 'bodiedSyncBlock' : 'syncBlock',
|
|
16
|
-
attrs: {
|
|
17
|
-
localId: node.attrs.localId,
|
|
18
|
-
resourceId: node.attrs.resourceId
|
|
19
|
-
},
|
|
20
|
-
content: includeContent ? node.content.content.map(toJSON) : undefined
|
|
21
|
-
};
|
|
22
|
-
};
|
|
6
|
+
exports.isBlogPageType = exports.convertSyncBlockPMNodeToSyncBlockData = void 0;
|
|
23
7
|
var convertSyncBlockPMNodeToSyncBlockData = exports.convertSyncBlockPMNodeToSyncBlockData = function convertSyncBlockPMNodeToSyncBlockData(node) {
|
|
24
8
|
return {
|
|
25
9
|
blockInstanceId: node.attrs.localId,
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}({});
|
|
8
|
-
export class SyncBlockDataProvider extends NodeDataProvider {}
|
|
1
|
+
export let SyncBlockError = /*#__PURE__*/function (SyncBlockError) {
|
|
2
|
+
SyncBlockError["Errored"] = "errored";
|
|
3
|
+
SyncBlockError["NotFound"] = "not_found";
|
|
4
|
+
SyncBlockError["Forbidden"] = "forbidden";
|
|
5
|
+
return SyncBlockError;
|
|
6
|
+
}({});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { SyncBlockError } from '../common/types';
|
|
3
|
+
export const useFetchSyncBlockData = (manager, syncBlockNode) => {
|
|
4
|
+
const [fetchSyncBlockDataResult, setFetchSyncBlockDataResult] = useState(null);
|
|
5
|
+
const fetchSyncBlockNode = useCallback(() => {
|
|
6
|
+
manager.fetchSyncBlockData(syncBlockNode).then(data => {
|
|
7
|
+
if (data !== null && data !== void 0 && data.error) {
|
|
8
|
+
// if there is an error, we don't want to replace real existing data with the error data
|
|
9
|
+
setFetchSyncBlockDataResult(prev => {
|
|
10
|
+
if (!prev || prev.error) {
|
|
11
|
+
return data;
|
|
12
|
+
}
|
|
13
|
+
return prev;
|
|
14
|
+
});
|
|
15
|
+
} else {
|
|
16
|
+
setFetchSyncBlockDataResult(data !== null && data !== void 0 ? data : null);
|
|
17
|
+
}
|
|
18
|
+
}).catch(() => {
|
|
19
|
+
//TODO: EDITOR-1921 - add error analytics
|
|
20
|
+
setFetchSyncBlockDataResult(prev => {
|
|
21
|
+
if (!prev || prev.error) {
|
|
22
|
+
return {
|
|
23
|
+
error: SyncBlockError.Errored
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return prev;
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}, [manager, syncBlockNode]);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
fetchSyncBlockNode();
|
|
32
|
+
const interval = window.setInterval(fetchSyncBlockNode, 3000);
|
|
33
|
+
return () => {
|
|
34
|
+
window.clearInterval(interval);
|
|
35
|
+
};
|
|
36
|
+
}, [fetchSyncBlockNode]);
|
|
37
|
+
return fetchSyncBlockDataResult;
|
|
38
|
+
};
|
package/dist/es2019/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/* eslint-disable @atlaskit/editor/no-re-export */
|
|
2
2
|
|
|
3
|
-
export { SyncBlockProvider as SyncedBlockProvider, useMemoizedSyncedBlockProvider } from './
|
|
4
|
-
export { SyncBlockStoreManager
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
3
|
+
export { SyncBlockProvider as SyncedBlockProvider, useMemoizedSyncedBlockProvider } from './providers/syncBlockProvider';
|
|
4
|
+
export { SyncBlockStoreManager } from './store-manager/syncBlockStoreManager';
|
|
5
|
+
export { useFetchSyncBlockData } from './hooks/useFetchSyncBlockData';
|
|
6
|
+
export { useHandleContentChanges } from './hooks/useHandleContentChanges';
|
|
7
|
+
export { SyncBlockError } from './common/types';
|
|
8
|
+
export { inMemoryFetchProvider, inMemoryWriteProvider } from './providers/in-memory/inMemory';
|
|
7
9
|
export { getDefaultSyncBlockSchema } from './common/schema';
|
|
8
|
-
export { createContentAPIProvidersWithDefaultKey, useMemoizedContentAPIProviders } from './providers/confluenceContentAPI';
|
|
10
|
+
export { createContentAPIProvidersWithDefaultKey, useMemoizedContentAPIProviders } from './providers/confluence/confluenceContentAPI';
|
|
9
11
|
export { getConfluencePageAri } from './utils/ari';
|
|
10
|
-
export {
|
|
12
|
+
export { convertSyncBlockPMNodeToSyncBlockData } from './utils/utils';
|
|
11
13
|
export { rebaseTransaction } from './common/rebase-transaction';
|
package/dist/es2019/providers/{confluenceContentAPI.js → confluence/confluenceContentAPI.js}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import { getLocalIdFromAri, getPageIdAndTypeFromAri } from '
|
|
5
|
-
import { getContentProperty, createContentProperty, updateContentProperty } from '
|
|
6
|
-
import { isBlogPageType } from '
|
|
3
|
+
import { SyncBlockError } from '../../common/types';
|
|
4
|
+
import { getLocalIdFromAri, getPageIdAndTypeFromAri } from '../../utils/ari';
|
|
5
|
+
import { getContentProperty, createContentProperty, updateContentProperty } from '../../utils/contentProperty';
|
|
6
|
+
import { isBlogPageType } from '../../utils/utils';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Configuration for Content API providers
|
|
@@ -27,16 +27,16 @@ const parseSyncedBlockContentPropertyValue = value => {
|
|
|
27
27
|
throw new Error(`Failed to parse synced block data: ${error}`);
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
|
-
const
|
|
30
|
+
const getResponseError = contentProperty => {
|
|
31
31
|
var _content$properties;
|
|
32
32
|
const content = 'blogPost' in contentProperty.data.confluence ? contentProperty.data.confluence.blogPost : contentProperty.data.confluence.page;
|
|
33
33
|
if (!content) {
|
|
34
|
-
return
|
|
34
|
+
return SyncBlockError.Forbidden;
|
|
35
35
|
}
|
|
36
36
|
if (!((_content$properties = content.properties) !== null && _content$properties !== void 0 && _content$properties[0])) {
|
|
37
|
-
return
|
|
37
|
+
return SyncBlockError.NotFound;
|
|
38
38
|
}
|
|
39
|
-
return
|
|
39
|
+
return SyncBlockError.Errored;
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -60,22 +60,22 @@ class ConfluenceADFFetchProvider {
|
|
|
60
60
|
cloudId: this.config.cloudId,
|
|
61
61
|
pageType
|
|
62
62
|
};
|
|
63
|
-
let
|
|
63
|
+
let error;
|
|
64
64
|
let value;
|
|
65
65
|
if (isBlogPageType(pageType)) {
|
|
66
66
|
var _contentProperty$data, _contentProperty$data2, _contentProperty$data3;
|
|
67
67
|
const contentProperty = await getContentProperty(options);
|
|
68
68
|
value = (_contentProperty$data = contentProperty.data.confluence.blogPost) === null || _contentProperty$data === void 0 ? void 0 : (_contentProperty$data2 = _contentProperty$data.properties) === null || _contentProperty$data2 === void 0 ? void 0 : (_contentProperty$data3 = _contentProperty$data2[0]) === null || _contentProperty$data3 === void 0 ? void 0 : _contentProperty$data3.value;
|
|
69
|
-
|
|
69
|
+
error = getResponseError(contentProperty);
|
|
70
70
|
} else {
|
|
71
71
|
var _contentProperty$data4, _contentProperty$data5, _contentProperty$data6;
|
|
72
72
|
const contentProperty = await getContentProperty(options);
|
|
73
73
|
value = (_contentProperty$data4 = contentProperty.data.confluence.page) === null || _contentProperty$data4 === void 0 ? void 0 : (_contentProperty$data5 = _contentProperty$data4.properties) === null || _contentProperty$data5 === void 0 ? void 0 : (_contentProperty$data6 = _contentProperty$data5[0]) === null || _contentProperty$data6 === void 0 ? void 0 : _contentProperty$data6.value;
|
|
74
|
-
|
|
74
|
+
error = getResponseError(contentProperty);
|
|
75
75
|
}
|
|
76
76
|
if (!value) {
|
|
77
77
|
return {
|
|
78
|
-
|
|
78
|
+
error,
|
|
79
79
|
resourceId
|
|
80
80
|
};
|
|
81
81
|
}
|
|
@@ -83,13 +83,16 @@ class ConfluenceADFFetchProvider {
|
|
|
83
83
|
// Parse the synced block content from the property value
|
|
84
84
|
const syncedBlockData = parseSyncedBlockContentPropertyValue(value);
|
|
85
85
|
return {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
data: {
|
|
87
|
+
content: syncedBlockData.content,
|
|
88
|
+
resourceId,
|
|
89
|
+
blockInstanceId: localId
|
|
90
|
+
},
|
|
91
|
+
resourceId
|
|
89
92
|
};
|
|
90
93
|
} catch {
|
|
91
94
|
return {
|
|
92
|
-
|
|
95
|
+
error: SyncBlockError.Errored,
|
|
93
96
|
resourceId
|
|
94
97
|
};
|
|
95
98
|
}
|
|
@@ -5,7 +5,10 @@ export const inMemoryFetchProvider = {
|
|
|
5
5
|
if (!data) {
|
|
6
6
|
throw new Error('Sync Block Provider (in-mem): Data not found');
|
|
7
7
|
}
|
|
8
|
-
return Promise.resolve(
|
|
8
|
+
return Promise.resolve({
|
|
9
|
+
data,
|
|
10
|
+
resourceId
|
|
11
|
+
});
|
|
9
12
|
}
|
|
10
13
|
};
|
|
11
14
|
export const inMemoryWriteProvider = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
|
+
import { SyncBlockDataProvider } from '../providers/types';
|
|
3
4
|
import { getLocalIdFromAri, getPageARIFromResourceId } from '../utils/ari';
|
|
4
|
-
import { SyncBlockDataProvider } from './types';
|
|
5
5
|
export class SyncBlockProvider extends SyncBlockDataProvider {
|
|
6
6
|
constructor(fetchProvider, writeProvider, sourceId) {
|
|
7
7
|
super();
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const createSyncBlockNode = (localId, resourceId) => {
|
|
2
|
+
return {
|
|
3
|
+
type: 'syncBlock',
|
|
4
|
+
attrs: {
|
|
5
|
+
localId,
|
|
6
|
+
resourceId
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export class ReferenceSyncBlockStoreManager {
|
|
11
|
+
constructor(dataProvider) {
|
|
12
|
+
this.dataProvider = dataProvider;
|
|
13
|
+
this.syncBlockCache = new Map();
|
|
14
|
+
this.syncBlockURLRequests = new Map();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
*
|
|
19
|
+
* @param localId - The local ID of the sync block to get the source URL for
|
|
20
|
+
* @param resourceId - The resource ID of the sync block to get the source URL for
|
|
21
|
+
* Fetches source URl for a sync block and updates sync block data with the source URL asynchronously.
|
|
22
|
+
*/
|
|
23
|
+
fetchSyncBlockSourceURL({
|
|
24
|
+
localId,
|
|
25
|
+
resourceId
|
|
26
|
+
}) {
|
|
27
|
+
if (!localId || !resourceId || !this.dataProvider) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// if the sync block is a reference block, we need to fetch the URL to the source
|
|
32
|
+
// we could optimise this further by checking if the sync block is on the same page as the source
|
|
33
|
+
if (!this.syncBlockURLRequests.get(localId)) {
|
|
34
|
+
this.syncBlockURLRequests.set(localId, true);
|
|
35
|
+
this.dataProvider.retrieveSyncBlockSourceUrl(createSyncBlockNode(localId, resourceId)).then(sourceURL => {
|
|
36
|
+
const existingSyncBlock = this.syncBlockCache.get(localId);
|
|
37
|
+
if (existingSyncBlock) {
|
|
38
|
+
existingSyncBlock.sourceURL = sourceURL;
|
|
39
|
+
}
|
|
40
|
+
}).finally(() => {
|
|
41
|
+
this.syncBlockURLRequests.set(localId, false);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async fetchSyncBlockData(syncBlockNode) {
|
|
46
|
+
if (!this.dataProvider) {
|
|
47
|
+
throw new Error('Data provider not set');
|
|
48
|
+
}
|
|
49
|
+
const syncNode = createSyncBlockNode(syncBlockNode.attrs.localId, syncBlockNode.attrs.resourceId);
|
|
50
|
+
|
|
51
|
+
// async fetch source URL if it is not already fetched
|
|
52
|
+
const existingSyncBlock = this.syncBlockCache.get(syncBlockNode.attrs.localId);
|
|
53
|
+
if (!(existingSyncBlock !== null && existingSyncBlock !== void 0 && existingSyncBlock.sourceURL)) {
|
|
54
|
+
this.fetchSyncBlockSourceURL({
|
|
55
|
+
localId: syncBlockNode.attrs.localId,
|
|
56
|
+
resourceId: syncBlockNode.attrs.resourceId
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const data = await this.dataProvider.fetchNodesData([syncNode]);
|
|
60
|
+
if (!data) {
|
|
61
|
+
throw new Error('Failed to fetch sync block node data');
|
|
62
|
+
}
|
|
63
|
+
const fetchSyncBlockDataResult = data[0];
|
|
64
|
+
if (!fetchSyncBlockDataResult.error && fetchSyncBlockDataResult.data) {
|
|
65
|
+
// only adds it to the map if it did not error out
|
|
66
|
+
this.syncBlockCache.set(syncBlockNode.attrs.localId, {
|
|
67
|
+
...existingSyncBlock,
|
|
68
|
+
...fetchSyncBlockDataResult.data
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return fetchSyncBlockDataResult;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get the URL for a sync block.
|
|
76
|
+
* @param localId - The local ID of the sync block to get the URL for
|
|
77
|
+
* @returns
|
|
78
|
+
*/
|
|
79
|
+
getSyncBlockURL(localId) {
|
|
80
|
+
const syncBlock = this.syncBlockCache.get(localId);
|
|
81
|
+
return syncBlock === null || syncBlock === void 0 ? void 0 : syncBlock.sourceURL;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import uuid from 'uuid';
|
|
2
|
+
import { rebaseTransaction } from '../common/rebase-transaction';
|
|
3
|
+
import { resourceIdFromSourceAndLocalId } from '../utils/ari';
|
|
4
|
+
import { convertSyncBlockPMNodeToSyncBlockData } from '../utils/utils';
|
|
5
|
+
export class SourceSyncBlockStoreManager {
|
|
6
|
+
constructor(dataProvider) {
|
|
7
|
+
this.dataProvider = dataProvider;
|
|
8
|
+
this.syncBlockCache = new Map();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Add/update a sync block node to/from the local cache
|
|
13
|
+
* @param syncBlockNode - The sync block node to update
|
|
14
|
+
*/
|
|
15
|
+
updateSyncBlockData(syncBlockNode) {
|
|
16
|
+
try {
|
|
17
|
+
const {
|
|
18
|
+
localId,
|
|
19
|
+
resourceId
|
|
20
|
+
} = syncBlockNode.attrs;
|
|
21
|
+
if (!localId || !resourceId) {
|
|
22
|
+
throw new Error('Local ID or resource ID is not set');
|
|
23
|
+
}
|
|
24
|
+
const syncBlockData = convertSyncBlockPMNodeToSyncBlockData(syncBlockNode);
|
|
25
|
+
this.syncBlockCache.set(localId, syncBlockData);
|
|
26
|
+
} catch {
|
|
27
|
+
//TODO: EDITOR-1921 - add error analytics
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Save content of bodiedSyncBlock nodes in local cache to backend
|
|
33
|
+
*
|
|
34
|
+
* @returns true if saving all nodes successfully, false if fail to save some/all nodes
|
|
35
|
+
*/
|
|
36
|
+
async flushBodiedSyncBlocks() {
|
|
37
|
+
try {
|
|
38
|
+
if (!this.dataProvider) {
|
|
39
|
+
throw new Error('Data provider not set');
|
|
40
|
+
}
|
|
41
|
+
const bodiedSyncBlockNodes = [];
|
|
42
|
+
const bodiedSyncBlockData = [];
|
|
43
|
+
Array.from(this.syncBlockCache.values()).forEach(syncBlockData => {
|
|
44
|
+
bodiedSyncBlockNodes.push({
|
|
45
|
+
type: 'bodiedSyncBlock',
|
|
46
|
+
attrs: {
|
|
47
|
+
localId: syncBlockData.blockInstanceId,
|
|
48
|
+
resourceId: syncBlockData.resourceId
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
bodiedSyncBlockData.push(syncBlockData);
|
|
52
|
+
});
|
|
53
|
+
if (bodiedSyncBlockNodes.length === 0) {
|
|
54
|
+
return Promise.resolve(true);
|
|
55
|
+
}
|
|
56
|
+
const resourceIds = await this.dataProvider.writeNodesData(bodiedSyncBlockNodes, bodiedSyncBlockData);
|
|
57
|
+
return resourceIds.every(resourceId => resourceId !== undefined);
|
|
58
|
+
} catch {
|
|
59
|
+
//TODO: EDITOR-1921 - add error analytics
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
setEditorView(editorView) {
|
|
64
|
+
this.editorView = editorView;
|
|
65
|
+
}
|
|
66
|
+
registerConfirmationCallback(callback) {
|
|
67
|
+
this.confirmationCallback = callback;
|
|
68
|
+
return () => {
|
|
69
|
+
this.confirmationCallback = undefined;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
requireConfirmationBeforeDelete() {
|
|
73
|
+
return !!this.confirmationCallback;
|
|
74
|
+
}
|
|
75
|
+
createSyncBlockNode() {
|
|
76
|
+
var _this$dataProvider;
|
|
77
|
+
const blockInstanceId = uuid();
|
|
78
|
+
const sourceId = (_this$dataProvider = this.dataProvider) === null || _this$dataProvider === void 0 ? void 0 : _this$dataProvider.getSourceId();
|
|
79
|
+
if (!sourceId) {
|
|
80
|
+
throw new Error('Provider of sync block plugin is not set');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// This should be generated by the data provider implementation as it differs between data providers
|
|
84
|
+
const resourceId = resourceIdFromSourceAndLocalId(sourceId, blockInstanceId);
|
|
85
|
+
const syncBlockNode = {
|
|
86
|
+
attrs: {
|
|
87
|
+
resourceId,
|
|
88
|
+
localId: blockInstanceId
|
|
89
|
+
},
|
|
90
|
+
type: 'bodiedSyncBlock'
|
|
91
|
+
};
|
|
92
|
+
return syncBlockNode;
|
|
93
|
+
}
|
|
94
|
+
async deleteSyncBlocksWithConfirmation(tr, syncBlockIds) {
|
|
95
|
+
if (this.confirmationCallback) {
|
|
96
|
+
this.confirmationTransaction = tr;
|
|
97
|
+
const confirmed = await this.confirmationCallback();
|
|
98
|
+
if (confirmed) {
|
|
99
|
+
var _this$editorView;
|
|
100
|
+
(_this$editorView = this.editorView) === null || _this$editorView === void 0 ? void 0 : _this$editorView.dispatch(this.confirmationTransaction.setMeta('isConfirmedSyncBlockDeletion', true));
|
|
101
|
+
// Need to update the BE on deletion
|
|
102
|
+
syncBlockIds.forEach(({
|
|
103
|
+
localId
|
|
104
|
+
}) => this.syncBlockCache.delete(localId));
|
|
105
|
+
}
|
|
106
|
+
this.confirmationTransaction = undefined;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
rebaseTransaction(incomingTr, state) {
|
|
110
|
+
if (!this.confirmationTransaction) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
this.confirmationTransaction = rebaseTransaction(this.confirmationTransaction, incomingTr, state);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ReferenceSyncBlockStoreManager } from './referenceSyncBlockStoreManager';
|
|
2
|
+
import { SourceSyncBlockStoreManager } from './sourceSyncBlockStoreManager';
|
|
3
|
+
|
|
4
|
+
// A store manager responsible for the lifecycle and state management of sync blocks in an editor instance.
|
|
5
|
+
// Supports create, read, update, and delete operations for sync blocks.
|
|
6
|
+
// Designed to manage local in-memory state and synchronize with an external data provider.
|
|
7
|
+
// Handles caching, debouncing updates, and publish/subscribe for local changes.
|
|
8
|
+
// Ensures consistency between local and remote state, and can be used in both editor and renderer contexts.
|
|
9
|
+
export class SyncBlockStoreManager {
|
|
10
|
+
constructor(dataProvider) {
|
|
11
|
+
this.referenceSyncBlockStoreManager = new ReferenceSyncBlockStoreManager(dataProvider);
|
|
12
|
+
this.sourceSyncBlockStoreManager = new SourceSyncBlockStoreManager(dataProvider);
|
|
13
|
+
}
|
|
14
|
+
fetchSyncBlockData(syncBlockNode) {
|
|
15
|
+
if (this.isSourceBlock(syncBlockNode)) {
|
|
16
|
+
return Promise.reject(new Error('Invalid sync block node type provided for fetchSyncBlockData'));
|
|
17
|
+
}
|
|
18
|
+
return this.referenceSyncBlockStoreManager.fetchSyncBlockData(syncBlockNode);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Add/update a sync block node to/from the local cache
|
|
23
|
+
* @param syncBlockNode - The sync block node to update
|
|
24
|
+
*/
|
|
25
|
+
updateSyncBlockData(syncBlockNode) {
|
|
26
|
+
if (this.isSourceBlock(syncBlockNode)) {
|
|
27
|
+
this.sourceSyncBlockStoreManager.updateSyncBlockData(syncBlockNode);
|
|
28
|
+
} else {
|
|
29
|
+
throw new Error('Invalid sync block node type provided for updateSyncBlockData');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Save content of bodiedSyncBlock nodes in local cache to backend
|
|
35
|
+
*
|
|
36
|
+
* @returns true if saving all nodes successfully, false if fail to save some/all nodes
|
|
37
|
+
*/
|
|
38
|
+
flushBodiedSyncBlocks() {
|
|
39
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
40
|
+
return this.sourceSyncBlockStoreManager.flushBodiedSyncBlocks();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get the URL for a sync block.
|
|
45
|
+
* @param localId - The local ID of the sync block to get the URL for
|
|
46
|
+
* @returns
|
|
47
|
+
*/
|
|
48
|
+
getSyncBlockURL(localId) {
|
|
49
|
+
// only applicable to reference sync block, for now (will be refactored further)
|
|
50
|
+
return this.referenceSyncBlockStoreManager.getSyncBlockURL(localId);
|
|
51
|
+
}
|
|
52
|
+
setEditorView(editorView) {
|
|
53
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
54
|
+
this.sourceSyncBlockStoreManager.setEditorView(editorView);
|
|
55
|
+
}
|
|
56
|
+
isSourceBlock(node) {
|
|
57
|
+
return node.type.name === 'bodiedSyncBlock';
|
|
58
|
+
}
|
|
59
|
+
registerConfirmationCallback(callback) {
|
|
60
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
61
|
+
return this.sourceSyncBlockStoreManager.registerConfirmationCallback(callback);
|
|
62
|
+
}
|
|
63
|
+
requireConfirmationBeforeDelete() {
|
|
64
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
65
|
+
return this.sourceSyncBlockStoreManager.requireConfirmationBeforeDelete();
|
|
66
|
+
}
|
|
67
|
+
createSyncBlockNode() {
|
|
68
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
69
|
+
return this.sourceSyncBlockStoreManager.createSyncBlockNode();
|
|
70
|
+
}
|
|
71
|
+
deleteSyncBlocksWithConfirmation(tr, syncBlockIds) {
|
|
72
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
73
|
+
return this.sourceSyncBlockStoreManager.deleteSyncBlocksWithConfirmation(tr, syncBlockIds);
|
|
74
|
+
}
|
|
75
|
+
rebaseTransaction(incomingTr, state) {
|
|
76
|
+
// only applicable to source sync block, for now (will be refactored further)
|
|
77
|
+
this.sourceSyncBlockStoreManager.rebaseTransaction(incomingTr, state);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
import { JSONTransformer } from '@atlaskit/editor-json-transformer';
|
|
2
|
-
export const convertSyncBlockPMNodeToSyncBlockNode = (node, includeContent = false) => {
|
|
3
|
-
const transformer = new JSONTransformer();
|
|
4
|
-
const toJSON = node => transformer.encodeNode(node);
|
|
5
|
-
return {
|
|
6
|
-
type: node.type.name === 'bodiedSyncBlock' ? 'bodiedSyncBlock' : 'syncBlock',
|
|
7
|
-
attrs: {
|
|
8
|
-
localId: node.attrs.localId,
|
|
9
|
-
resourceId: node.attrs.resourceId
|
|
10
|
-
},
|
|
11
|
-
content: includeContent ? node.content.content.map(toJSON) : undefined
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
1
|
export const convertSyncBlockPMNodeToSyncBlockData = node => {
|
|
15
2
|
return {
|
|
16
3
|
blockInstanceId: node.attrs.localId,
|
package/dist/esm/common/types.js
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
|
8
|
-
import { NodeDataProvider } from '@atlaskit/node-data-provider';
|
|
9
|
-
export var SyncBlockStatus = /*#__PURE__*/function (SyncBlockStatus) {
|
|
10
|
-
SyncBlockStatus["Errored"] = "errored";
|
|
11
|
-
SyncBlockStatus["NotFound"] = "not_found";
|
|
12
|
-
SyncBlockStatus["Unauthorized"] = "unauthorized";
|
|
13
|
-
return SyncBlockStatus;
|
|
14
|
-
}({});
|
|
15
|
-
export var SyncBlockDataProvider = /*#__PURE__*/function (_NodeDataProvider) {
|
|
16
|
-
function SyncBlockDataProvider() {
|
|
17
|
-
_classCallCheck(this, SyncBlockDataProvider);
|
|
18
|
-
return _callSuper(this, SyncBlockDataProvider, arguments);
|
|
19
|
-
}
|
|
20
|
-
_inherits(SyncBlockDataProvider, _NodeDataProvider);
|
|
21
|
-
return _createClass(SyncBlockDataProvider);
|
|
22
|
-
}(NodeDataProvider);
|
|
1
|
+
export var SyncBlockError = /*#__PURE__*/function (SyncBlockError) {
|
|
2
|
+
SyncBlockError["Errored"] = "errored";
|
|
3
|
+
SyncBlockError["NotFound"] = "not_found";
|
|
4
|
+
SyncBlockError["Forbidden"] = "forbidden";
|
|
5
|
+
return SyncBlockError;
|
|
6
|
+
}({});
|