@atlaskit/editor-synced-block-provider 0.2.0 → 0.4.0
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 +14 -0
- package/dist/cjs/common/rebase-transaction.js +31 -0
- package/dist/cjs/common/syncBlockProvider.js +24 -12
- package/dist/cjs/common/syncBlockStoreManager.js +24 -11
- package/dist/cjs/index.js +34 -1
- package/dist/cjs/providers/confluenceContentAPI.js +238 -0
- package/dist/cjs/utils/ari.js +42 -0
- package/dist/cjs/utils/contentProperty.js +192 -0
- package/dist/es2019/common/rebase-transaction.js +26 -0
- package/dist/es2019/common/syncBlockProvider.js +25 -13
- package/dist/es2019/common/syncBlockStoreManager.js +18 -7
- package/dist/es2019/index.js +5 -2
- package/dist/es2019/providers/confluenceContentAPI.js +150 -0
- package/dist/es2019/utils/ari.js +32 -0
- package/dist/es2019/utils/contentProperty.js +160 -0
- package/dist/esm/common/rebase-transaction.js +26 -0
- package/dist/esm/common/syncBlockProvider.js +24 -12
- package/dist/esm/common/syncBlockStoreManager.js +24 -11
- package/dist/esm/index.js +5 -2
- package/dist/esm/providers/confluenceContentAPI.js +232 -0
- package/dist/esm/utils/ari.js +36 -0
- package/dist/esm/utils/contentProperty.js +185 -0
- package/dist/types/common/rebase-transaction.d.ts +11 -0
- package/dist/types/common/syncBlockProvider.d.ts +3 -1
- package/dist/types/common/syncBlockStoreManager.d.ts +5 -2
- package/dist/types/common/types.d.ts +1 -0
- package/dist/types/index.d.ts +5 -2
- package/dist/types/providers/confluenceContentAPI.d.ts +41 -0
- package/dist/types/utils/ari.d.ts +10 -0
- package/dist/types/utils/contentProperty.d.ts +61 -0
- package/dist/types-ts4.5/common/rebase-transaction.d.ts +11 -0
- package/dist/types-ts4.5/common/syncBlockProvider.d.ts +3 -1
- package/dist/types-ts4.5/common/syncBlockStoreManager.d.ts +5 -2
- package/dist/types-ts4.5/common/types.d.ts +1 -0
- package/dist/types-ts4.5/index.d.ts +5 -2
- package/dist/types-ts4.5/providers/confluenceContentAPI.d.ts +41 -0
- package/dist/types-ts4.5/utils/ari.d.ts +10 -0
- package/dist/types-ts4.5/utils/contentProperty.d.ts +61 -0
- package/package.json +2 -2
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.updateContentProperty = exports.getContentProperty = exports.createContentProperty = void 0;
|
|
8
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
9
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
10
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
11
|
+
var _ari = require("./ari");
|
|
12
|
+
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; }
|
|
13
|
+
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) { (0, _defineProperty2.default)(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; }
|
|
14
|
+
var COMMON_HEADERS = {
|
|
15
|
+
'Content-Type': 'application/json',
|
|
16
|
+
Accept: 'application/json'
|
|
17
|
+
};
|
|
18
|
+
var AGG_HEADERS = {
|
|
19
|
+
'X-ExperimentalApi': 'confluence-agg-beta'
|
|
20
|
+
};
|
|
21
|
+
var GRAPHQL_ENDPOINT = '/gateway/api/graphql';
|
|
22
|
+
var GET_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_GET';
|
|
23
|
+
var CREATE_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_CREATE';
|
|
24
|
+
var UPDATE_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_UPDATE';
|
|
25
|
+
/**
|
|
26
|
+
* Query to get the page property by key
|
|
27
|
+
* @param documentARI
|
|
28
|
+
* @param key
|
|
29
|
+
* @returns
|
|
30
|
+
*/
|
|
31
|
+
var GET_QUERY = "query ".concat(GET_OPERATION_NAME, " ($id: ID!, $keys: [String]!) {\n\t\t\t\t\tconfluence {\n\t\t\t\t\t\tpage (id: $id) {\n\t\t\t\t\t\t\tproperties(keys: $keys) {\n\t\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\t\tvalue\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}");
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Query to create a page property with key and value
|
|
35
|
+
* @param documentARI
|
|
36
|
+
* @param key
|
|
37
|
+
* @param value
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
var CREATE_QUERY = "mutation ".concat(CREATE_OPERATION_NAME, " ($input: ConfluenceCreatePagePropertyInput!){\n\t\t\t\t\t\tconfluence {\n\t\t\t\t\t\t\tcreatePageProperty(input: $input) {\n\t\t\t\t\t\t\t\tpageProperty {\n\t\t\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\t\t\tvalue\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}");
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Query to update a page property with key and value without bumping the version
|
|
44
|
+
* @param documentARI
|
|
45
|
+
* @param key
|
|
46
|
+
* @param value
|
|
47
|
+
* @returns
|
|
48
|
+
*/
|
|
49
|
+
var UPDATE_QUERY = "mutation ".concat(UPDATE_OPERATION_NAME, " ($input: ConfluenceUpdateValuePagePropertyInput!) {\n\t\t\t\t\t\tconfluence {\n\t\t\t\t\t\t\tupdateValuePageProperty(input: $input) {\n\t\t\t\t\t\t\t\tpageProperty {\n\t\t\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\t\t\tvalue\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}");
|
|
50
|
+
var getContentProperty = exports.getContentProperty = /*#__PURE__*/function () {
|
|
51
|
+
var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref) {
|
|
52
|
+
var pageId, key, cloudId, documentARI, bodyData, response, contentProperty;
|
|
53
|
+
return _regenerator.default.wrap(function _callee$(_context) {
|
|
54
|
+
while (1) switch (_context.prev = _context.next) {
|
|
55
|
+
case 0:
|
|
56
|
+
pageId = _ref.pageId, key = _ref.key, cloudId = _ref.cloudId;
|
|
57
|
+
documentARI = (0, _ari.getConfluencePageAri)(pageId, cloudId);
|
|
58
|
+
bodyData = {
|
|
59
|
+
query: GET_QUERY,
|
|
60
|
+
operationName: GET_OPERATION_NAME,
|
|
61
|
+
variables: {
|
|
62
|
+
id: documentARI,
|
|
63
|
+
keys: [key]
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
_context.next = 5;
|
|
67
|
+
return fetch(GRAPHQL_ENDPOINT, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
|
|
70
|
+
body: JSON.stringify(bodyData)
|
|
71
|
+
});
|
|
72
|
+
case 5:
|
|
73
|
+
response = _context.sent;
|
|
74
|
+
if (response.ok) {
|
|
75
|
+
_context.next = 8;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
throw new Error("Failed to get content property: ".concat(response.statusText));
|
|
79
|
+
case 8:
|
|
80
|
+
_context.next = 10;
|
|
81
|
+
return response.json();
|
|
82
|
+
case 10:
|
|
83
|
+
contentProperty = _context.sent;
|
|
84
|
+
return _context.abrupt("return", contentProperty);
|
|
85
|
+
case 12:
|
|
86
|
+
case "end":
|
|
87
|
+
return _context.stop();
|
|
88
|
+
}
|
|
89
|
+
}, _callee);
|
|
90
|
+
}));
|
|
91
|
+
return function getContentProperty(_x) {
|
|
92
|
+
return _ref2.apply(this, arguments);
|
|
93
|
+
};
|
|
94
|
+
}();
|
|
95
|
+
var updateContentProperty = exports.updateContentProperty = /*#__PURE__*/function () {
|
|
96
|
+
var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(_ref3) {
|
|
97
|
+
var pageId, key, value, cloudId, documentARI, bodyData, response, contentProperty;
|
|
98
|
+
return _regenerator.default.wrap(function _callee2$(_context2) {
|
|
99
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
100
|
+
case 0:
|
|
101
|
+
pageId = _ref3.pageId, key = _ref3.key, value = _ref3.value, cloudId = _ref3.cloudId;
|
|
102
|
+
documentARI = (0, _ari.getConfluencePageAri)(pageId, cloudId);
|
|
103
|
+
bodyData = {
|
|
104
|
+
query: UPDATE_QUERY,
|
|
105
|
+
operationName: UPDATE_OPERATION_NAME,
|
|
106
|
+
variables: {
|
|
107
|
+
input: {
|
|
108
|
+
pageId: documentARI,
|
|
109
|
+
key: key,
|
|
110
|
+
value: value,
|
|
111
|
+
useSameVersion: true
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
_context2.next = 5;
|
|
116
|
+
return fetch(GRAPHQL_ENDPOINT, {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
|
|
119
|
+
body: JSON.stringify(bodyData)
|
|
120
|
+
});
|
|
121
|
+
case 5:
|
|
122
|
+
response = _context2.sent;
|
|
123
|
+
if (response.ok) {
|
|
124
|
+
_context2.next = 8;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
throw new Error("Failed to update content property: ".concat(response.statusText));
|
|
128
|
+
case 8:
|
|
129
|
+
_context2.next = 10;
|
|
130
|
+
return response.json();
|
|
131
|
+
case 10:
|
|
132
|
+
contentProperty = _context2.sent;
|
|
133
|
+
return _context2.abrupt("return", contentProperty);
|
|
134
|
+
case 12:
|
|
135
|
+
case "end":
|
|
136
|
+
return _context2.stop();
|
|
137
|
+
}
|
|
138
|
+
}, _callee2);
|
|
139
|
+
}));
|
|
140
|
+
return function updateContentProperty(_x2) {
|
|
141
|
+
return _ref4.apply(this, arguments);
|
|
142
|
+
};
|
|
143
|
+
}();
|
|
144
|
+
var createContentProperty = exports.createContentProperty = /*#__PURE__*/function () {
|
|
145
|
+
var _ref6 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(_ref5) {
|
|
146
|
+
var pageId, key, value, cloudId, documentARI, escapedValue, bodyData, response, contentProperty;
|
|
147
|
+
return _regenerator.default.wrap(function _callee3$(_context3) {
|
|
148
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
149
|
+
case 0:
|
|
150
|
+
pageId = _ref5.pageId, key = _ref5.key, value = _ref5.value, cloudId = _ref5.cloudId;
|
|
151
|
+
documentARI = (0, _ari.getConfluencePageAri)(pageId, cloudId); // eslint-disable-next-line require-unicode-regexp
|
|
152
|
+
escapedValue = value.replace(/"/g, '\\"');
|
|
153
|
+
bodyData = {
|
|
154
|
+
query: CREATE_QUERY,
|
|
155
|
+
operationName: CREATE_OPERATION_NAME,
|
|
156
|
+
variables: {
|
|
157
|
+
input: {
|
|
158
|
+
pageId: documentARI,
|
|
159
|
+
key: key,
|
|
160
|
+
value: escapedValue
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
_context3.next = 6;
|
|
165
|
+
return fetch(GRAPHQL_ENDPOINT, {
|
|
166
|
+
method: 'POST',
|
|
167
|
+
headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
|
|
168
|
+
body: JSON.stringify(bodyData)
|
|
169
|
+
});
|
|
170
|
+
case 6:
|
|
171
|
+
response = _context3.sent;
|
|
172
|
+
if (response.ok) {
|
|
173
|
+
_context3.next = 9;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
throw new Error("Failed to create content property: ".concat(response.statusText));
|
|
177
|
+
case 9:
|
|
178
|
+
_context3.next = 11;
|
|
179
|
+
return response.json();
|
|
180
|
+
case 11:
|
|
181
|
+
contentProperty = _context3.sent;
|
|
182
|
+
return _context3.abrupt("return", contentProperty);
|
|
183
|
+
case 13:
|
|
184
|
+
case "end":
|
|
185
|
+
return _context3.stop();
|
|
186
|
+
}
|
|
187
|
+
}, _callee3);
|
|
188
|
+
}));
|
|
189
|
+
return function createContentProperty(_x3) {
|
|
190
|
+
return _ref6.apply(this, arguments);
|
|
191
|
+
};
|
|
192
|
+
}();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Mapping } from '@atlaskit/editor-prosemirror/transform';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Rebase `currentTr` over `incomingTr` based on the provided `state`.
|
|
5
|
+
* This function adjusts the steps in `currentTr` to account for the changes made by `incomingTr`.
|
|
6
|
+
*
|
|
7
|
+
* @param currentTr - The transaction to be rebased.
|
|
8
|
+
* @param incomingTr - The transaction that has already been applied to the state.
|
|
9
|
+
* @param state - The editor state after applying `incomingTr`.
|
|
10
|
+
* @returns A new transaction that represents `currentTr` rebased over `incomingTr`.
|
|
11
|
+
*/
|
|
12
|
+
export const rebaseTransaction = (currentTr, incomingTr, state) => {
|
|
13
|
+
if (!incomingTr.docChanged) {
|
|
14
|
+
return currentTr;
|
|
15
|
+
}
|
|
16
|
+
const currentMapping = new Mapping(incomingTr.mapping.maps);
|
|
17
|
+
const rebasedTransaction = state.tr;
|
|
18
|
+
currentTr.steps.forEach(step => {
|
|
19
|
+
const mappedStep = step.map(currentMapping);
|
|
20
|
+
if (mappedStep) {
|
|
21
|
+
rebasedTransaction.step(mappedStep);
|
|
22
|
+
currentMapping.appendMap(mappedStep.getMap());
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return rebasedTransaction;
|
|
26
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
2
|
+
import { useEffect, useState, useMemo } from 'react';
|
|
3
3
|
import { convertSyncBlockPMNodeToSyncBlockData } from '../utils/utils';
|
|
4
4
|
import { SyncBlockDataProvider } from './types';
|
|
5
5
|
export class SyncBlockProvider extends SyncBlockDataProvider {
|
|
@@ -32,6 +32,9 @@ export class SyncBlockProvider extends SyncBlockDataProvider {
|
|
|
32
32
|
});
|
|
33
33
|
return Promise.all(resourceIds);
|
|
34
34
|
});
|
|
35
|
+
_defineProperty(this, "getSourceId", () => {
|
|
36
|
+
return this.sourceId;
|
|
37
|
+
});
|
|
35
38
|
this.fetchProvider = fetchProvider;
|
|
36
39
|
this.writeProvider = writeProvider;
|
|
37
40
|
this.sourceId = sourceId;
|
|
@@ -39,26 +42,35 @@ export class SyncBlockProvider extends SyncBlockDataProvider {
|
|
|
39
42
|
}
|
|
40
43
|
export const useFetchDocNode = (editorView, node, defaultDocNode, provider) => {
|
|
41
44
|
const [docNode, setDocNode] = useState(defaultDocNode);
|
|
45
|
+
const fetchNode = (editorView, node, provider) => {
|
|
46
|
+
const nodes = [convertSyncBlockPMNodeToSyncBlockData(node, false)];
|
|
47
|
+
provider === null || provider === void 0 ? void 0 : provider.fetchNodesData(nodes).then(data => {
|
|
48
|
+
var _data$;
|
|
49
|
+
if (data && (_data$ = data[0]) !== null && _data$ !== void 0 && _data$.content) {
|
|
50
|
+
const newNode = editorView.state.schema.nodeFromJSON(data[0].content);
|
|
51
|
+
setDocNode({
|
|
52
|
+
...newNode.toJSON(),
|
|
53
|
+
version: 1
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
|
42
58
|
useEffect(() => {
|
|
43
59
|
if (!provider) {
|
|
44
60
|
return;
|
|
45
61
|
}
|
|
62
|
+
fetchNode(editorView, node, provider);
|
|
46
63
|
const interval = window.setInterval(() => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
var _data$;
|
|
50
|
-
if (data && (_data$ = data[0]) !== null && _data$ !== void 0 && _data$.content) {
|
|
51
|
-
const newNode = editorView.state.schema.nodeFromJSON(data[0].content);
|
|
52
|
-
setDocNode({
|
|
53
|
-
...newNode.toJSON(),
|
|
54
|
-
version: 1
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
}, 1000);
|
|
64
|
+
fetchNode(editorView, node, provider);
|
|
65
|
+
}, 3000);
|
|
59
66
|
return () => {
|
|
60
67
|
window.clearInterval(interval);
|
|
61
68
|
};
|
|
62
69
|
}, [editorView, node, provider]);
|
|
63
70
|
return docNode;
|
|
71
|
+
};
|
|
72
|
+
export const useMemoizedSyncedBlockProvider = (fetchProvider, writeProvider, sourceId) => {
|
|
73
|
+
return useMemo(() => {
|
|
74
|
+
return new SyncBlockProvider(fetchProvider, writeProvider, sourceId);
|
|
75
|
+
}, [fetchProvider, writeProvider, sourceId]);
|
|
64
76
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import uuid from 'uuid';
|
|
2
|
+
import { rebaseTransaction } from './rebase-transaction';
|
|
2
3
|
|
|
3
4
|
// Do this typedef to make it clear that
|
|
4
5
|
// this is a local identifier for a resource for local use
|
|
@@ -9,14 +10,14 @@ import uuid from 'uuid';
|
|
|
9
10
|
// Handles caching, debouncing updates, and publish/subscribe for local changes.
|
|
10
11
|
// Ensures consistency between local and remote state, and can be used in both editor and renderer contexts.
|
|
11
12
|
export class SyncBlockStoreManager {
|
|
12
|
-
constructor(
|
|
13
|
+
constructor(dataProvider) {
|
|
13
14
|
this.syncBlocks = new Map();
|
|
15
|
+
this.dataProvider = dataProvider;
|
|
14
16
|
}
|
|
15
17
|
setEditorView(editorView) {
|
|
16
18
|
this.editorView = editorView;
|
|
17
19
|
}
|
|
18
20
|
isSourceBlock(node) {
|
|
19
|
-
var _this$syncBlocks$get;
|
|
20
21
|
if (node.type.name !== 'syncBlock') {
|
|
21
22
|
return false;
|
|
22
23
|
}
|
|
@@ -24,7 +25,7 @@ export class SyncBlockStoreManager {
|
|
|
24
25
|
resourceId,
|
|
25
26
|
localId
|
|
26
27
|
} = node.attrs;
|
|
27
|
-
return
|
|
28
|
+
return resourceId.includes(localId);
|
|
28
29
|
}
|
|
29
30
|
registerConfirmationCallback(callback) {
|
|
30
31
|
this.confirmationCallback = callback;
|
|
@@ -36,15 +37,18 @@ export class SyncBlockStoreManager {
|
|
|
36
37
|
return !!this.confirmationCallback;
|
|
37
38
|
}
|
|
38
39
|
createSyncBlockNode() {
|
|
40
|
+
var _this$dataProvider;
|
|
39
41
|
// TODO: EDITOR-1644 - properly implement creation of the synced block
|
|
40
42
|
// below is a temporary implementation for the creation of the synced block
|
|
41
43
|
// the resource id needs to have pageId and content property key in it
|
|
44
|
+
// Note: If the data provider is not set, the resource id will be the local id
|
|
42
45
|
|
|
43
|
-
const blockInstanceId = uuid();
|
|
44
46
|
const localId = uuid();
|
|
47
|
+
const sourceId = (_this$dataProvider = this.dataProvider) === null || _this$dataProvider === void 0 ? void 0 : _this$dataProvider.getSourceId();
|
|
48
|
+
const resourceId = sourceId ? `${sourceId}/${localId}` : localId;
|
|
45
49
|
const syncBlockNode = {
|
|
46
50
|
attrs: {
|
|
47
|
-
resourceId
|
|
51
|
+
resourceId,
|
|
48
52
|
localId
|
|
49
53
|
},
|
|
50
54
|
type: 'syncBlock'
|
|
@@ -57,16 +61,23 @@ export class SyncBlockStoreManager {
|
|
|
57
61
|
}
|
|
58
62
|
async deleteSyncBlocksWithConfirmation(tr, syncBlockIds) {
|
|
59
63
|
if (this.confirmationCallback) {
|
|
64
|
+
this.confirmationTransaction = tr;
|
|
60
65
|
const confirmed = await this.confirmationCallback();
|
|
61
66
|
if (confirmed) {
|
|
62
67
|
var _this$editorView;
|
|
63
|
-
|
|
64
|
-
(_this$editorView = this.editorView) === null || _this$editorView === void 0 ? void 0 : _this$editorView.dispatch(tr.setMeta('isConfirmedSyncBlockDeletion', true));
|
|
68
|
+
(_this$editorView = this.editorView) === null || _this$editorView === void 0 ? void 0 : _this$editorView.dispatch(this.confirmationTransaction.setMeta('isConfirmedSyncBlockDeletion', true));
|
|
65
69
|
// Need to update the BE on deletion
|
|
66
70
|
syncBlockIds.forEach(({
|
|
67
71
|
resourceId
|
|
68
72
|
}) => this.syncBlocks.delete(resourceId));
|
|
69
73
|
}
|
|
74
|
+
this.confirmationTransaction = undefined;
|
|
70
75
|
}
|
|
71
76
|
}
|
|
77
|
+
rebaseTransaction(incomingTr, state) {
|
|
78
|
+
if (!this.confirmationTransaction) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
this.confirmationTransaction = rebaseTransaction(this.confirmationTransaction, incomingTr, state);
|
|
82
|
+
}
|
|
72
83
|
}
|
package/dist/es2019/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/* eslint-disable @atlaskit/editor/no-re-export */
|
|
2
2
|
|
|
3
|
-
export { SyncBlockProvider as SyncedBlockProvider, useFetchDocNode } from './common/syncBlockProvider';
|
|
3
|
+
export { SyncBlockProvider as SyncedBlockProvider, useFetchDocNode, useMemoizedSyncedBlockProvider } from './common/syncBlockProvider';
|
|
4
4
|
export { SyncBlockStoreManager } from './common/syncBlockStoreManager';
|
|
5
5
|
export { inMemoryFetchProvider, inMemoryWriteProvider } from './providers/inMemory';
|
|
6
|
+
export { getDefaultSyncBlockSchema } from './common/schema';
|
|
7
|
+
export { createContentAPIProvidersWithDefaultKey, useMemoizedContentAPIProviders } from './providers/confluenceContentAPI';
|
|
8
|
+
export { getConfluencePageAri } from './utils/ari';
|
|
6
9
|
export { convertSyncBlockPMNodeToSyncBlockData, generateSyncBlockSourceUrl } from './utils/utils';
|
|
7
|
-
export {
|
|
10
|
+
export { rebaseTransaction } from './common/rebase-transaction';
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { getLocalIdFromAri, getPageIdFromAri } from '../utils/ari';
|
|
4
|
+
import { getContentProperty, createContentProperty, updateContentProperty } from '../utils/contentProperty';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for Content API providers
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const getContentPropertyKey = (contentPropertyKey, localId) => {
|
|
11
|
+
return contentPropertyKey + '-' + localId;
|
|
12
|
+
};
|
|
13
|
+
const parseSyncedBlockContentPropertyValue = value => {
|
|
14
|
+
try {
|
|
15
|
+
if (typeof value === 'string') {
|
|
16
|
+
return JSON.parse(value);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
// eslint-disable-next-line no-console
|
|
21
|
+
console.error('Failed to parse synced block content:', error);
|
|
22
|
+
return {
|
|
23
|
+
content: undefined
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* ADFFetchProvider implementation that fetches synced block data from Confluence Content API
|
|
30
|
+
*/
|
|
31
|
+
class ConfluenceADFFetchProvider {
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.config = config;
|
|
34
|
+
}
|
|
35
|
+
async fetchData(resourceId) {
|
|
36
|
+
try {
|
|
37
|
+
var _contentProperty$data, _contentProperty$data2;
|
|
38
|
+
const pageId = getPageIdFromAri(resourceId);
|
|
39
|
+
const localId = getLocalIdFromAri(resourceId);
|
|
40
|
+
const key = getContentPropertyKey(this.config.contentPropertyKey, localId);
|
|
41
|
+
const options = {
|
|
42
|
+
pageId,
|
|
43
|
+
key,
|
|
44
|
+
cloudId: this.config.cloudId
|
|
45
|
+
};
|
|
46
|
+
const contentProperty = await getContentProperty(options);
|
|
47
|
+
const value = (_contentProperty$data = contentProperty.data.confluence.page.properties) === null || _contentProperty$data === void 0 ? void 0 : (_contentProperty$data2 = _contentProperty$data[0]) === null || _contentProperty$data2 === void 0 ? void 0 : _contentProperty$data2.value;
|
|
48
|
+
if (!value) {
|
|
49
|
+
throw new Error('Content property value does not exist');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Parse the synced block content from the property value
|
|
53
|
+
const syncedBlockData = parseSyncedBlockContentPropertyValue(value);
|
|
54
|
+
return {
|
|
55
|
+
content: syncedBlockData.content
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
59
|
+
console.error('Failed to fetch synced block data:', error);
|
|
60
|
+
return {
|
|
61
|
+
content: undefined
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* ADFWriteProvider implementation that writes synced block data to Confluence Content API
|
|
69
|
+
*/
|
|
70
|
+
class ConfluenceADFWriteProvider {
|
|
71
|
+
constructor(config) {
|
|
72
|
+
_defineProperty(this, "createNewContentProperty", async (pageId, key, value) => {
|
|
73
|
+
var _contentProperty$data3;
|
|
74
|
+
const contentProperty = await createContentProperty({
|
|
75
|
+
pageId,
|
|
76
|
+
key,
|
|
77
|
+
value,
|
|
78
|
+
cloudId: this.config.cloudId
|
|
79
|
+
});
|
|
80
|
+
if (((_contentProperty$data3 = contentProperty.data.confluence.createPageProperty.pageProperty) === null || _contentProperty$data3 === void 0 ? void 0 : _contentProperty$data3.key) === key) {
|
|
81
|
+
return key;
|
|
82
|
+
} else {
|
|
83
|
+
throw new Error('Failed to create content property');
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
this.config = config;
|
|
87
|
+
}
|
|
88
|
+
async writeData(sourceId, localId, data, resourceId) {
|
|
89
|
+
try {
|
|
90
|
+
const pageId = getPageIdFromAri(sourceId);
|
|
91
|
+
const syncedBlockValue = JSON.stringify({
|
|
92
|
+
content: data
|
|
93
|
+
});
|
|
94
|
+
if (resourceId) {
|
|
95
|
+
var _contentProperty$data4;
|
|
96
|
+
// Update existing content property
|
|
97
|
+
const localId = getLocalIdFromAri(resourceId);
|
|
98
|
+
const key = getContentPropertyKey(this.config.contentPropertyKey, localId);
|
|
99
|
+
const contentProperty = await updateContentProperty({
|
|
100
|
+
pageId,
|
|
101
|
+
key,
|
|
102
|
+
value: syncedBlockValue,
|
|
103
|
+
cloudId: this.config.cloudId
|
|
104
|
+
});
|
|
105
|
+
if (((_contentProperty$data4 = contentProperty.data.confluence.updateValuePageProperty.pageProperty) === null || _contentProperty$data4 === void 0 ? void 0 : _contentProperty$data4.key) === key) {
|
|
106
|
+
return key;
|
|
107
|
+
} else if (contentProperty.data.confluence.updateValuePageProperty.pageProperty === null) {
|
|
108
|
+
return this.createNewContentProperty(pageId, key, syncedBlockValue);
|
|
109
|
+
} else {
|
|
110
|
+
throw new Error('Failed to update content property');
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
// Create new content property
|
|
114
|
+
const key = getContentPropertyKey(this.config.contentPropertyKey, localId);
|
|
115
|
+
return this.createNewContentProperty(pageId, key, syncedBlockValue);
|
|
116
|
+
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
// eslint-disable-next-line no-console
|
|
119
|
+
console.error('Failed to write synced block data:', error);
|
|
120
|
+
return Promise.reject(error);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Factory function to create both providers with shared configuration
|
|
127
|
+
*/
|
|
128
|
+
const createContentAPIProviders = config => {
|
|
129
|
+
const fetchProvider = new ConfluenceADFFetchProvider(config);
|
|
130
|
+
const writeProvider = new ConfluenceADFWriteProvider(config);
|
|
131
|
+
return {
|
|
132
|
+
fetchProvider,
|
|
133
|
+
writeProvider
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Convenience function to create providers with default content property key
|
|
139
|
+
*/
|
|
140
|
+
export const createContentAPIProvidersWithDefaultKey = cloudId => {
|
|
141
|
+
return createContentAPIProviders({
|
|
142
|
+
cloudId,
|
|
143
|
+
contentPropertyKey: 'editor-synced-block'
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
export const useMemoizedContentAPIProviders = cloudId => {
|
|
147
|
+
return useMemo(() => {
|
|
148
|
+
return createContentAPIProvidersWithDefaultKey(cloudId);
|
|
149
|
+
}, [cloudId]);
|
|
150
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const getConfluencePageAri = (pageId, cloudId) => `ari:cloud:confluence:${cloudId}:page/${pageId}`;
|
|
2
|
+
export const getPageIdFromAri = ari => {
|
|
3
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
4
|
+
const match = ari.match(/ari:cloud:confluence:[^:]+:page\/(\d+)/);
|
|
5
|
+
if (match !== null && match !== void 0 && match[1]) {
|
|
6
|
+
return match[1];
|
|
7
|
+
}
|
|
8
|
+
throw new Error(`Invalid page ARI: ${ari}`);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param ari ari:cloud:confluence:<cloudId>:page/<pageId>/<localId>
|
|
14
|
+
* @returns
|
|
15
|
+
*/
|
|
16
|
+
export const getLocalIdFromAri = ari => {
|
|
17
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
18
|
+
const match = ari.match(/ari:cloud:confluence:[^:]+:page\/\d+\/([a-zA-Z0-9-]+)/);
|
|
19
|
+
if (match !== null && match !== void 0 && match[1]) {
|
|
20
|
+
return match[1];
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`Invalid page ARI: ${ari}`);
|
|
23
|
+
};
|
|
24
|
+
export const getContentPropertyAri = (contentPropertyId, cloudId) => `ari:cloud:confluence:${cloudId}:content/${contentPropertyId}`;
|
|
25
|
+
export const getContentPropertyIdFromAri = ari => {
|
|
26
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
27
|
+
const match = ari.match(/ari:cloud:confluence:[^:]+:content\/([^/]+)/);
|
|
28
|
+
if (match) {
|
|
29
|
+
return match[1];
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`Invalid content property ARI: ${ari}`);
|
|
32
|
+
};
|