@atlaskit/editor-synced-block-provider 2.6.0 → 2.7.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/cjs/hooks/useFetchSyncBlockData.js +2 -2
  3. package/dist/cjs/index.js +9 -4
  4. package/dist/cjs/providers/confluence/confluenceContentAPI.js +86 -73
  5. package/dist/cjs/providers/syncBlockProvider.js +60 -36
  6. package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +4 -4
  7. package/dist/cjs/store-manager/sourceSyncBlockStoreManager.js +78 -17
  8. package/dist/cjs/store-manager/syncBlockStoreManager.js +41 -8
  9. package/dist/cjs/utils/errorHandling.js +13 -0
  10. package/dist/cjs/utils/utils.js +32 -1
  11. package/dist/es2019/hooks/useFetchSyncBlockData.js +1 -1
  12. package/dist/es2019/index.js +2 -3
  13. package/dist/es2019/providers/confluence/confluenceContentAPI.js +62 -40
  14. package/dist/es2019/providers/syncBlockProvider.js +15 -9
  15. package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +1 -1
  16. package/dist/es2019/store-manager/sourceSyncBlockStoreManager.js +64 -12
  17. package/dist/es2019/store-manager/syncBlockStoreManager.js +31 -8
  18. package/dist/es2019/utils/errorHandling.js +7 -0
  19. package/dist/es2019/utils/utils.js +31 -0
  20. package/dist/esm/hooks/useFetchSyncBlockData.js +1 -1
  21. package/dist/esm/index.js +2 -3
  22. package/dist/esm/providers/confluence/confluenceContentAPI.js +79 -66
  23. package/dist/esm/providers/syncBlockProvider.js +60 -36
  24. package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +1 -1
  25. package/dist/esm/store-manager/sourceSyncBlockStoreManager.js +79 -18
  26. package/dist/esm/store-manager/syncBlockStoreManager.js +41 -8
  27. package/dist/esm/utils/errorHandling.js +7 -0
  28. package/dist/esm/utils/utils.js +31 -0
  29. package/dist/types/index.d.ts +1 -2
  30. package/dist/types/providers/confluence/confluenceContentAPI.d.ts +2 -2
  31. package/dist/types/providers/syncBlockProvider.d.ts +4 -3
  32. package/dist/types/providers/types.d.ts +6 -2
  33. package/dist/types/store-manager/sourceSyncBlockStoreManager.d.ts +15 -3
  34. package/dist/types/store-manager/syncBlockStoreManager.d.ts +20 -3
  35. package/dist/types/utils/errorHandling.d.ts +1 -0
  36. package/dist/types/utils/utils.d.ts +6 -1
  37. package/dist/types-ts4.5/index.d.ts +1 -2
  38. package/dist/types-ts4.5/providers/confluence/confluenceContentAPI.d.ts +2 -2
  39. package/dist/types-ts4.5/providers/syncBlockProvider.d.ts +4 -3
  40. package/dist/types-ts4.5/providers/types.d.ts +6 -2
  41. package/dist/types-ts4.5/store-manager/sourceSyncBlockStoreManager.d.ts +15 -3
  42. package/dist/types-ts4.5/store-manager/syncBlockStoreManager.d.ts +20 -3
  43. package/dist/types-ts4.5/utils/errorHandling.d.ts +1 -0
  44. package/dist/types-ts4.5/utils/utils.d.ts +6 -1
  45. package/package.json +2 -2
  46. package/dist/cjs/utils/createSyncBlock.js +0 -15
  47. package/dist/es2019/utils/createSyncBlock.js +0 -9
  48. package/dist/esm/utils/createSyncBlock.js +0 -9
  49. package/dist/types/utils/createSyncBlock.d.ts +0 -2
  50. package/dist/types-ts4.5/utils/createSyncBlock.d.ts +0 -2
@@ -47,7 +47,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
47
47
  key: "flushBodiedSyncBlocks",
48
48
  value: (function () {
49
49
  var _flushBodiedSyncBlocks = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
50
- var bodiedSyncBlockNodes, bodiedSyncBlockData, resourceIds;
50
+ var bodiedSyncBlockNodes, bodiedSyncBlockData, writeResults;
51
51
  return _regenerator.default.wrap(function _callee$(_context) {
52
52
  while (1) switch (_context.prev = _context.next) {
53
53
  case 0:
@@ -79,9 +79,9 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
79
79
  _context.next = 10;
80
80
  return this.dataProvider.writeNodesData(bodiedSyncBlockNodes, bodiedSyncBlockData);
81
81
  case 10:
82
- resourceIds = _context.sent;
83
- return _context.abrupt("return", resourceIds.every(function (resourceId) {
84
- return resourceId !== undefined;
82
+ writeResults = _context.sent;
83
+ return _context.abrupt("return", writeResults.every(function (result) {
84
+ return result.resourceId !== undefined;
85
85
  }));
86
86
  case 14:
87
87
  _context.prev = 14;
@@ -103,6 +103,35 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
103
103
  value: function setEditorView(editorView) {
104
104
  this.editorView = editorView;
105
105
  }
106
+ }, {
107
+ key: "registerPendingCreation",
108
+ value: function registerPendingCreation(resourceId) {
109
+ this.pendingResourceId = resourceId;
110
+ }
111
+ }, {
112
+ key: "registerCreationCallback",
113
+ value: function registerCreationCallback(callback) {
114
+ this.creationCallback = callback;
115
+ }
116
+
117
+ /**
118
+ * Fires callback to insert node (if creation is successful) and clears pending creation data
119
+ * @param success
120
+ */
121
+ }, {
122
+ key: "commitPendingCreation",
123
+ value: function commitPendingCreation(success) {
124
+ if (success && this.creationCallback) {
125
+ this.creationCallback();
126
+ }
127
+ this.pendingResourceId = undefined;
128
+ this.creationCallback = undefined;
129
+ }
130
+ }, {
131
+ key: "hasPendingCreation",
132
+ value: function hasPendingCreation() {
133
+ return !!this.pendingResourceId;
134
+ }
106
135
  }, {
107
136
  key: "registerConfirmationCallback",
108
137
  value: function registerConfirmationCallback(callback) {
@@ -118,31 +147,63 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
118
147
  return !!this.confirmationCallback;
119
148
  }
120
149
  }, {
121
- key: "createSyncBlockNode",
122
- value: function createSyncBlockNode() {
150
+ key: "generateBodiedSyncBlockAttrs",
151
+ value: function generateBodiedSyncBlockAttrs() {
123
152
  var _this$dataProvider;
124
- var blockInstanceId = (0, _uuid.default)();
153
+ var localId = (0, _uuid.default)();
125
154
  var sourceId = (_this$dataProvider = this.dataProvider) === null || _this$dataProvider === void 0 ? void 0 : _this$dataProvider.getSourceId();
126
155
  if (!sourceId) {
127
156
  throw new Error('Provider of sync block plugin is not set');
128
157
  }
129
158
 
130
159
  // This should be generated by the data provider implementation as it differs between data providers
131
- var resourceId = (0, _ari.resourceIdFromSourceAndLocalId)(sourceId, blockInstanceId);
132
- var syncBlockNode = {
133
- attrs: {
134
- resourceId: resourceId,
135
- localId: blockInstanceId
136
- },
137
- type: 'bodiedSyncBlock'
160
+ var resourceId = (0, _ari.resourceIdFromSourceAndLocalId)(sourceId, localId);
161
+ return {
162
+ resourceId: resourceId,
163
+ localId: localId
138
164
  };
139
- return syncBlockNode;
165
+ }
166
+ }, {
167
+ key: "createBodiedSyncBlockNode",
168
+ value: function createBodiedSyncBlockNode(attrs) {
169
+ var _this2 = this;
170
+ try {
171
+ if (!this.dataProvider) {
172
+ throw new Error('Data provider not set');
173
+ }
174
+ var resourceId = attrs.resourceId,
175
+ blockInstanceId = attrs.localId;
176
+ this.dataProvider.writeNodesData([(0, _utils.createBodiedSyncBlockNode)(blockInstanceId, resourceId)], [{
177
+ content: [],
178
+ blockInstanceId: blockInstanceId,
179
+ resourceId: resourceId
180
+ }]).then(function (results) {
181
+ results.forEach(function (result) {
182
+ var resourceId = result.resourceId;
183
+ if (resourceId) {
184
+ _this2.commitPendingCreation(true);
185
+ } else {
186
+ _this2.commitPendingCreation(false);
187
+ // TODO: EDITOR-1921 - add error analytics
188
+ }
189
+ });
190
+ }).catch(function (_reason) {
191
+ _this2.commitPendingCreation(false);
192
+ // TODO: EDITOR-1921 - add error analytics
193
+ });
194
+ this.registerPendingCreation(resourceId);
195
+ } catch (error) {
196
+ if (this.hasPendingCreation()) {
197
+ this.commitPendingCreation(false);
198
+ }
199
+ // TODO: EDITOR-1921 - add error analytics
200
+ }
140
201
  }
141
202
  }, {
142
203
  key: "deleteSyncBlocksWithConfirmation",
143
204
  value: function () {
144
205
  var _deleteSyncBlocksWithConfirmation = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(tr, syncBlockIds) {
145
- var _this2 = this;
206
+ var _this3 = this;
146
207
  var confirmed, _this$editorView, results;
147
208
  return _regenerator.default.wrap(function _callee2$(_context2) {
148
209
  while (1) switch (_context2.prev = _context2.next) {
@@ -177,7 +238,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
177
238
  results.forEach(function (result) {
178
239
  if (result.success) {
179
240
  // Only delete when it's deleted successfully in backend
180
- _this2.syncBlockCache.delete(result.resourceId);
241
+ _this3.syncBlockCache.delete(result.resourceId);
181
242
  } else {
182
243
  // TODO: EDITOR-1921 - add error analytics with result.error
183
244
  }
@@ -7,7 +7,7 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.SyncBlockStoreManager = void 0;
8
8
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
9
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
- var _createSyncBlock = require("../utils/createSyncBlock");
10
+ var _utils = require("../utils/utils");
11
11
  var _referenceSyncBlockStoreManager = require("./referenceSyncBlockStoreManager");
12
12
  var _sourceSyncBlockStoreManager = require("./sourceSyncBlockStoreManager");
13
13
  // A store manager responsible for the lifecycle and state management of sync blocks in an editor instance.
@@ -30,10 +30,10 @@ var SyncBlockStoreManager = exports.SyncBlockStoreManager = /*#__PURE__*/functio
30
30
  return (0, _createClass2.default)(SyncBlockStoreManager, [{
31
31
  key: "fetchSyncBlocksData",
32
32
  value: function fetchSyncBlocksData(nodes) {
33
- var syncBlockNodes = nodes.filter(function (node) {
34
- return node.type.name === 'syncBlock' && node.attrs.resourceId && node.attrs.localId;
35
- }).map(function (node) {
36
- return (0, _createSyncBlock.createSyncBlockNode)(node.attrs.localId, node.attrs.resourceId);
33
+ var syncBlockNodes = nodes.map(function (node) {
34
+ return (0, _utils.convertPMNodeToSyncBlockNode)(node);
35
+ }).filter(function (node) {
36
+ return node !== undefined;
37
37
  }) || [];
38
38
  if (syncBlockNodes.length === 0) {
39
39
  return Promise.resolve([]);
@@ -105,11 +105,44 @@ var SyncBlockStoreManager = exports.SyncBlockStoreManager = /*#__PURE__*/functio
105
105
  // only applicable to source sync block, for now (will be refactored further)
106
106
  return this.sourceSyncBlockStoreManager.requireConfirmationBeforeDelete();
107
107
  }
108
+
109
+ /**
110
+ * Register callback function (which inserts node, handles focus etc) to be used later when creation to backend succeed
111
+ */
112
+ }, {
113
+ key: "registerCreationCallback",
114
+ value: function registerCreationCallback(callback) {
115
+ this.sourceSyncBlockStoreManager.registerCreationCallback(callback);
116
+ }
117
+
118
+ /**
119
+ *
120
+ * @returns true if waiting for the result of saving new bodiedSyncBlock to backend
121
+ */
122
+ }, {
123
+ key: "hasPendingCreation",
124
+ value: function hasPendingCreation() {
125
+ return this.sourceSyncBlockStoreManager.hasPendingCreation();
126
+ }
127
+
128
+ /**
129
+ * @returns attributes for a new bodiedSyncBlock node
130
+ */
131
+ }, {
132
+ key: "generateBodiedSyncBlockAttrs",
133
+ value: function generateBodiedSyncBlockAttrs() {
134
+ return this.sourceSyncBlockStoreManager.generateBodiedSyncBlockAttrs();
135
+ }
136
+
137
+ /**
138
+ * Save bodiedSyncBlock with empty content to backend
139
+ * @param attrs attributes Ids of the node
140
+ */
108
141
  }, {
109
- key: "createSyncBlockNode",
110
- value: function createSyncBlockNode() {
142
+ key: "createBodiedSyncBlockNode",
143
+ value: function createBodiedSyncBlockNode(attrs) {
111
144
  // only applicable to source sync block, for now (will be refactored further)
112
- return this.sourceSyncBlockStoreManager.createSyncBlockNode();
145
+ return this.sourceSyncBlockStoreManager.createBodiedSyncBlockNode(attrs);
113
146
  }
114
147
  }, {
115
148
  key: "subscribeToSyncBlockData",
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.stringifyError = void 0;
7
+ var stringifyError = exports.stringifyError = function stringifyError(error) {
8
+ try {
9
+ return JSON.stringify(error);
10
+ } catch (_unused) {
11
+ return undefined;
12
+ }
13
+ };
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isBlogPageType = exports.convertSyncBlockPMNodeToSyncBlockData = void 0;
6
+ exports.isBlogPageType = exports.createSyncBlockNode = exports.createBodiedSyncBlockNode = exports.convertSyncBlockPMNodeToSyncBlockData = exports.convertSyncBlockJSONNodeToSyncBlockNode = exports.convertPMNodeToSyncBlockNode = void 0;
7
7
  var convertSyncBlockPMNodeToSyncBlockData = exports.convertSyncBlockPMNodeToSyncBlockData = function convertSyncBlockPMNodeToSyncBlockData(node) {
8
8
  return {
9
9
  blockInstanceId: node.attrs.localId,
@@ -13,4 +13,35 @@ var convertSyncBlockPMNodeToSyncBlockData = exports.convertSyncBlockPMNodeToSync
13
13
  };
14
14
  var isBlogPageType = exports.isBlogPageType = function isBlogPageType(pageType) {
15
15
  return pageType === 'blogpost';
16
+ };
17
+ var createSyncBlockNode = exports.createSyncBlockNode = function createSyncBlockNode(localId, resourceId) {
18
+ return {
19
+ type: 'syncBlock',
20
+ attrs: {
21
+ localId: localId,
22
+ resourceId: resourceId
23
+ }
24
+ };
25
+ };
26
+ var createBodiedSyncBlockNode = exports.createBodiedSyncBlockNode = function createBodiedSyncBlockNode(localId, resourceId) {
27
+ return {
28
+ type: 'bodiedSyncBlock',
29
+ attrs: {
30
+ localId: localId,
31
+ resourceId: resourceId
32
+ }
33
+ };
34
+ };
35
+ var convertSyncBlockJSONNodeToSyncBlockNode = exports.convertSyncBlockJSONNodeToSyncBlockNode = function convertSyncBlockJSONNodeToSyncBlockNode(node) {
36
+ if (node.type !== 'syncBlock' || !node.attrs || !('localId' in node.attrs) || !('resourceId' in node.attrs) || typeof node.attrs.localId !== 'string' || typeof node.attrs.resourceId !== 'string') {
37
+ return undefined;
38
+ }
39
+ return createSyncBlockNode(node.attrs.localId, node.attrs.resourceId);
40
+ };
41
+ var convertPMNodeToSyncBlockNode = exports.convertPMNodeToSyncBlockNode = function convertPMNodeToSyncBlockNode(node) {
42
+ var _node$attrs, _node$attrs2;
43
+ if (node.type.name !== 'syncBlock' || !((_node$attrs = node.attrs) !== null && _node$attrs !== void 0 && _node$attrs.localId) || !((_node$attrs2 = node.attrs) !== null && _node$attrs2 !== void 0 && _node$attrs2.resourceId) || typeof node.attrs.localId !== 'string' || typeof node.attrs.resourceId !== 'string') {
44
+ return undefined;
45
+ }
46
+ return createSyncBlockNode(node.attrs.localId, node.attrs.resourceId);
16
47
  };
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useEffect, useState } from 'react';
2
2
  import { SyncBlockError } from '../common/types';
3
- import { createSyncBlockNode } from '../utils/createSyncBlock';
3
+ import { createSyncBlockNode } from '../utils/utils';
4
4
  export const useFetchSyncBlockData = (manager, resourceId, localId) => {
5
5
  const [syncBlockInstance, setSyncBlockInstance] = useState(null);
6
6
  const [isLoading, setIsLoading] = useState(true);
@@ -11,6 +11,5 @@ export { SyncBlockProvider as SyncedBlockProvider, useMemoizedSyncedBlockProvide
11
11
  export { ReferenceSyncBlockStoreManager } from './store-manager/referenceSyncBlockStoreManager';
12
12
  export { SyncBlockStoreManager } from './store-manager/syncBlockStoreManager';
13
13
  export { getConfluencePageAri, getPageIdAndTypeFromAri } from './utils/ari';
14
- export { createSyncBlockNode } from './utils/createSyncBlock';
15
- export { resolveSyncBlockInstance } from './utils/resolveSyncBlockInstance';
16
- export { convertSyncBlockPMNodeToSyncBlockData } from './utils/utils';
14
+ export { createSyncBlockNode, convertSyncBlockPMNodeToSyncBlockData, convertSyncBlockJSONNodeToSyncBlockNode } from './utils/utils';
15
+ export { resolveSyncBlockInstance } from './utils/resolveSyncBlockInstance';
@@ -3,6 +3,7 @@ import { useMemo } from 'react';
3
3
  import { SyncBlockError } from '../../common/types';
4
4
  import { getLocalIdFromAri, getPageIdAndTypeFromAri } from '../../utils/ari';
5
5
  import { getContentProperty, createContentProperty, updateContentProperty, deleteContentProperty } from '../../utils/contentProperty';
6
+ import { stringifyError } from '../../utils/errorHandling';
6
7
  import { isBlogPageType } from '../../utils/utils';
7
8
 
8
9
  /**
@@ -118,7 +119,7 @@ class ConfluenceADFWriteProvider {
118
119
  if (((_contentProperty$data7 = contentProperty.data.confluence.createBlogPostProperty.blogPostProperty) === null || _contentProperty$data7 === void 0 ? void 0 : _contentProperty$data7.key) === key) {
119
120
  return key;
120
121
  } else {
121
- throw new Error('Failed to create blog post content property');
122
+ return Promise.reject('Failed to create blog post content property');
122
123
  }
123
124
  } else {
124
125
  var _contentProperty$data8;
@@ -126,20 +127,31 @@ class ConfluenceADFWriteProvider {
126
127
  if (((_contentProperty$data8 = contentProperty.data.confluence.createPageProperty.pageProperty) === null || _contentProperty$data8 === void 0 ? void 0 : _contentProperty$data8.key) === key) {
127
128
  return key;
128
129
  } else {
129
- throw new Error('Failed to create page content property');
130
+ return Promise.reject('Failed to create page content property');
130
131
  }
131
132
  }
132
133
  });
133
134
  this.config = config;
134
135
  }
135
136
  async writeData(data) {
137
+ let match;
138
+ const {
139
+ resourceId
140
+ } = data;
141
+ try {
142
+ match = getPageIdAndTypeFromAri(data.resourceId);
143
+ } catch (error) {
144
+ return {
145
+ error: stringifyError(error)
146
+ };
147
+ }
136
148
  const {
137
149
  id: pageId,
138
150
  type: pageType
139
- } = getPageIdAndTypeFromAri(data.resourceId);
140
- if (data.resourceId) {
151
+ } = match;
152
+ try {
141
153
  // Update existing content property
142
- const localId = getLocalIdFromAri(data.resourceId);
154
+ const localId = getLocalIdFromAri(resourceId);
143
155
  const key = getContentPropertyKey(this.config.contentPropertyKey, localId);
144
156
  const options = {
145
157
  pageId,
@@ -148,55 +160,65 @@ class ConfluenceADFWriteProvider {
148
160
  cloudId: this.config.cloudId,
149
161
  pageType
150
162
  };
151
- if (isBlogPageType(pageType)) {
152
- var _contentProperty$data9;
153
- const contentProperty = await updateContentProperty(options);
154
- if (((_contentProperty$data9 = contentProperty.data.confluence.updateValueBlogPostProperty.blogPostProperty) === null || _contentProperty$data9 === void 0 ? void 0 : _contentProperty$data9.key) === key) {
155
- return key;
156
- } else if (contentProperty.data.confluence.updateValueBlogPostProperty.blogPostProperty === null) {
157
- return this.createNewContentProperty(pageId, key, data, pageType);
158
- } else {
159
- throw new Error('Failed to update blog post content property');
160
- }
163
+ const updatePayload = await updateContentProperty(options);
164
+ const updateResult = isBlogPageType(pageType) ? updatePayload.data.confluence.updateValueBlogPostProperty.blogPostProperty : updatePayload.data.confluence.updateValuePageProperty.pageProperty;
165
+ if ((updateResult === null || updateResult === void 0 ? void 0 : updateResult.key) === key) {
166
+ return {
167
+ resourceId
168
+ };
169
+ } else if (!updateResult) {
170
+ return this.createNewContentProperty(pageId, key, data, pageType).then(() => {
171
+ return {
172
+ resourceId
173
+ };
174
+ }, error => {
175
+ return {
176
+ error
177
+ };
178
+ });
161
179
  } else {
162
- var _contentProperty$data0;
163
- const contentProperty = await updateContentProperty(options);
164
- if (((_contentProperty$data0 = contentProperty.data.confluence.updateValuePageProperty.pageProperty) === null || _contentProperty$data0 === void 0 ? void 0 : _contentProperty$data0.key) === key) {
165
- return key;
166
- } else if (contentProperty.data.confluence.updateValuePageProperty.pageProperty === null) {
167
- return this.createNewContentProperty(pageId, key, data, pageType);
168
- } else {
169
- throw new Error('Failed to update content property');
170
- }
180
+ return {
181
+ error: `Failed to update ${pageType} content property`
182
+ };
171
183
  }
172
- } else {
173
- // Create new content property
174
- const key = getContentPropertyKey(this.config.contentPropertyKey, data.blockInstanceId);
175
- return this.createNewContentProperty(pageId, key, data, pageType);
184
+ } catch {
185
+ return {
186
+ error: `Failed to write ${pageType}`
187
+ };
176
188
  }
177
189
  }
178
190
  async deleteData(resourceId) {
191
+ let deletePayload, deleteResult, match;
192
+ try {
193
+ match = getPageIdAndTypeFromAri(resourceId);
194
+ } catch (error) {
195
+ return {
196
+ resourceId,
197
+ success: false,
198
+ error: stringifyError(error)
199
+ };
200
+ }
179
201
  const {
180
202
  id: pageId,
181
203
  type: pageType
182
- } = getPageIdAndTypeFromAri(resourceId);
183
- const localId = getLocalIdFromAri(resourceId);
184
- const key = getContentPropertyKey(this.config.contentPropertyKey, localId);
185
- const options = {
186
- pageId,
187
- key,
188
- cloudId: this.config.cloudId,
189
- pageType
190
- };
191
- let deletePayload, deleteResult;
204
+ } = match;
192
205
  try {
206
+ const localId = getLocalIdFromAri(resourceId);
207
+ const key = getContentPropertyKey(this.config.contentPropertyKey, localId);
208
+ const options = {
209
+ pageId,
210
+ key,
211
+ cloudId: this.config.cloudId,
212
+ pageType
213
+ };
193
214
  deletePayload = await deleteContentProperty(options);
194
215
  deleteResult = isBlogPageType(pageType) ? deletePayload.data.confluence.deleteBlogPostProperty : deletePayload.data.confluence.deletePageProperty;
195
- } catch {
216
+ } catch (error) {
217
+ var _stringifyError;
196
218
  return {
197
219
  resourceId,
198
220
  success: false,
199
- error: `Fail to delete ${pageType} content property`
221
+ error: (_stringifyError = stringifyError(error)) !== null && _stringifyError !== void 0 ? _stringifyError : `Fail to delete ${pageType} content property`
200
222
  };
201
223
  }
202
224
  return {
@@ -41,19 +41,25 @@ export class SyncBlockProvider extends SyncBlockDataProvider {
41
41
  * @param nodes
42
42
  * @param data
43
43
  *
44
- * @returns the resource ids of the nodes that were written
44
+ * @returns Array of {resourceId?: string, error?: string}.
45
+ * resourceId: resource id of the node if write successfully , error: reason for when write failed
45
46
  */
46
- writeNodesData(nodes, data) {
47
- const resourceIds = [];
48
- nodes.forEach((_node, index) => {
47
+ async writeNodesData(nodes, data) {
48
+ const results = await Promise.allSettled(nodes.map((_node, index) => {
49
49
  if (!data[index].content) {
50
- resourceIds.push(Promise.reject('No Synced Block content to write'));
51
- return;
50
+ return Promise.reject('No Synced Block content to write');
51
+ }
52
+ return this.writeProvider.writeData(data[index]);
53
+ }));
54
+ return results.map(result => {
55
+ if (result.status === 'fulfilled') {
56
+ return result.value;
57
+ } else {
58
+ return {
59
+ error: result.reason
60
+ };
52
61
  }
53
- const resourceId = this.writeProvider.writeData(data[index]);
54
- resourceIds.push(resourceId);
55
62
  });
56
- return Promise.all(resourceIds);
57
63
  }
58
64
  async deleteNodesData(resourceIds) {
59
65
  const results = await Promise.allSettled(resourceIds.map(resourceId => this.writeProvider.deleteData(resourceId)));
@@ -1,6 +1,6 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
- import { createSyncBlockNode } from '../utils/createSyncBlock';
3
2
  import { resolveSyncBlockInstance } from '../utils/resolveSyncBlockInstance';
3
+ import { createSyncBlockNode } from '../utils/utils';
4
4
  export class ReferenceSyncBlockStoreManager {
5
5
  constructor(dataProvider) {
6
6
  _defineProperty(this, "isRefreshingSubscriptions", false);
@@ -1,7 +1,7 @@
1
1
  import uuid from 'uuid';
2
2
  import { rebaseTransaction } from '../common/rebase-transaction';
3
3
  import { resourceIdFromSourceAndLocalId } from '../utils/ari';
4
- import { convertSyncBlockPMNodeToSyncBlockData } from '../utils/utils';
4
+ import { convertSyncBlockPMNodeToSyncBlockData, createBodiedSyncBlockNode } from '../utils/utils';
5
5
  export class SourceSyncBlockStoreManager {
6
6
  constructor(dataProvider) {
7
7
  this.dataProvider = dataProvider;
@@ -50,8 +50,8 @@ export class SourceSyncBlockStoreManager {
50
50
  if (bodiedSyncBlockNodes.length === 0) {
51
51
  return Promise.resolve(true);
52
52
  }
53
- const resourceIds = await this.dataProvider.writeNodesData(bodiedSyncBlockNodes, bodiedSyncBlockData);
54
- return resourceIds.every(resourceId => resourceId !== undefined);
53
+ const writeResults = await this.dataProvider.writeNodesData(bodiedSyncBlockNodes, bodiedSyncBlockData);
54
+ return writeResults.every(result => result.resourceId !== undefined);
55
55
  } catch {
56
56
  //TODO: EDITOR-1921 - add error analytics
57
57
  return false;
@@ -60,6 +60,27 @@ export class SourceSyncBlockStoreManager {
60
60
  setEditorView(editorView) {
61
61
  this.editorView = editorView;
62
62
  }
63
+ registerPendingCreation(resourceId) {
64
+ this.pendingResourceId = resourceId;
65
+ }
66
+ registerCreationCallback(callback) {
67
+ this.creationCallback = callback;
68
+ }
69
+
70
+ /**
71
+ * Fires callback to insert node (if creation is successful) and clears pending creation data
72
+ * @param success
73
+ */
74
+ commitPendingCreation(success) {
75
+ if (success && this.creationCallback) {
76
+ this.creationCallback();
77
+ }
78
+ this.pendingResourceId = undefined;
79
+ this.creationCallback = undefined;
80
+ }
81
+ hasPendingCreation() {
82
+ return !!this.pendingResourceId;
83
+ }
63
84
  registerConfirmationCallback(callback) {
64
85
  this.confirmationCallback = callback;
65
86
  return () => {
@@ -69,24 +90,55 @@ export class SourceSyncBlockStoreManager {
69
90
  requireConfirmationBeforeDelete() {
70
91
  return !!this.confirmationCallback;
71
92
  }
72
- createSyncBlockNode() {
93
+ generateBodiedSyncBlockAttrs() {
73
94
  var _this$dataProvider;
74
- const blockInstanceId = uuid();
95
+ const localId = uuid();
75
96
  const sourceId = (_this$dataProvider = this.dataProvider) === null || _this$dataProvider === void 0 ? void 0 : _this$dataProvider.getSourceId();
76
97
  if (!sourceId) {
77
98
  throw new Error('Provider of sync block plugin is not set');
78
99
  }
79
100
 
80
101
  // This should be generated by the data provider implementation as it differs between data providers
81
- const resourceId = resourceIdFromSourceAndLocalId(sourceId, blockInstanceId);
82
- const syncBlockNode = {
83
- attrs: {
102
+ const resourceId = resourceIdFromSourceAndLocalId(sourceId, localId);
103
+ return {
104
+ resourceId,
105
+ localId
106
+ };
107
+ }
108
+ createBodiedSyncBlockNode(attrs) {
109
+ try {
110
+ if (!this.dataProvider) {
111
+ throw new Error('Data provider not set');
112
+ }
113
+ const {
84
114
  resourceId,
85
115
  localId: blockInstanceId
86
- },
87
- type: 'bodiedSyncBlock'
88
- };
89
- return syncBlockNode;
116
+ } = attrs;
117
+ this.dataProvider.writeNodesData([createBodiedSyncBlockNode(blockInstanceId, resourceId)], [{
118
+ content: [],
119
+ blockInstanceId,
120
+ resourceId: resourceId
121
+ }]).then(results => {
122
+ results.forEach(result => {
123
+ const resourceId = result.resourceId;
124
+ if (resourceId) {
125
+ this.commitPendingCreation(true);
126
+ } else {
127
+ this.commitPendingCreation(false);
128
+ // TODO: EDITOR-1921 - add error analytics
129
+ }
130
+ });
131
+ }).catch(_reason => {
132
+ this.commitPendingCreation(false);
133
+ // TODO: EDITOR-1921 - add error analytics
134
+ });
135
+ this.registerPendingCreation(resourceId);
136
+ } catch (error) {
137
+ if (this.hasPendingCreation()) {
138
+ this.commitPendingCreation(false);
139
+ }
140
+ // TODO: EDITOR-1921 - add error analytics
141
+ }
90
142
  }
91
143
  async deleteSyncBlocksWithConfirmation(tr, syncBlockIds) {
92
144
  if (this.confirmationCallback) {
@@ -1,4 +1,4 @@
1
- import { createSyncBlockNode } from '../utils/createSyncBlock';
1
+ import { convertPMNodeToSyncBlockNode } from '../utils/utils';
2
2
  import { ReferenceSyncBlockStoreManager } from './referenceSyncBlockStoreManager';
3
3
  import { SourceSyncBlockStoreManager } from './sourceSyncBlockStoreManager';
4
4
 
@@ -19,11 +19,7 @@ export class SyncBlockStoreManager {
19
19
  * @returns The fetched sync block data results
20
20
  */
21
21
  fetchSyncBlocksData(nodes) {
22
- const syncBlockNodes = nodes.filter(node => {
23
- return node.type.name === 'syncBlock' && node.attrs.resourceId && node.attrs.localId;
24
- }).map(node => {
25
- return createSyncBlockNode(node.attrs.localId, node.attrs.resourceId);
26
- }) || [];
22
+ const syncBlockNodes = nodes.map(node => convertPMNodeToSyncBlockNode(node)).filter(node => node !== undefined) || [];
27
23
  if (syncBlockNodes.length === 0) {
28
24
  return Promise.resolve([]);
29
25
  }
@@ -78,9 +74,36 @@ export class SyncBlockStoreManager {
78
74
  // only applicable to source sync block, for now (will be refactored further)
79
75
  return this.sourceSyncBlockStoreManager.requireConfirmationBeforeDelete();
80
76
  }
81
- createSyncBlockNode() {
77
+
78
+ /**
79
+ * Register callback function (which inserts node, handles focus etc) to be used later when creation to backend succeed
80
+ */
81
+ registerCreationCallback(callback) {
82
+ this.sourceSyncBlockStoreManager.registerCreationCallback(callback);
83
+ }
84
+
85
+ /**
86
+ *
87
+ * @returns true if waiting for the result of saving new bodiedSyncBlock to backend
88
+ */
89
+ hasPendingCreation() {
90
+ return this.sourceSyncBlockStoreManager.hasPendingCreation();
91
+ }
92
+
93
+ /**
94
+ * @returns attributes for a new bodiedSyncBlock node
95
+ */
96
+ generateBodiedSyncBlockAttrs() {
97
+ return this.sourceSyncBlockStoreManager.generateBodiedSyncBlockAttrs();
98
+ }
99
+
100
+ /**
101
+ * Save bodiedSyncBlock with empty content to backend
102
+ * @param attrs attributes Ids of the node
103
+ */
104
+ createBodiedSyncBlockNode(attrs) {
82
105
  // only applicable to source sync block, for now (will be refactored further)
83
- return this.sourceSyncBlockStoreManager.createSyncBlockNode();
106
+ return this.sourceSyncBlockStoreManager.createBodiedSyncBlockNode(attrs);
84
107
  }
85
108
  subscribeToSyncBlockData(node, callback) {
86
109
  return this.referenceSyncBlockStoreManager.subscribe(node, callback);
@@ -0,0 +1,7 @@
1
+ export const stringifyError = error => {
2
+ try {
3
+ return JSON.stringify(error);
4
+ } catch {
5
+ return undefined;
6
+ }
7
+ };