@atlaskit/editor-synced-block-provider 12.0.6 → 12.1.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,23 @@
1
1
  # @atlaskit/editor-synced-block-provider
2
2
 
3
+ ## 12.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`90712d67e0e35`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/90712d67e0e35) -
8
+ Add APIs and renderer support for unpublished same-page synced block references behind
9
+ editor-synced-block-same-page-sync.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+
15
+ ## 12.0.7
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies
20
+
3
21
  ## 12.0.6
4
22
 
5
23
  ### Patch Changes
@@ -8,14 +8,17 @@ exports.ReferenceSyncBlockStoreManager = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
10
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
11
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
11
12
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
12
13
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
13
14
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
14
15
  var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
15
16
  var _monitoring = require("@atlaskit/editor-common/monitoring");
17
+ var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
16
18
  var _types = require("../common/types");
17
19
  var _errorHandling = require("../utils/errorHandling");
18
20
  var _experienceTracking = require("../utils/experienceTracking");
21
+ var _resourceId = require("../utils/resourceId");
19
22
  var _resolveSyncBlockInstance = require("../utils/resolveSyncBlockInstance");
20
23
  var _utils = require("../utils/utils");
21
24
  var _syncBlockBatchFetcher = require("./syncBlockBatchFetcher");
@@ -44,7 +47,7 @@ var CACHE_DELETION_MAX_RESCHEDULES = 10;
44
47
  // Handles fetching source URL and title for sync blocks.
45
48
  // Can be used in both editor and renderer contexts.
46
49
  var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
47
- function ReferenceSyncBlockStoreManager(dataProvider, viewMode) {
50
+ function ReferenceSyncBlockStoreManager(dataProvider, viewMode, sourceManager) {
48
51
  var _this = this,
49
52
  _this$dataProvider;
50
53
  (0, _classCallCheck2.default)(this, ReferenceSyncBlockStoreManager);
@@ -69,6 +72,7 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
69
72
  (0, _defineProperty2.default)(this, "isDestroyed", false);
70
73
  this.dataProvider = dataProvider;
71
74
  this.viewMode = viewMode;
75
+ this.sourceManager = sourceManager;
72
76
  this.syncBlockFetchDataRequests = new Map();
73
77
  this.syncBlockSourceInfoRequests = new Map();
74
78
  this.newlyAddedSyncBlocks = new Set();
@@ -245,6 +249,10 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
245
249
  key: "getInitialSyncBlockData",
246
250
  value: function getInitialSyncBlockData(resourceId) {
247
251
  var _this$dataProvider2;
252
+ var localReference = this.getLocalReference(resourceId);
253
+ if (localReference) {
254
+ return localReference;
255
+ }
248
256
  var syncBlockNode = (0, _utils.createSyncBlockNode)('', resourceId);
249
257
  var providerData = (_this$dataProvider2 = this.dataProvider) === null || _this$dataProvider2 === void 0 || (_this$dataProvider2 = _this$dataProvider2.getNodeDataFromCache(syncBlockNode)) === null || _this$dataProvider2 === void 0 ? void 0 : _this$dataProvider2.data;
250
258
  if (providerData) {
@@ -254,6 +262,41 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
254
262
  }
255
263
  return this.getFromSessionCache(resourceId);
256
264
  }
265
+ }, {
266
+ key: "getSameDocumentReferenceParts",
267
+ value: function getSameDocumentReferenceParts(resourceId) {
268
+ var parsed = (0, _resourceId.parseResourceId)(resourceId);
269
+ if (!parsed || !this.dataProvider || this.dataProvider.generateResourceIdForReference(parsed.uuid) !== resourceId) {
270
+ return undefined;
271
+ }
272
+ return parsed;
273
+ }
274
+ }, {
275
+ key: "getLocalReference",
276
+ value: function getLocalReference(resourceId) {
277
+ var _this$sourceManager;
278
+ if (!(0, _isExperimentEnabled.isExperimentEnabled)('editor-synced-block-same-page-sync')) {
279
+ return undefined;
280
+ }
281
+ var parsed = this.getSameDocumentReferenceParts(resourceId);
282
+ if (!parsed) {
283
+ return undefined;
284
+ }
285
+ var snapshot = (_this$sourceManager = this.sourceManager) === null || _this$sourceManager === void 0 ? void 0 : _this$sourceManager.getLocalSourceSnapshot(parsed.uuid);
286
+ if (!snapshot) {
287
+ return undefined;
288
+ }
289
+ return {
290
+ resourceId: resourceId,
291
+ data: _objectSpread(_objectSpread({}, snapshot), {}, {
292
+ content: (0, _toConsumableArray2.default)(snapshot.content)
293
+ }),
294
+ localSameDocumentSource: {
295
+ sourceBlockInstanceId: snapshot.blockInstanceId,
296
+ sourceProduct: parsed.product
297
+ }
298
+ };
299
+ }
257
300
  }, {
258
301
  key: "normalizeReferenceData",
259
302
  value: function normalizeReferenceData(syncBlock) {
@@ -921,7 +964,35 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
921
964
  }, {
922
965
  key: "subscribeToSyncBlock",
923
966
  value: function subscribeToSyncBlock(resourceId, localId, callback) {
924
- return this._subscriptionManager.subscribeToSyncBlock(resourceId, localId, callback);
967
+ var _this$sourceManager2,
968
+ _this9 = this;
969
+ var isSamePageSyncEnabled = (0, _isExperimentEnabled.isExperimentEnabled)('editor-synced-block-same-page-sync');
970
+ var sameDocumentParts = isSamePageSyncEnabled ? this.getSameDocumentReferenceParts(resourceId) : undefined;
971
+ var localReference = isSamePageSyncEnabled ? this.getLocalReference(resourceId) : undefined;
972
+ var unsubscribeRemote = this._subscriptionManager.subscribeToSyncBlock(resourceId, localId, function (instance) {
973
+ if (!localReference) {
974
+ callback(instance);
975
+ }
976
+ });
977
+ var unsubscribeLocal = sameDocumentParts ? (_this$sourceManager2 = this.sourceManager) === null || _this$sourceManager2 === void 0 ? void 0 : _this$sourceManager2.subscribeToLocalSource(sameDocumentParts.uuid, function () {
978
+ var hadLocalReference = Boolean(localReference);
979
+ localReference = _this9.getLocalReference(resourceId);
980
+ if (localReference) {
981
+ callback(localReference);
982
+ return;
983
+ }
984
+ if (!hadLocalReference) {
985
+ return;
986
+ }
987
+ // Source left the document: keep the last local instance on screen
988
+ // and fetch the current backend value. Do not replay a remote
989
+ // value captured while the local source was authoritative.
990
+ _this9.debouncedBatchedFetchSyncBlocks(resourceId);
991
+ }) : undefined;
992
+ return function () {
993
+ unsubscribeLocal === null || unsubscribeLocal === void 0 || unsubscribeLocal();
994
+ unsubscribeRemote();
995
+ };
925
996
  }
926
997
  }, {
927
998
  key: "subscribeToSourceTitle",
@@ -942,7 +1013,7 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
942
1013
  if (!localId || !resourceId) {
943
1014
  throw new Error('Missing local id or resource id');
944
1015
  }
945
- return this._subscriptionManager.subscribeToSyncBlock(resourceId, localId, callback);
1016
+ return this.subscribeToSyncBlock(resourceId, localId, callback);
946
1017
  } catch (error) {
947
1018
  var _this$fireAnalyticsEv7;
948
1019
  // EDITOR-7860: benign not-ready/torn-down case — suppress both the
@@ -1010,7 +1081,7 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
1010
1081
  key: "flush",
1011
1082
  value: (function () {
1012
1083
  var _flush = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() {
1013
- var _this9 = this;
1084
+ var _this0 = this;
1014
1085
  var success, syncedBlocksToFlush, _this$saveExperience, blocks, _iterator, _step, _loop, updateResult, _this$saveExperience2, _this$fireAnalyticsEv8, _this$saveExperience3, _this$fireAnalyticsEv9, _this$saveExperience4, _t2, _t3;
1015
1086
  return _regenerator.default.wrap(function (_context4) {
1016
1087
  while (1) switch (_context4.prev = _context4.next) {
@@ -1154,8 +1225,8 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
1154
1225
  // Use setTimeout to avoid deep recursion and run queued flush asynchronously
1155
1226
  // Note: flush() handles all exceptions internally and never rejects
1156
1227
  this.queuedFlushTimeout = setTimeout(function () {
1157
- _this9.queuedFlushTimeout = undefined;
1158
- void _this9.flush();
1228
+ _this0.queuedFlushTimeout = undefined;
1229
+ void _this0.flush();
1159
1230
  }, 0);
1160
1231
  }
1161
1232
  return _context4.finish(17);
@@ -5,18 +5,22 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.SourceSyncBlockStoreManager = void 0;
8
- var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
8
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
10
+ var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
11
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
10
12
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
11
13
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
12
14
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
13
15
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
14
16
  var _monitoring = require("@atlaskit/editor-common/monitoring");
17
+ var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
15
18
  var _fg = require("@atlaskit/platform-feature-flags/fg");
16
19
  var _types = require("../common/types");
17
20
  var _errorHandling = require("../utils/errorHandling");
18
21
  var _experienceTracking = require("../utils/experienceTracking");
19
22
  var _utils = require("../utils/utils");
23
+ var _excluded = ["contentFragment", "isDirty"];
20
24
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
21
25
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
22
26
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
@@ -42,6 +46,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
42
46
  function SourceSyncBlockStoreManager(dataProvider, viewMode, isLivePage) {
43
47
  var _this = this;
44
48
  (0, _classCallCheck2.default)(this, SourceSyncBlockStoreManager);
49
+ (0, _defineProperty2.default)(this, "localSourceSubscribers", new Map());
45
50
  (0, _defineProperty2.default)(this, "hasReceivedContentChange", false);
46
51
  /**
47
52
  * Promises for in-flight block creations, keyed by resourceId.
@@ -188,6 +193,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
188
193
  contentFragment: syncBlockNode.content,
189
194
  status: cachedBlock === null || cachedBlock === void 0 ? void 0 : cachedBlock.status
190
195
  }));
196
+ this.notifyLocalSource(resourceId);
191
197
  return true;
192
198
  } catch (error) {
193
199
  var _this$fireAnalyticsEv, _syncBlockNode$attrs2, _syncBlockNode$attrs3;
@@ -306,6 +312,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
306
312
  var cachedData = _this2.syncBlockCache.get(result.resourceId);
307
313
  if (cachedData) {
308
314
  cachedData.isDirty = true;
315
+ _this2.notifyLocalSource(result.resourceId);
309
316
  }
310
317
  }
311
318
  });
@@ -323,6 +330,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
323
330
  var cachedData = _this2.syncBlockCache.get(result.resourceId);
324
331
  if (cachedData && result.status) {
325
332
  cachedData.status = result.status;
333
+ _this2.notifyLocalSource(result.resourceId);
326
334
  }
327
335
  (_this2$fireAnalyticsE = _this2.fireAnalyticsEvent) === null || _this2$fireAnalyticsE === void 0 || _this2$fireAnalyticsE.call(_this2, (0, _errorHandling.updateSuccessPayload)(result.resourceId, false, (0, _utils.getSourceProductFromResourceIdSafe)(result.resourceId)));
328
336
  }
@@ -407,6 +415,54 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
407
415
  var _this$syncBlockCache$;
408
416
  return (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.status;
409
417
  }
418
+ }, {
419
+ key: "getLocalSourceSnapshot",
420
+ value: function getLocalSourceSnapshot(resourceId) {
421
+ if (!(0, _isExperimentEnabled.isExperimentEnabled)('editor-synced-block-same-page-sync')) {
422
+ return undefined;
423
+ }
424
+ var cached = this.syncBlockCache.get(resourceId);
425
+ if (!cached || cached.status === 'deleted') {
426
+ return undefined;
427
+ }
428
+ var _contentFragment = cached.contentFragment,
429
+ _isDirty = cached.isDirty,
430
+ data = (0, _objectWithoutProperties2.default)(cached, _excluded);
431
+ return _objectSpread(_objectSpread({}, data), {}, {
432
+ content: (0, _toConsumableArray2.default)(data.content)
433
+ });
434
+ }
435
+ }, {
436
+ key: "subscribeToLocalSource",
437
+ value: function subscribeToLocalSource(resourceId, callback) {
438
+ var _this3 = this;
439
+ if (!(0, _isExperimentEnabled.isExperimentEnabled)('editor-synced-block-same-page-sync')) {
440
+ return function () {};
441
+ }
442
+ var subscribers = this.localSourceSubscribers.get(resourceId);
443
+ if (!subscribers) {
444
+ subscribers = new Set();
445
+ this.localSourceSubscribers.set(resourceId, subscribers);
446
+ }
447
+ subscribers.add(callback);
448
+ callback(this.getLocalSourceSnapshot(resourceId));
449
+ return function () {
450
+ var currentSubscribers = _this3.localSourceSubscribers.get(resourceId);
451
+ currentSubscribers === null || currentSubscribers === void 0 || currentSubscribers.delete(callback);
452
+ if ((currentSubscribers === null || currentSubscribers === void 0 ? void 0 : currentSubscribers.size) === 0) {
453
+ _this3.localSourceSubscribers.delete(resourceId);
454
+ }
455
+ };
456
+ }
457
+ }, {
458
+ key: "notifyLocalSource",
459
+ value: function notifyLocalSource(resourceId) {
460
+ var _this$localSourceSubs;
461
+ var snapshot = this.getLocalSourceSnapshot(resourceId);
462
+ (_this$localSourceSubs = this.localSourceSubscribers.get(resourceId)) === null || _this$localSourceSubs === void 0 || _this$localSourceSubs.forEach(function (callback) {
463
+ return callback(snapshot);
464
+ });
465
+ }
410
466
 
411
467
  /**
412
468
  * Fires callback to insert node (if creation is successful) and clears pending creation data
@@ -453,6 +509,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
453
509
  var _this$fireAnalyticsEv6;
454
510
  // Delete the node from cache if fail to create so it's not flushed to BE
455
511
  this.syncBlockCache.delete(resourceId || '');
512
+ this.notifyLocalSource(resourceId);
456
513
  this.newSourceBlockResourceIds.delete(resourceId || '');
457
514
  // Creation failed, so there is no block to add content to — drop the
458
515
  // first-content tracking entry so a later unrelated edit cannot fire it.
@@ -486,10 +543,10 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
486
543
  }, {
487
544
  key: "registerConfirmationCallback",
488
545
  value: function registerConfirmationCallback(callback) {
489
- var _this3 = this;
546
+ var _this4 = this;
490
547
  this.confirmationCallback = callback;
491
548
  return function () {
492
- _this3.confirmationCallback = undefined;
549
+ _this4.confirmationCallback = undefined;
493
550
  };
494
551
  }
495
552
  }, {
@@ -529,7 +586,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
529
586
  }, {
530
587
  key: "createBodiedSyncBlockNode",
531
588
  value: function createBodiedSyncBlockNode(attrs, node, onCompletion, enrichment) {
532
- var _this4 = this;
589
+ var _this5 = this;
533
590
  if (this.viewMode === 'view') {
534
591
  return;
535
592
  }
@@ -559,6 +616,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
559
616
  var cached = this.syncBlockCache.get(resourceId);
560
617
  if (cached) {
561
618
  cached.status = 'unpublished';
619
+ this.notifyLocalSource(resourceId);
562
620
  }
563
621
  this.creationCompletionCallbacks.set(resourceId, onCompletion);
564
622
  (_this$createExperienc = this.createExperience) === null || _this$createExperienc === void 0 || _this$createExperienc.start({});
@@ -569,29 +627,29 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
569
627
  }).then(function (result) {
570
628
  var resourceId = result.resourceId || '';
571
629
  if (resourceId && !result.error) {
572
- var _this4$createExperien;
573
- _this4.commitPendingCreation(true, resourceId);
574
- (_this4$createExperien = _this4.createExperience) === null || _this4$createExperien === void 0 || _this4$createExperien.success();
630
+ var _this5$createExperien;
631
+ _this5.commitPendingCreation(true, resourceId);
632
+ (_this5$createExperien = _this5.createExperience) === null || _this5$createExperien === void 0 || _this5$createExperien.success();
575
633
  } else {
576
- var _this4$createExperien2, _this4$fireAnalyticsE;
577
- _this4.commitPendingCreation(false, resourceId);
578
- (_this4$createExperien2 = _this4.createExperience) === null || _this4$createExperien2 === void 0 || _this4$createExperien2.failure({
634
+ var _this5$createExperien2, _this5$fireAnalyticsE;
635
+ _this5.commitPendingCreation(false, resourceId);
636
+ (_this5$createExperien2 = _this5.createExperience) === null || _this5$createExperien2 === void 0 || _this5$createExperien2.failure({
579
637
  reason: result.error || 'Failed to create bodied sync block'
580
638
  });
581
- (_this4$fireAnalyticsE = _this4.fireAnalyticsEvent) === null || _this4$fireAnalyticsE === void 0 || _this4$fireAnalyticsE.call(_this4, (0, _errorHandling.createErrorPayload)(result.error || 'Failed to create bodied sync block', resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildErrorAttribution)(result.error, result.statusCode)));
639
+ (_this5$fireAnalyticsE = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE === void 0 || _this5$fireAnalyticsE.call(_this5, (0, _errorHandling.createErrorPayload)(result.error || 'Failed to create bodied sync block', resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildErrorAttribution)(result.error, result.statusCode)));
582
640
  }
583
641
  }).catch(function (error) {
584
- var _this4$createExperien3, _this4$fireAnalyticsE2;
585
- _this4.commitPendingCreation(false, resourceId);
642
+ var _this5$createExperien3, _this5$fireAnalyticsE2;
643
+ _this5.commitPendingCreation(false, resourceId);
586
644
  (0, _monitoring.logException)(error, {
587
645
  location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
588
646
  });
589
- (_this4$createExperien3 = _this4.createExperience) === null || _this4$createExperien3 === void 0 || _this4$createExperien3.failure({
647
+ (_this5$createExperien3 = _this5.createExperience) === null || _this5$createExperien3 === void 0 || _this5$createExperien3.failure({
590
648
  reason: error.message
591
649
  });
592
- (_this4$fireAnalyticsE2 = _this4.fireAnalyticsEvent) === null || _this4$fireAnalyticsE2 === void 0 || _this4$fireAnalyticsE2.call(_this4, (0, _errorHandling.createErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
650
+ (_this5$fireAnalyticsE2 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE2 === void 0 || _this5$fireAnalyticsE2.call(_this5, (0, _errorHandling.createErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
593
651
  }).finally(function () {
594
- _this4.pendingCreationPromises.delete(resourceId);
652
+ _this5.pendingCreationPromises.delete(resourceId);
595
653
  });
596
654
  this.pendingCreationPromises.set(resourceId, creationPromise);
597
655
  } catch (error) {
@@ -661,7 +719,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
661
719
  key: "delete",
662
720
  value: function () {
663
721
  var _delete2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(syncBlockIds, onDelete, onDeleteCompleted, reason, mechanism) {
664
- var _this5 = this;
722
+ var _this6 = this;
665
723
  var _this$deleteExperienc, results, callback, isDeleteSuccessful, _this$deleteExperienc2, _this$deleteExperienc3, attribution, _t2;
666
724
  return _regenerator.default.wrap(function (_context2) {
667
725
  while (1) switch (_context2.prev = _context2.next) {
@@ -680,7 +738,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
680
738
  throw new Error('Data provider not set');
681
739
  case 2:
682
740
  syncBlockIds.forEach(function (Ids) {
683
- _this5.setPendingDeletion(Ids, true);
741
+ _this6.setPendingDeletion(Ids, true);
684
742
  });
685
743
  (_this$deleteExperienc = this.deleteExperience) === null || _this$deleteExperienc === void 0 || _this$deleteExperienc.start({});
686
744
  _context2.next = 3;
@@ -698,24 +756,25 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
698
756
  // Emit before the cache entry is deleted below, so blockInstanceId
699
757
  // is still available to emitDeleteSuccess.
700
758
  results.forEach(function (result) {
701
- _this5.emitDeleteSuccess(result.resourceId, reason, mechanism);
759
+ _this6.emitDeleteSuccess(result.resourceId, reason, mechanism);
702
760
  });
703
761
  callback = function callback(Ids) {
704
- return _this5.syncBlockCache.delete(Ids.resourceId);
762
+ _this6.syncBlockCache.delete(Ids.resourceId);
763
+ _this6.notifyLocalSource(Ids.resourceId);
705
764
  };
706
765
  this.clearPendingDeletion();
707
766
  (_this$deleteExperienc2 = this.deleteExperience) === null || _this$deleteExperienc2 === void 0 || _this$deleteExperienc2.success();
708
767
  } else {
709
768
  callback = function callback(Ids) {
710
- _this5.setPendingDeletion(Ids, false);
769
+ _this6.setPendingDeletion(Ids, false);
711
770
  };
712
771
  (_this$deleteExperienc3 = this.deleteExperience) === null || _this$deleteExperienc3 === void 0 || _this$deleteExperienc3.failure();
713
772
  results.forEach(function (result) {
714
773
  if (result.success) {
715
- _this5.emitDeleteSuccess(result.resourceId, reason, mechanism);
774
+ _this6.emitDeleteSuccess(result.resourceId, reason, mechanism);
716
775
  } else {
717
- var _this5$fireAnalyticsE;
718
- (_this5$fireAnalyticsE = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE === void 0 || _this5$fireAnalyticsE.call(_this5, (0, _errorHandling.deleteErrorPayload)(result.error || 'Failed to delete synced block', result.resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(result.resourceId), (0, _errorHandling.buildErrorAttribution)(result.error, result.statusCode)));
776
+ var _this6$fireAnalyticsE;
777
+ (_this6$fireAnalyticsE = _this6.fireAnalyticsEvent) === null || _this6$fireAnalyticsE === void 0 || _this6$fireAnalyticsE.call(_this6, (0, _errorHandling.deleteErrorPayload)(result.error || 'Failed to delete synced block', result.resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(result.resourceId), (0, _errorHandling.buildErrorAttribution)(result.error, result.statusCode)));
719
778
  }
720
779
  });
721
780
  }
@@ -728,9 +787,9 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
728
787
  // so the attribution `reason` falls back to `unknown`.
729
788
  attribution = (0, _errorHandling.buildErrorAttribution)();
730
789
  syncBlockIds.forEach(function (Ids) {
731
- var _this5$fireAnalyticsE2;
732
- _this5.setPendingDeletion(Ids, false);
733
- (_this5$fireAnalyticsE2 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE2 === void 0 || _this5$fireAnalyticsE2.call(_this5, (0, _errorHandling.deleteErrorPayload)(_t2.message, Ids.resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(Ids.resourceId), attribution));
790
+ var _this6$fireAnalyticsE2;
791
+ _this6.setPendingDeletion(Ids, false);
792
+ (_this6$fireAnalyticsE2 = _this6.fireAnalyticsEvent) === null || _this6$fireAnalyticsE2 === void 0 || _this6$fireAnalyticsE2.call(_this6, (0, _errorHandling.deleteErrorPayload)(_t2.message, Ids.resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(Ids.resourceId), attribution));
734
793
  });
735
794
  (0, _monitoring.logException)(_t2, {
736
795
  location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
@@ -808,7 +867,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
808
867
  key: "fetchAndCacheStatuses",
809
868
  value: (function () {
810
869
  var _fetchAndCacheStatuses = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4() {
811
- var _this6 = this;
870
+ var _this7 = this;
812
871
  var sourceToReferenceMap, syncBlockNodes, results, _iterator3, _step3, _sourceToReferenceMap, _result$data, result, sourceResourceId, cached, _t3;
813
872
  return _regenerator.default.wrap(function (_context4) {
814
873
  while (1) switch (_context4.prev = _context4.next) {
@@ -826,11 +885,11 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
826
885
  // so we can update the correct cache entries.
827
886
  sourceToReferenceMap = new Map();
828
887
  syncBlockNodes = Array.from(this.syncBlockCache.entries()).map(function (_ref) {
829
- var _this6$dataProvider$g, _this6$dataProvider;
888
+ var _this7$dataProvider$g, _this7$dataProvider;
830
889
  var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
831
890
  resourceId = _ref2[0],
832
891
  data = _ref2[1];
833
- var referenceResourceId = (_this6$dataProvider$g = (_this6$dataProvider = _this6.dataProvider) === null || _this6$dataProvider === void 0 ? void 0 : _this6$dataProvider.generateResourceIdForReference(resourceId)) !== null && _this6$dataProvider$g !== void 0 ? _this6$dataProvider$g : resourceId;
892
+ var referenceResourceId = (_this7$dataProvider$g = (_this7$dataProvider = _this7.dataProvider) === null || _this7$dataProvider === void 0 ? void 0 : _this7$dataProvider.generateResourceIdForReference(resourceId)) !== null && _this7$dataProvider$g !== void 0 ? _this7$dataProvider$g : resourceId;
834
893
  sourceToReferenceMap.set(referenceResourceId, resourceId);
835
894
  return {
836
895
  type: 'bodiedSyncBlock',
@@ -854,6 +913,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
854
913
  cached = this.syncBlockCache.get(sourceResourceId);
855
914
  if (cached && (_result$data = result.data) !== null && _result$data !== void 0 && _result$data.status) {
856
915
  cached.status = result.data.status;
916
+ this.notifyLocalSource(sourceResourceId);
857
917
  }
858
918
  }
859
919
  } catch (err) {
@@ -983,7 +1043,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
983
1043
  }, {
984
1044
  key: "getSyncBlockSourceInfo",
985
1045
  value: function getSyncBlockSourceInfo(localId) {
986
- var _this7 = this;
1046
+ var _this8 = this;
987
1047
  try {
988
1048
  var _this$fetchSourceInfo;
989
1049
  if (!this.dataProvider) {
@@ -992,13 +1052,13 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
992
1052
  (_this$fetchSourceInfo = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo === void 0 || _this$fetchSourceInfo.start();
993
1053
  return this.dataProvider.fetchSyncBlockSourceInfo(localId, undefined, undefined).then(function (sourceInfo) {
994
1054
  if (!sourceInfo) {
995
- var _this7$fetchSourceInf;
996
- (_this7$fetchSourceInf = _this7.fetchSourceInfoExperience) === null || _this7$fetchSourceInf === void 0 || _this7$fetchSourceInf.failure({
1055
+ var _this8$fetchSourceInf;
1056
+ (_this8$fetchSourceInf = _this8.fetchSourceInfoExperience) === null || _this8$fetchSourceInf === void 0 || _this8$fetchSourceInf.failure({
997
1057
  reason: 'No source info returned'
998
1058
  });
999
1059
  } else {
1000
- var _this7$fetchSourceInf2;
1001
- (_this7$fetchSourceInf2 = _this7.fetchSourceInfoExperience) === null || _this7$fetchSourceInf2 === void 0 || _this7$fetchSourceInf2.success();
1060
+ var _this8$fetchSourceInf2;
1061
+ (_this8$fetchSourceInf2 = _this8.fetchSourceInfoExperience) === null || _this8$fetchSourceInf2 === void 0 || _this8$fetchSourceInf2.success();
1002
1062
  }
1003
1063
  return sourceInfo;
1004
1064
  });
@@ -1033,8 +1093,17 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
1033
1093
  }, {
1034
1094
  key: "destroy",
1035
1095
  value: function destroy() {
1036
- var _this$saveExperience4, _this$createExperienc2, _this$deleteExperienc4, _this$fetchSourceInfo2;
1096
+ var _this9 = this,
1097
+ _this$saveExperience4,
1098
+ _this$createExperienc2,
1099
+ _this$deleteExperienc4,
1100
+ _this$fetchSourceInfo2;
1101
+ var subscribedResourceIds = (0, _toConsumableArray2.default)(this.localSourceSubscribers.keys());
1037
1102
  this.syncBlockCache.clear();
1103
+ subscribedResourceIds.forEach(function (resourceId) {
1104
+ return _this9.notifyLocalSource(resourceId);
1105
+ });
1106
+ this.localSourceSubscribers.clear();
1038
1107
  this.confirmationCallback = undefined;
1039
1108
  this.creationCompletionCallbacks.clear();
1040
1109
  this.flushCompletionCallback = undefined;
@@ -29,10 +29,8 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
29
29
  var SyncBlockStoreManager = exports.SyncBlockStoreManager = /*#__PURE__*/function () {
30
30
  function SyncBlockStoreManager(dataProvider, viewMode, isLivePage) {
31
31
  (0, _classCallCheck2.default)(this, SyncBlockStoreManager);
32
- // In future, if reference manager needs to reach to source manager and read its current in memory cache
33
- // we can pass the source manager as a parameter to the reference manager constructor
34
32
  this.sourceSyncBlockStoreManager = new _sourceSyncBlockStoreManager.SourceSyncBlockStoreManager(dataProvider, viewMode, isLivePage);
35
- this.referenceSyncBlockStoreManager = new _referenceSyncBlockStoreManager.ReferenceSyncBlockStoreManager(dataProvider, viewMode);
33
+ this.referenceSyncBlockStoreManager = new _referenceSyncBlockStoreManager.ReferenceSyncBlockStoreManager(dataProvider, viewMode, this.sourceSyncBlockStoreManager);
36
34
  this.dataProvider = dataProvider;
37
35
  this.referenceSyncBlockStoreManager.setRealTimeSubscriptionsEnabled(true);
38
36
  }
@@ -1,9 +1,11 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import isEqual from 'lodash/isEqual';
3
3
  import { logException } from '@atlaskit/editor-common/monitoring';
4
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
4
5
  import { isProviderNotReadyError, ProviderNotReadyError, SyncBlockError } from '../common/types';
5
6
  import { buildErrorAttribution, buildFetchErrorAttribution, cacheDeletionForcedPayload, fetchErrorPayload, fetchSuccessPayload, getSourceInfoErrorPayload, sourceInfoOrphanedPayload, updateReferenceErrorPayload } from '../utils/errorHandling';
6
7
  import { getFetchExperience, getFetchSourceInfoExperience, getSaveReferenceExperience } from '../utils/experienceTracking';
8
+ import { parseResourceId } from '../utils/resourceId';
7
9
  import { resolveSyncBlockInstance } from '../utils/resolveSyncBlockInstance';
8
10
  import { createSyncBlockNode, getSourceProductFromResourceIdSafe, normalizeSyncBlockJSONContent } from '../utils/utils';
9
11
  import { SyncBlockBatchFetcher } from './syncBlockBatchFetcher';
@@ -27,7 +29,7 @@ const CACHE_DELETION_MAX_RESCHEDULES = 10;
27
29
  // Handles fetching source URL and title for sync blocks.
28
30
  // Can be used in both editor and renderer contexts.
29
31
  export class ReferenceSyncBlockStoreManager {
30
- constructor(dataProvider, viewMode) {
32
+ constructor(dataProvider, viewMode, sourceManager) {
31
33
  var _this$dataProvider;
32
34
  // Keeps track of addition and deletion of reference synced blocks on the document
33
35
  // This starts as true to always flush the cache when document is saved for the first time
@@ -50,6 +52,7 @@ export class ReferenceSyncBlockStoreManager {
50
52
  _defineProperty(this, "isDestroyed", false);
51
53
  this.dataProvider = dataProvider;
52
54
  this.viewMode = viewMode;
55
+ this.sourceManager = sourceManager;
53
56
  this.syncBlockFetchDataRequests = new Map();
54
57
  this.syncBlockSourceInfoRequests = new Map();
55
58
  this.newlyAddedSyncBlocks = new Set();
@@ -170,6 +173,10 @@ export class ReferenceSyncBlockStoreManager {
170
173
  }
171
174
  getInitialSyncBlockData(resourceId) {
172
175
  var _this$dataProvider2, _this$dataProvider2$g;
176
+ const localReference = this.getLocalReference(resourceId);
177
+ if (localReference) {
178
+ return localReference;
179
+ }
173
180
  const syncBlockNode = createSyncBlockNode('', resourceId);
174
181
  const providerData = (_this$dataProvider2 = this.dataProvider) === null || _this$dataProvider2 === void 0 ? void 0 : (_this$dataProvider2$g = _this$dataProvider2.getNodeDataFromCache(syncBlockNode)) === null || _this$dataProvider2$g === void 0 ? void 0 : _this$dataProvider2$g.data;
175
182
  if (providerData) {
@@ -179,6 +186,38 @@ export class ReferenceSyncBlockStoreManager {
179
186
  }
180
187
  return this.getFromSessionCache(resourceId);
181
188
  }
189
+ getSameDocumentReferenceParts(resourceId) {
190
+ const parsed = parseResourceId(resourceId);
191
+ if (!parsed || !this.dataProvider || this.dataProvider.generateResourceIdForReference(parsed.uuid) !== resourceId) {
192
+ return undefined;
193
+ }
194
+ return parsed;
195
+ }
196
+ getLocalReference(resourceId) {
197
+ var _this$sourceManager;
198
+ if (!isExperimentEnabled('editor-synced-block-same-page-sync')) {
199
+ return undefined;
200
+ }
201
+ const parsed = this.getSameDocumentReferenceParts(resourceId);
202
+ if (!parsed) {
203
+ return undefined;
204
+ }
205
+ const snapshot = (_this$sourceManager = this.sourceManager) === null || _this$sourceManager === void 0 ? void 0 : _this$sourceManager.getLocalSourceSnapshot(parsed.uuid);
206
+ if (!snapshot) {
207
+ return undefined;
208
+ }
209
+ return {
210
+ resourceId,
211
+ data: {
212
+ ...snapshot,
213
+ content: [...snapshot.content]
214
+ },
215
+ localSameDocumentSource: {
216
+ sourceBlockInstanceId: snapshot.blockInstanceId,
217
+ sourceProduct: parsed.product
218
+ }
219
+ };
220
+ }
182
221
  normalizeReferenceData(syncBlock) {
183
222
  var _syncBlock$data, _syncBlock$data$conte;
184
223
  if (!((_syncBlock$data = syncBlock.data) !== null && _syncBlock$data !== void 0 && (_syncBlock$data$conte = _syncBlock$data.content) !== null && _syncBlock$data$conte !== void 0 && _syncBlock$data$conte.length)) {
@@ -765,7 +804,34 @@ export class ReferenceSyncBlockStoreManager {
765
804
  });
766
805
  }
767
806
  subscribeToSyncBlock(resourceId, localId, callback) {
768
- return this._subscriptionManager.subscribeToSyncBlock(resourceId, localId, callback);
807
+ var _this$sourceManager2;
808
+ const isSamePageSyncEnabled = isExperimentEnabled('editor-synced-block-same-page-sync');
809
+ const sameDocumentParts = isSamePageSyncEnabled ? this.getSameDocumentReferenceParts(resourceId) : undefined;
810
+ let localReference = isSamePageSyncEnabled ? this.getLocalReference(resourceId) : undefined;
811
+ const unsubscribeRemote = this._subscriptionManager.subscribeToSyncBlock(resourceId, localId, instance => {
812
+ if (!localReference) {
813
+ callback(instance);
814
+ }
815
+ });
816
+ const unsubscribeLocal = sameDocumentParts ? (_this$sourceManager2 = this.sourceManager) === null || _this$sourceManager2 === void 0 ? void 0 : _this$sourceManager2.subscribeToLocalSource(sameDocumentParts.uuid, () => {
817
+ const hadLocalReference = Boolean(localReference);
818
+ localReference = this.getLocalReference(resourceId);
819
+ if (localReference) {
820
+ callback(localReference);
821
+ return;
822
+ }
823
+ if (!hadLocalReference) {
824
+ return;
825
+ }
826
+ // Source left the document: keep the last local instance on screen
827
+ // and fetch the current backend value. Do not replay a remote
828
+ // value captured while the local source was authoritative.
829
+ this.debouncedBatchedFetchSyncBlocks(resourceId);
830
+ }) : undefined;
831
+ return () => {
832
+ unsubscribeLocal === null || unsubscribeLocal === void 0 ? void 0 : unsubscribeLocal();
833
+ unsubscribeRemote();
834
+ };
769
835
  }
770
836
  subscribeToSourceTitle(node, callback) {
771
837
  return this._subscriptionManager.subscribeToSourceTitle(node, callback);
@@ -783,7 +849,7 @@ export class ReferenceSyncBlockStoreManager {
783
849
  if (!localId || !resourceId) {
784
850
  throw new Error('Missing local id or resource id');
785
851
  }
786
- return this._subscriptionManager.subscribeToSyncBlock(resourceId, localId, callback);
852
+ return this.subscribeToSyncBlock(resourceId, localId, callback);
787
853
  } catch (error) {
788
854
  var _this$fireAnalyticsEv11;
789
855
  // EDITOR-7860: benign not-ready/torn-down case — suppress both the