@atlaskit/editor-synced-block-provider 2.14.0 → 2.15.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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # @atlaskit/editor-synced-block-provider
2
2
 
3
+ ## 2.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`4daaa6358e6fb`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/4daaa6358e6fb) -
8
+ Update resourceId generation for create reference from browser copy + pasting source block
9
+
10
+ ### Patch Changes
11
+
12
+ - [`dcc6a3e73f414`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/dcc6a3e73f414) -
13
+ [ux] EDITOR-3304 add offline error state for sync blocks and update other error UI to match new
14
+ designs
15
+ - Updated dependencies
16
+
17
+ ## 2.14.1
18
+
19
+ ### Patch Changes
20
+
21
+ - [`433b512284284`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/433b512284284) -
22
+ [EDITOR-2558] Update bodiedSyncBlock deletion failure flow
23
+ - Updated dependencies
24
+
3
25
  ## 2.14.0
4
26
 
5
27
  ### Minor Changes
@@ -13,6 +13,8 @@ var SyncBlockError = exports.SyncBlockError = /*#__PURE__*/function (SyncBlockEr
13
13
  SyncBlockError["Conflict"] = "conflict";
14
14
  // attempt to create block that already exists
15
15
  SyncBlockError["ServerError"] = "server_error";
16
- SyncBlockError["InvalidContent"] = "invalid_content"; // content is not a valid JSON
16
+ SyncBlockError["InvalidContent"] = "invalid_content";
17
+ // content is not a valid JSON
18
+ SyncBlockError["Offline"] = "offline";
17
19
  return SyncBlockError;
18
20
  }({});
@@ -196,10 +196,11 @@ var BlockServiceADFFetchProvider = /*#__PURE__*/function () {
196
196
  * ADFWriteProvider implementation that writes synced block data to Block Service API
197
197
  */
198
198
  var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
199
- function BlockServiceADFWriteProvider(sourceAri, product) {
199
+ function BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId) {
200
200
  (0, _classCallCheck2.default)(this, BlockServiceADFWriteProvider);
201
201
  this.sourceAri = sourceAri;
202
202
  this.product = product;
203
+ this.sourceDocumentId = sourceDocumentId;
203
204
  }
204
205
 
205
206
  // it will first try to update and if it can't (404) then it will try to create
@@ -352,12 +353,7 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
352
353
  }, {
353
354
  key: "generateResourceIdForReference",
354
355
  value: function generateResourceIdForReference(sourceId) {
355
- var match = this.sourceAri.match(/ari:cloud:confluence:([^:]+):(page|blogpost)\/(\d+)/);
356
- if (!(match !== null && match !== void 0 && match[1])) {
357
- throw new Error("Invalid source ARI: ".concat(this.sourceAri));
358
- }
359
- var pageId = match[3];
360
- return "".concat(this.product, "/").concat(pageId, "/").concat(sourceId);
356
+ return "".concat(this.product, "/").concat(this.sourceDocumentId, "/").concat(sourceId);
361
357
  }
362
358
  }, {
363
359
  key: "generateResourceId",
@@ -369,16 +365,16 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
369
365
  /**
370
366
  * Factory function to create both providers with shared configuration
371
367
  */
372
- var createBlockServiceAPIProviders = function createBlockServiceAPIProviders(sourceAri, product) {
368
+ var createBlockServiceAPIProviders = function createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId) {
373
369
  var fetchProvider = new BlockServiceADFFetchProvider(sourceAri);
374
- var writeProvider = new BlockServiceADFWriteProvider(sourceAri, product);
370
+ var writeProvider = new BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId);
375
371
  return {
376
372
  fetchProvider: fetchProvider,
377
373
  writeProvider: writeProvider
378
374
  };
379
375
  };
380
- var useMemoizedBlockServiceAPIProviders = exports.useMemoizedBlockServiceAPIProviders = function useMemoizedBlockServiceAPIProviders(sourceAri, product) {
376
+ var useMemoizedBlockServiceAPIProviders = exports.useMemoizedBlockServiceAPIProviders = function useMemoizedBlockServiceAPIProviders(sourceAri, product, sourceDocumentId) {
381
377
  return (0, _react.useMemo)(function () {
382
- return createBlockServiceAPIProviders(sourceAri, product);
383
- }, [sourceAri, product]);
378
+ return createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId);
379
+ }, [sourceAri, product, sourceDocumentId]);
384
380
  };
@@ -274,49 +274,40 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
274
274
  }
275
275
  }
276
276
  }, {
277
- key: "deleteSyncBlocksWithConfirmation",
277
+ key: "delete",
278
278
  value: function () {
279
- var _deleteSyncBlocksWithConfirmation = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(syncBlockIds, deleteCallback) {
279
+ var _delete2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(syncBlockIds, onDelete, onDeleteCompleted) {
280
280
  var _this5 = this;
281
- var confirmed, results, callback, _this$fireAnalyticsEv4;
281
+ var results, callback, isDeleteSuccessful, _this$fireAnalyticsEv4;
282
282
  return _regenerator.default.wrap(function _callee2$(_context2) {
283
283
  while (1) switch (_context2.prev = _context2.next) {
284
284
  case 0:
285
- if (!this.confirmationCallback) {
286
- _context2.next = 22;
287
- break;
288
- }
289
- _context2.next = 3;
290
- return this.confirmationCallback(syncBlockIds.length);
291
- case 3:
292
- confirmed = _context2.sent;
293
- if (!confirmed) {
294
- _context2.next = 22;
295
- break;
296
- }
297
- deleteCallback();
298
- _context2.prev = 6;
285
+ _context2.prev = 0;
299
286
  if (this.dataProvider) {
300
- _context2.next = 9;
287
+ _context2.next = 3;
301
288
  break;
302
289
  }
303
290
  throw new Error('Data provider not set');
304
- case 9:
291
+ case 3:
305
292
  syncBlockIds.forEach(function (Ids) {
306
293
  _this5.setPendingDeletion(Ids, true);
307
294
  });
308
- _context2.next = 12;
295
+ _context2.next = 6;
309
296
  return this.dataProvider.deleteNodesData(syncBlockIds.map(function (attrs) {
310
297
  return attrs.resourceId;
311
298
  }));
312
- case 12:
299
+ case 6:
313
300
  results = _context2.sent;
314
- if (results.every(function (result) {
301
+ isDeleteSuccessful = results.every(function (result) {
315
302
  return result.success;
316
- })) {
303
+ });
304
+ onDeleteCompleted(isDeleteSuccessful);
305
+ if (isDeleteSuccessful) {
306
+ onDelete();
317
307
  callback = function callback(Ids) {
318
308
  return _this5.syncBlockCache.delete(Ids.resourceId);
319
309
  };
310
+ this.clearPendingDeletion();
320
311
  } else {
321
312
  callback = function callback(Ids) {
322
313
  _this5.setPendingDeletion(Ids, false);
@@ -329,11 +320,10 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
329
320
  });
330
321
  }
331
322
  syncBlockIds.forEach(callback);
332
- _context2.next = 22;
333
- break;
334
- case 17:
335
- _context2.prev = 17;
336
- _context2.t0 = _context2["catch"](6);
323
+ return _context2.abrupt("return", isDeleteSuccessful);
324
+ case 14:
325
+ _context2.prev = 14;
326
+ _context2.t0 = _context2["catch"](0);
337
327
  syncBlockIds.forEach(function (Ids) {
338
328
  _this5.setPendingDeletion(Ids, false);
339
329
  });
@@ -341,17 +331,119 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
341
331
  location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
342
332
  });
343
333
  (_this$fireAnalyticsEv4 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv4 === void 0 || _this$fireAnalyticsEv4.call(this, (0, _errorHandling.deleteErrorPayload)(_context2.t0.message));
344
- case 22:
334
+ onDeleteCompleted(false);
335
+ return _context2.abrupt("return", false);
336
+ case 21:
345
337
  case "end":
346
338
  return _context2.stop();
347
339
  }
348
- }, _callee2, this, [[6, 17]]);
340
+ }, _callee2, this, [[0, 14]]);
341
+ }));
342
+ function _delete(_x, _x2, _x3) {
343
+ return _delete2.apply(this, arguments);
344
+ }
345
+ return _delete;
346
+ }()
347
+ }, {
348
+ key: "isRetryingDeletion",
349
+ value: function isRetryingDeletion() {
350
+ return !!this.deletionRetryInfo;
351
+ }
352
+ }, {
353
+ key: "retryDeletion",
354
+ value: function () {
355
+ var _retryDeletion = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() {
356
+ var _this$deletionRetryIn, syncBlockIds, onDelete, onDeleteCompleted;
357
+ return _regenerator.default.wrap(function _callee3$(_context3) {
358
+ while (1) switch (_context3.prev = _context3.next) {
359
+ case 0:
360
+ if (this.deletionRetryInfo) {
361
+ _context3.next = 2;
362
+ break;
363
+ }
364
+ return _context3.abrupt("return", Promise.resolve());
365
+ case 2:
366
+ _this$deletionRetryIn = this.deletionRetryInfo, syncBlockIds = _this$deletionRetryIn.syncBlockIds, onDelete = _this$deletionRetryIn.onDelete, onDeleteCompleted = _this$deletionRetryIn.onDeleteCompleted;
367
+ if (!this.confirmationCallback) {
368
+ _context3.next = 6;
369
+ break;
370
+ }
371
+ _context3.next = 6;
372
+ return this.delete(syncBlockIds, onDelete, onDeleteCompleted);
373
+ case 6:
374
+ case "end":
375
+ return _context3.stop();
376
+ }
377
+ }, _callee3, this);
349
378
  }));
350
- function deleteSyncBlocksWithConfirmation(_x, _x2) {
379
+ function retryDeletion() {
380
+ return _retryDeletion.apply(this, arguments);
381
+ }
382
+ return retryDeletion;
383
+ }()
384
+ }, {
385
+ key: "clearPendingDeletion",
386
+ value: function clearPendingDeletion() {
387
+ var _this$deletionRetryIn2;
388
+ (_this$deletionRetryIn2 = this.deletionRetryInfo) === null || _this$deletionRetryIn2 === void 0 || _this$deletionRetryIn2.destroyCallback();
389
+ this.deletionRetryInfo = undefined;
390
+ }
391
+
392
+ /**
393
+ *
394
+ * @param syncBlockIds - The sync block ids to delete
395
+ * @param onDelete - The callback to delete sync block node from document
396
+ * @param onDeleteCompleted - The callback for after the deletion is saved to BE (whether successful or not)
397
+ * @param destroyCallback - The callback to clear any reference stored for deletion (regardless if deletion is completed or abort)
398
+ */
399
+ }, {
400
+ key: "deleteSyncBlocksWithConfirmation",
401
+ value: (function () {
402
+ var _deleteSyncBlocksWithConfirmation = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(syncBlockIds, onDelete, onDeleteCompleted, destroyCallback) {
403
+ var confirmed, isDeleteSuccessful;
404
+ return _regenerator.default.wrap(function _callee4$(_context4) {
405
+ while (1) switch (_context4.prev = _context4.next) {
406
+ case 0:
407
+ if (!this.confirmationCallback) {
408
+ _context4.next = 12;
409
+ break;
410
+ }
411
+ _context4.next = 3;
412
+ return this.confirmationCallback(syncBlockIds.length);
413
+ case 3:
414
+ confirmed = _context4.sent;
415
+ if (!confirmed) {
416
+ _context4.next = 11;
417
+ break;
418
+ }
419
+ _context4.next = 7;
420
+ return this.delete(syncBlockIds, onDelete, onDeleteCompleted);
421
+ case 7:
422
+ isDeleteSuccessful = _context4.sent;
423
+ if (!isDeleteSuccessful) {
424
+ // If deletion failed, save deletion info for potential retry
425
+ this.deletionRetryInfo = {
426
+ syncBlockIds: syncBlockIds,
427
+ onDelete: onDelete,
428
+ onDeleteCompleted: onDeleteCompleted,
429
+ destroyCallback: destroyCallback
430
+ };
431
+ }
432
+ _context4.next = 12;
433
+ break;
434
+ case 11:
435
+ destroyCallback();
436
+ case 12:
437
+ case "end":
438
+ return _context4.stop();
439
+ }
440
+ }, _callee4, this);
441
+ }));
442
+ function deleteSyncBlocksWithConfirmation(_x4, _x5, _x6, _x7) {
351
443
  return _deleteSyncBlocksWithConfirmation.apply(this, arguments);
352
444
  }
353
445
  return deleteSyncBlocksWithConfirmation;
354
- }()
446
+ }())
355
447
  }, {
356
448
  key: "destroy",
357
449
  value: function destroy() {
@@ -360,6 +452,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
360
452
  this.pendingResourceId = undefined;
361
453
  this.creationCallback = undefined;
362
454
  this.dataProvider = undefined;
455
+ this.clearPendingDeletion();
363
456
  }
364
457
  }]);
365
458
  }();
@@ -7,6 +7,8 @@ export let SyncBlockError = /*#__PURE__*/function (SyncBlockError) {
7
7
  SyncBlockError["Conflict"] = "conflict";
8
8
  // attempt to create block that already exists
9
9
  SyncBlockError["ServerError"] = "server_error";
10
- SyncBlockError["InvalidContent"] = "invalid_content"; // content is not a valid JSON
10
+ SyncBlockError["InvalidContent"] = "invalid_content";
11
+ // content is not a valid JSON
12
+ SyncBlockError["Offline"] = "offline";
11
13
  return SyncBlockError;
12
14
  }({});
@@ -134,9 +134,10 @@ class BlockServiceADFFetchProvider {
134
134
  * ADFWriteProvider implementation that writes synced block data to Block Service API
135
135
  */
136
136
  class BlockServiceADFWriteProvider {
137
- constructor(sourceAri, product) {
137
+ constructor(sourceAri, product, sourceDocumentId) {
138
138
  this.sourceAri = sourceAri;
139
139
  this.product = product;
140
+ this.sourceDocumentId = sourceDocumentId;
140
141
  }
141
142
 
142
143
  // it will first try to update and if it can't (404) then it will try to create
@@ -227,12 +228,7 @@ class BlockServiceADFWriteProvider {
227
228
 
228
229
  // the sourceId is the resourceId of the source synced block.
229
230
  generateResourceIdForReference(sourceId) {
230
- const match = this.sourceAri.match(/ari:cloud:confluence:([^:]+):(page|blogpost)\/(\d+)/);
231
- if (!(match !== null && match !== void 0 && match[1])) {
232
- throw new Error(`Invalid source ARI: ${this.sourceAri}`);
233
- }
234
- const pageId = match[3];
235
- return `${this.product}/${pageId}/${sourceId}`;
231
+ return `${this.product}/${this.sourceDocumentId}/${sourceId}`;
236
232
  }
237
233
  generateResourceId() {
238
234
  return crypto.randomUUID();
@@ -242,14 +238,14 @@ class BlockServiceADFWriteProvider {
242
238
  /**
243
239
  * Factory function to create both providers with shared configuration
244
240
  */
245
- const createBlockServiceAPIProviders = (sourceAri, product) => {
241
+ const createBlockServiceAPIProviders = (sourceAri, product, sourceDocumentId) => {
246
242
  const fetchProvider = new BlockServiceADFFetchProvider(sourceAri);
247
- const writeProvider = new BlockServiceADFWriteProvider(sourceAri, product);
243
+ const writeProvider = new BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId);
248
244
  return {
249
245
  fetchProvider,
250
246
  writeProvider
251
247
  };
252
248
  };
253
- export const useMemoizedBlockServiceAPIProviders = (sourceAri, product) => {
254
- return useMemo(() => createBlockServiceAPIProviders(sourceAri, product), [sourceAri, product]);
249
+ export const useMemoizedBlockServiceAPIProviders = (sourceAri, product, sourceDocumentId) => {
250
+ return useMemo(() => createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId), [sourceAri, product, sourceDocumentId]);
255
251
  };
@@ -205,42 +205,91 @@ export class SourceSyncBlockStoreManager {
205
205
  (_this$fireAnalyticsEv6 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv6 === void 0 ? void 0 : _this$fireAnalyticsEv6.call(this, createErrorPayload(error.message));
206
206
  }
207
207
  }
208
- async deleteSyncBlocksWithConfirmation(syncBlockIds, deleteCallback) {
208
+ async delete(syncBlockIds, onDelete, onDeleteCompleted) {
209
+ try {
210
+ if (!this.dataProvider) {
211
+ throw new Error('Data provider not set');
212
+ }
213
+ syncBlockIds.forEach(Ids => {
214
+ this.setPendingDeletion(Ids, true);
215
+ });
216
+ const results = await this.dataProvider.deleteNodesData(syncBlockIds.map(attrs => attrs.resourceId));
217
+ let callback;
218
+ const isDeleteSuccessful = results.every(result => result.success);
219
+ onDeleteCompleted(isDeleteSuccessful);
220
+ if (isDeleteSuccessful) {
221
+ onDelete();
222
+ callback = Ids => this.syncBlockCache.delete(Ids.resourceId);
223
+ this.clearPendingDeletion();
224
+ } else {
225
+ callback = Ids => {
226
+ this.setPendingDeletion(Ids, false);
227
+ };
228
+ results.filter(result => result.resourceId === undefined).forEach(result => {
229
+ var _this$fireAnalyticsEv7;
230
+ (_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 ? void 0 : _this$fireAnalyticsEv7.call(this, deleteErrorPayload(result.error || 'Failed to delete synced block'));
231
+ });
232
+ }
233
+ syncBlockIds.forEach(callback);
234
+ return isDeleteSuccessful;
235
+ } catch (error) {
236
+ var _this$fireAnalyticsEv8;
237
+ syncBlockIds.forEach(Ids => {
238
+ this.setPendingDeletion(Ids, false);
239
+ });
240
+ logException(error, {
241
+ location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
242
+ });
243
+ (_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 ? void 0 : _this$fireAnalyticsEv8.call(this, deleteErrorPayload(error.message));
244
+ onDeleteCompleted(false);
245
+ return false;
246
+ }
247
+ }
248
+ isRetryingDeletion() {
249
+ return !!this.deletionRetryInfo;
250
+ }
251
+ async retryDeletion() {
252
+ if (!this.deletionRetryInfo) {
253
+ return Promise.resolve();
254
+ }
255
+ const {
256
+ syncBlockIds,
257
+ onDelete,
258
+ onDeleteCompleted
259
+ } = this.deletionRetryInfo;
260
+ if (this.confirmationCallback) {
261
+ await this.delete(syncBlockIds, onDelete, onDeleteCompleted);
262
+ }
263
+ }
264
+ clearPendingDeletion() {
265
+ var _this$deletionRetryIn;
266
+ (_this$deletionRetryIn = this.deletionRetryInfo) === null || _this$deletionRetryIn === void 0 ? void 0 : _this$deletionRetryIn.destroyCallback();
267
+ this.deletionRetryInfo = undefined;
268
+ }
269
+
270
+ /**
271
+ *
272
+ * @param syncBlockIds - The sync block ids to delete
273
+ * @param onDelete - The callback to delete sync block node from document
274
+ * @param onDeleteCompleted - The callback for after the deletion is saved to BE (whether successful or not)
275
+ * @param destroyCallback - The callback to clear any reference stored for deletion (regardless if deletion is completed or abort)
276
+ */
277
+ async deleteSyncBlocksWithConfirmation(syncBlockIds, onDelete, onDeleteCompleted, destroyCallback) {
209
278
  if (this.confirmationCallback) {
210
279
  const confirmed = await this.confirmationCallback(syncBlockIds.length);
211
280
  if (confirmed) {
212
- deleteCallback();
213
- try {
214
- if (!this.dataProvider) {
215
- throw new Error('Data provider not set');
216
- }
217
- syncBlockIds.forEach(Ids => {
218
- this.setPendingDeletion(Ids, true);
219
- });
220
- const results = await this.dataProvider.deleteNodesData(syncBlockIds.map(attrs => attrs.resourceId));
221
- let callback;
222
- if (results.every(result => result.success)) {
223
- callback = Ids => this.syncBlockCache.delete(Ids.resourceId);
224
- } else {
225
- callback = Ids => {
226
- this.setPendingDeletion(Ids, false);
227
- };
228
- results.filter(result => result.resourceId === undefined).forEach(result => {
229
- var _this$fireAnalyticsEv7;
230
- (_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 ? void 0 : _this$fireAnalyticsEv7.call(this, deleteErrorPayload(result.error || 'Failed to delete synced block'));
231
- });
232
- }
233
- syncBlockIds.forEach(callback);
234
- } catch (error) {
235
- var _this$fireAnalyticsEv8;
236
- syncBlockIds.forEach(Ids => {
237
- this.setPendingDeletion(Ids, false);
238
- });
239
- logException(error, {
240
- location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
241
- });
242
- (_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 ? void 0 : _this$fireAnalyticsEv8.call(this, deleteErrorPayload(error.message));
281
+ const isDeleteSuccessful = await this.delete(syncBlockIds, onDelete, onDeleteCompleted);
282
+ if (!isDeleteSuccessful) {
283
+ // If deletion failed, save deletion info for potential retry
284
+ this.deletionRetryInfo = {
285
+ syncBlockIds,
286
+ onDelete,
287
+ onDeleteCompleted,
288
+ destroyCallback
289
+ };
243
290
  }
291
+ } else {
292
+ destroyCallback();
244
293
  }
245
294
  }
246
295
  }
@@ -250,5 +299,6 @@ export class SourceSyncBlockStoreManager {
250
299
  this.pendingResourceId = undefined;
251
300
  this.creationCallback = undefined;
252
301
  this.dataProvider = undefined;
302
+ this.clearPendingDeletion();
253
303
  }
254
304
  }
@@ -7,6 +7,8 @@ export var SyncBlockError = /*#__PURE__*/function (SyncBlockError) {
7
7
  SyncBlockError["Conflict"] = "conflict";
8
8
  // attempt to create block that already exists
9
9
  SyncBlockError["ServerError"] = "server_error";
10
- SyncBlockError["InvalidContent"] = "invalid_content"; // content is not a valid JSON
10
+ SyncBlockError["InvalidContent"] = "invalid_content";
11
+ // content is not a valid JSON
12
+ SyncBlockError["Offline"] = "offline";
11
13
  return SyncBlockError;
12
14
  }({});
@@ -188,10 +188,11 @@ var BlockServiceADFFetchProvider = /*#__PURE__*/function () {
188
188
  * ADFWriteProvider implementation that writes synced block data to Block Service API
189
189
  */
190
190
  var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
191
- function BlockServiceADFWriteProvider(sourceAri, product) {
191
+ function BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId) {
192
192
  _classCallCheck(this, BlockServiceADFWriteProvider);
193
193
  this.sourceAri = sourceAri;
194
194
  this.product = product;
195
+ this.sourceDocumentId = sourceDocumentId;
195
196
  }
196
197
 
197
198
  // it will first try to update and if it can't (404) then it will try to create
@@ -344,12 +345,7 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
344
345
  }, {
345
346
  key: "generateResourceIdForReference",
346
347
  value: function generateResourceIdForReference(sourceId) {
347
- var match = this.sourceAri.match(/ari:cloud:confluence:([^:]+):(page|blogpost)\/(\d+)/);
348
- if (!(match !== null && match !== void 0 && match[1])) {
349
- throw new Error("Invalid source ARI: ".concat(this.sourceAri));
350
- }
351
- var pageId = match[3];
352
- return "".concat(this.product, "/").concat(pageId, "/").concat(sourceId);
348
+ return "".concat(this.product, "/").concat(this.sourceDocumentId, "/").concat(sourceId);
353
349
  }
354
350
  }, {
355
351
  key: "generateResourceId",
@@ -361,16 +357,16 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
361
357
  /**
362
358
  * Factory function to create both providers with shared configuration
363
359
  */
364
- var createBlockServiceAPIProviders = function createBlockServiceAPIProviders(sourceAri, product) {
360
+ var createBlockServiceAPIProviders = function createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId) {
365
361
  var fetchProvider = new BlockServiceADFFetchProvider(sourceAri);
366
- var writeProvider = new BlockServiceADFWriteProvider(sourceAri, product);
362
+ var writeProvider = new BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId);
367
363
  return {
368
364
  fetchProvider: fetchProvider,
369
365
  writeProvider: writeProvider
370
366
  };
371
367
  };
372
- export var useMemoizedBlockServiceAPIProviders = function useMemoizedBlockServiceAPIProviders(sourceAri, product) {
368
+ export var useMemoizedBlockServiceAPIProviders = function useMemoizedBlockServiceAPIProviders(sourceAri, product, sourceDocumentId) {
373
369
  return useMemo(function () {
374
- return createBlockServiceAPIProviders(sourceAri, product);
375
- }, [sourceAri, product]);
370
+ return createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId);
371
+ }, [sourceAri, product, sourceDocumentId]);
376
372
  };
@@ -267,49 +267,40 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
267
267
  }
268
268
  }
269
269
  }, {
270
- key: "deleteSyncBlocksWithConfirmation",
270
+ key: "delete",
271
271
  value: function () {
272
- var _deleteSyncBlocksWithConfirmation = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(syncBlockIds, deleteCallback) {
272
+ var _delete2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(syncBlockIds, onDelete, onDeleteCompleted) {
273
273
  var _this5 = this;
274
- var confirmed, results, callback, _this$fireAnalyticsEv4;
274
+ var results, callback, isDeleteSuccessful, _this$fireAnalyticsEv4;
275
275
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
276
276
  while (1) switch (_context2.prev = _context2.next) {
277
277
  case 0:
278
- if (!this.confirmationCallback) {
279
- _context2.next = 22;
280
- break;
281
- }
282
- _context2.next = 3;
283
- return this.confirmationCallback(syncBlockIds.length);
284
- case 3:
285
- confirmed = _context2.sent;
286
- if (!confirmed) {
287
- _context2.next = 22;
288
- break;
289
- }
290
- deleteCallback();
291
- _context2.prev = 6;
278
+ _context2.prev = 0;
292
279
  if (this.dataProvider) {
293
- _context2.next = 9;
280
+ _context2.next = 3;
294
281
  break;
295
282
  }
296
283
  throw new Error('Data provider not set');
297
- case 9:
284
+ case 3:
298
285
  syncBlockIds.forEach(function (Ids) {
299
286
  _this5.setPendingDeletion(Ids, true);
300
287
  });
301
- _context2.next = 12;
288
+ _context2.next = 6;
302
289
  return this.dataProvider.deleteNodesData(syncBlockIds.map(function (attrs) {
303
290
  return attrs.resourceId;
304
291
  }));
305
- case 12:
292
+ case 6:
306
293
  results = _context2.sent;
307
- if (results.every(function (result) {
294
+ isDeleteSuccessful = results.every(function (result) {
308
295
  return result.success;
309
- })) {
296
+ });
297
+ onDeleteCompleted(isDeleteSuccessful);
298
+ if (isDeleteSuccessful) {
299
+ onDelete();
310
300
  callback = function callback(Ids) {
311
301
  return _this5.syncBlockCache.delete(Ids.resourceId);
312
302
  };
303
+ this.clearPendingDeletion();
313
304
  } else {
314
305
  callback = function callback(Ids) {
315
306
  _this5.setPendingDeletion(Ids, false);
@@ -322,11 +313,10 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
322
313
  });
323
314
  }
324
315
  syncBlockIds.forEach(callback);
325
- _context2.next = 22;
326
- break;
327
- case 17:
328
- _context2.prev = 17;
329
- _context2.t0 = _context2["catch"](6);
316
+ return _context2.abrupt("return", isDeleteSuccessful);
317
+ case 14:
318
+ _context2.prev = 14;
319
+ _context2.t0 = _context2["catch"](0);
330
320
  syncBlockIds.forEach(function (Ids) {
331
321
  _this5.setPendingDeletion(Ids, false);
332
322
  });
@@ -334,17 +324,119 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
334
324
  location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
335
325
  });
336
326
  (_this$fireAnalyticsEv4 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv4 === void 0 || _this$fireAnalyticsEv4.call(this, deleteErrorPayload(_context2.t0.message));
337
- case 22:
327
+ onDeleteCompleted(false);
328
+ return _context2.abrupt("return", false);
329
+ case 21:
338
330
  case "end":
339
331
  return _context2.stop();
340
332
  }
341
- }, _callee2, this, [[6, 17]]);
333
+ }, _callee2, this, [[0, 14]]);
334
+ }));
335
+ function _delete(_x, _x2, _x3) {
336
+ return _delete2.apply(this, arguments);
337
+ }
338
+ return _delete;
339
+ }()
340
+ }, {
341
+ key: "isRetryingDeletion",
342
+ value: function isRetryingDeletion() {
343
+ return !!this.deletionRetryInfo;
344
+ }
345
+ }, {
346
+ key: "retryDeletion",
347
+ value: function () {
348
+ var _retryDeletion = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
349
+ var _this$deletionRetryIn, syncBlockIds, onDelete, onDeleteCompleted;
350
+ return _regeneratorRuntime.wrap(function _callee3$(_context3) {
351
+ while (1) switch (_context3.prev = _context3.next) {
352
+ case 0:
353
+ if (this.deletionRetryInfo) {
354
+ _context3.next = 2;
355
+ break;
356
+ }
357
+ return _context3.abrupt("return", Promise.resolve());
358
+ case 2:
359
+ _this$deletionRetryIn = this.deletionRetryInfo, syncBlockIds = _this$deletionRetryIn.syncBlockIds, onDelete = _this$deletionRetryIn.onDelete, onDeleteCompleted = _this$deletionRetryIn.onDeleteCompleted;
360
+ if (!this.confirmationCallback) {
361
+ _context3.next = 6;
362
+ break;
363
+ }
364
+ _context3.next = 6;
365
+ return this.delete(syncBlockIds, onDelete, onDeleteCompleted);
366
+ case 6:
367
+ case "end":
368
+ return _context3.stop();
369
+ }
370
+ }, _callee3, this);
342
371
  }));
343
- function deleteSyncBlocksWithConfirmation(_x, _x2) {
372
+ function retryDeletion() {
373
+ return _retryDeletion.apply(this, arguments);
374
+ }
375
+ return retryDeletion;
376
+ }()
377
+ }, {
378
+ key: "clearPendingDeletion",
379
+ value: function clearPendingDeletion() {
380
+ var _this$deletionRetryIn2;
381
+ (_this$deletionRetryIn2 = this.deletionRetryInfo) === null || _this$deletionRetryIn2 === void 0 || _this$deletionRetryIn2.destroyCallback();
382
+ this.deletionRetryInfo = undefined;
383
+ }
384
+
385
+ /**
386
+ *
387
+ * @param syncBlockIds - The sync block ids to delete
388
+ * @param onDelete - The callback to delete sync block node from document
389
+ * @param onDeleteCompleted - The callback for after the deletion is saved to BE (whether successful or not)
390
+ * @param destroyCallback - The callback to clear any reference stored for deletion (regardless if deletion is completed or abort)
391
+ */
392
+ }, {
393
+ key: "deleteSyncBlocksWithConfirmation",
394
+ value: (function () {
395
+ var _deleteSyncBlocksWithConfirmation = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(syncBlockIds, onDelete, onDeleteCompleted, destroyCallback) {
396
+ var confirmed, isDeleteSuccessful;
397
+ return _regeneratorRuntime.wrap(function _callee4$(_context4) {
398
+ while (1) switch (_context4.prev = _context4.next) {
399
+ case 0:
400
+ if (!this.confirmationCallback) {
401
+ _context4.next = 12;
402
+ break;
403
+ }
404
+ _context4.next = 3;
405
+ return this.confirmationCallback(syncBlockIds.length);
406
+ case 3:
407
+ confirmed = _context4.sent;
408
+ if (!confirmed) {
409
+ _context4.next = 11;
410
+ break;
411
+ }
412
+ _context4.next = 7;
413
+ return this.delete(syncBlockIds, onDelete, onDeleteCompleted);
414
+ case 7:
415
+ isDeleteSuccessful = _context4.sent;
416
+ if (!isDeleteSuccessful) {
417
+ // If deletion failed, save deletion info for potential retry
418
+ this.deletionRetryInfo = {
419
+ syncBlockIds: syncBlockIds,
420
+ onDelete: onDelete,
421
+ onDeleteCompleted: onDeleteCompleted,
422
+ destroyCallback: destroyCallback
423
+ };
424
+ }
425
+ _context4.next = 12;
426
+ break;
427
+ case 11:
428
+ destroyCallback();
429
+ case 12:
430
+ case "end":
431
+ return _context4.stop();
432
+ }
433
+ }, _callee4, this);
434
+ }));
435
+ function deleteSyncBlocksWithConfirmation(_x4, _x5, _x6, _x7) {
344
436
  return _deleteSyncBlocksWithConfirmation.apply(this, arguments);
345
437
  }
346
438
  return deleteSyncBlocksWithConfirmation;
347
- }()
439
+ }())
348
440
  }, {
349
441
  key: "destroy",
350
442
  value: function destroy() {
@@ -353,6 +445,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
353
445
  this.pendingResourceId = undefined;
354
446
  this.creationCallback = undefined;
355
447
  this.dataProvider = undefined;
448
+ this.clearPendingDeletion();
356
449
  }
357
450
  }]);
358
451
  }();
@@ -20,7 +20,8 @@ export declare enum SyncBlockError {
20
20
  RateLimited = "rate_limited",
21
21
  Conflict = "conflict",// attempt to create block that already exists
22
22
  ServerError = "server_error",
23
- InvalidContent = "invalid_content"
23
+ InvalidContent = "invalid_content",// content is not a valid JSON
24
+ Offline = "offline"
24
25
  }
25
26
  export interface SyncBlockData {
26
27
  blockInstanceId: BlockInstanceId;
@@ -14,15 +14,16 @@ declare class BlockServiceADFFetchProvider implements ADFFetchProvider {
14
14
  */
15
15
  declare class BlockServiceADFWriteProvider implements ADFWriteProvider {
16
16
  private sourceAri;
17
+ private sourceDocumentId;
17
18
  product: SyncBlockProduct;
18
- constructor(sourceAri: string, product: SyncBlockProduct);
19
+ constructor(sourceAri: string, product: SyncBlockProduct, sourceDocumentId: string);
19
20
  writeData(data: SyncBlockData): Promise<WriteSyncBlockResult>;
20
21
  createData(data: SyncBlockData): Promise<WriteSyncBlockResult>;
21
22
  deleteData(resourceId: string): Promise<DeleteSyncBlockResult>;
22
23
  generateResourceIdForReference(sourceId: ResourceId): ResourceId;
23
24
  generateResourceId(): ResourceId;
24
25
  }
25
- export declare const useMemoizedBlockServiceAPIProviders: (sourceAri: string, product: SyncBlockProduct) => {
26
+ export declare const useMemoizedBlockServiceAPIProviders: (sourceAri: string, product: SyncBlockProduct, sourceDocumentId: string) => {
26
27
  fetchProvider: BlockServiceADFFetchProvider;
27
28
  writeProvider: BlockServiceADFWriteProvider;
28
29
  };
@@ -3,12 +3,16 @@ import { type Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
3
  import type { ResourceId, SyncBlockAttrs } from '../common/types';
4
4
  import type { SyncBlockDataProvider } from '../providers/types';
5
5
  export type ConfirmationCallback = (syncBlockCount: number) => Promise<boolean>;
6
+ type OnDelete = () => void;
7
+ type OnDeleteCompleted = (success: boolean) => void;
8
+ type DestroyCallback = () => void;
6
9
  export type CreationCallback = () => void;
7
10
  export declare class SourceSyncBlockStoreManager {
8
11
  private dataProvider?;
9
12
  private fireAnalyticsEvent?;
10
13
  private syncBlockCache;
11
14
  private confirmationCallback?;
15
+ private deletionRetryInfo?;
12
16
  private pendingResourceId?;
13
17
  private creationCallback?;
14
18
  constructor(dataProvider?: SyncBlockDataProvider, fireAnalyticsEvent?: (payload: SyncBlockEventPayload) => void);
@@ -51,6 +55,18 @@ export declare class SourceSyncBlockStoreManager {
51
55
  */
52
56
  createBodiedSyncBlockNode(attrs: SyncBlockAttrs): void;
53
57
  private setPendingDeletion;
54
- deleteSyncBlocksWithConfirmation(syncBlockIds: SyncBlockAttrs[], deleteCallback: () => void): Promise<void>;
58
+ private delete;
59
+ isRetryingDeletion(): boolean;
60
+ retryDeletion(): Promise<void>;
61
+ clearPendingDeletion(): void;
62
+ /**
63
+ *
64
+ * @param syncBlockIds - The sync block ids to delete
65
+ * @param onDelete - The callback to delete sync block node from document
66
+ * @param onDeleteCompleted - The callback for after the deletion is saved to BE (whether successful or not)
67
+ * @param destroyCallback - The callback to clear any reference stored for deletion (regardless if deletion is completed or abort)
68
+ */
69
+ deleteSyncBlocksWithConfirmation(syncBlockIds: SyncBlockAttrs[], onDelete: OnDelete, onDeleteCompleted: OnDeleteCompleted, destroyCallback: DestroyCallback): Promise<void>;
55
70
  destroy(): void;
56
71
  }
72
+ export {};
@@ -20,7 +20,8 @@ export declare enum SyncBlockError {
20
20
  RateLimited = "rate_limited",
21
21
  Conflict = "conflict",// attempt to create block that already exists
22
22
  ServerError = "server_error",
23
- InvalidContent = "invalid_content"
23
+ InvalidContent = "invalid_content",// content is not a valid JSON
24
+ Offline = "offline"
24
25
  }
25
26
  export interface SyncBlockData {
26
27
  blockInstanceId: BlockInstanceId;
@@ -14,15 +14,16 @@ declare class BlockServiceADFFetchProvider implements ADFFetchProvider {
14
14
  */
15
15
  declare class BlockServiceADFWriteProvider implements ADFWriteProvider {
16
16
  private sourceAri;
17
+ private sourceDocumentId;
17
18
  product: SyncBlockProduct;
18
- constructor(sourceAri: string, product: SyncBlockProduct);
19
+ constructor(sourceAri: string, product: SyncBlockProduct, sourceDocumentId: string);
19
20
  writeData(data: SyncBlockData): Promise<WriteSyncBlockResult>;
20
21
  createData(data: SyncBlockData): Promise<WriteSyncBlockResult>;
21
22
  deleteData(resourceId: string): Promise<DeleteSyncBlockResult>;
22
23
  generateResourceIdForReference(sourceId: ResourceId): ResourceId;
23
24
  generateResourceId(): ResourceId;
24
25
  }
25
- export declare const useMemoizedBlockServiceAPIProviders: (sourceAri: string, product: SyncBlockProduct) => {
26
+ export declare const useMemoizedBlockServiceAPIProviders: (sourceAri: string, product: SyncBlockProduct, sourceDocumentId: string) => {
26
27
  fetchProvider: BlockServiceADFFetchProvider;
27
28
  writeProvider: BlockServiceADFWriteProvider;
28
29
  };
@@ -3,12 +3,16 @@ import { type Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
3
  import type { ResourceId, SyncBlockAttrs } from '../common/types';
4
4
  import type { SyncBlockDataProvider } from '../providers/types';
5
5
  export type ConfirmationCallback = (syncBlockCount: number) => Promise<boolean>;
6
+ type OnDelete = () => void;
7
+ type OnDeleteCompleted = (success: boolean) => void;
8
+ type DestroyCallback = () => void;
6
9
  export type CreationCallback = () => void;
7
10
  export declare class SourceSyncBlockStoreManager {
8
11
  private dataProvider?;
9
12
  private fireAnalyticsEvent?;
10
13
  private syncBlockCache;
11
14
  private confirmationCallback?;
15
+ private deletionRetryInfo?;
12
16
  private pendingResourceId?;
13
17
  private creationCallback?;
14
18
  constructor(dataProvider?: SyncBlockDataProvider, fireAnalyticsEvent?: (payload: SyncBlockEventPayload) => void);
@@ -51,6 +55,18 @@ export declare class SourceSyncBlockStoreManager {
51
55
  */
52
56
  createBodiedSyncBlockNode(attrs: SyncBlockAttrs): void;
53
57
  private setPendingDeletion;
54
- deleteSyncBlocksWithConfirmation(syncBlockIds: SyncBlockAttrs[], deleteCallback: () => void): Promise<void>;
58
+ private delete;
59
+ isRetryingDeletion(): boolean;
60
+ retryDeletion(): Promise<void>;
61
+ clearPendingDeletion(): void;
62
+ /**
63
+ *
64
+ * @param syncBlockIds - The sync block ids to delete
65
+ * @param onDelete - The callback to delete sync block node from document
66
+ * @param onDeleteCompleted - The callback for after the deletion is saved to BE (whether successful or not)
67
+ * @param destroyCallback - The callback to clear any reference stored for deletion (regardless if deletion is completed or abort)
68
+ */
69
+ deleteSyncBlocksWithConfirmation(syncBlockIds: SyncBlockAttrs[], onDelete: OnDelete, onDeleteCompleted: OnDeleteCompleted, destroyCallback: DestroyCallback): Promise<void>;
55
70
  destroy(): void;
56
71
  }
72
+ export {};
package/package.json CHANGED
@@ -38,7 +38,7 @@
38
38
  "react": "^18.2.0"
39
39
  },
40
40
  "devDependencies": {
41
- "@testing-library/react": "^13.4.0",
41
+ "@testing-library/react": "^16.3.0",
42
42
  "react-dom": "^18.2.0"
43
43
  },
44
44
  "techstack": {
@@ -77,7 +77,7 @@
77
77
  }
78
78
  },
79
79
  "name": "@atlaskit/editor-synced-block-provider",
80
- "version": "2.14.0",
80
+ "version": "2.15.0",
81
81
  "description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
82
82
  "author": "Atlassian Pty Ltd",
83
83
  "license": "Apache-2.0",