@atlaskit/editor-synced-block-provider 12.0.7 → 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.
@@ -1,5 +1,6 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import { logException } from '@atlaskit/editor-common/monitoring';
3
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
3
4
  import { fg } from '@atlaskit/platform-feature-flags/fg';
4
5
  import { SyncBlockError } from '../common/types';
5
6
  import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, createSuccessOperationalPayload, addContentSuccessPayload, deleteSuccessPayload, fetchReferencesErrorPayload, buildErrorAttribution } from '../utils/errorHandling';
@@ -23,6 +24,7 @@ const DELETE_DEDUPE_WINDOW_MS = 5000;
23
24
  // Ensures consistency between local and remote state, and can be used in both editor and renderer contexts.
24
25
  export class SourceSyncBlockStoreManager {
25
26
  constructor(dataProvider, viewMode, isLivePage) {
27
+ _defineProperty(this, "localSourceSubscribers", new Map());
26
28
  _defineProperty(this, "hasReceivedContentChange", false);
27
29
  /**
28
30
  * Promises for in-flight block creations, keyed by resourceId.
@@ -157,6 +159,7 @@ export class SourceSyncBlockStoreManager {
157
159
  contentFragment: syncBlockNode.content,
158
160
  status: cachedBlock === null || cachedBlock === void 0 ? void 0 : cachedBlock.status
159
161
  });
162
+ this.notifyLocalSource(resourceId);
160
163
  return true;
161
164
  } catch (error) {
162
165
  var _this$fireAnalyticsEv, _syncBlockNode$attrs, _syncBlockNode$attrs2;
@@ -257,6 +260,7 @@ export class SourceSyncBlockStoreManager {
257
260
  const cachedData = this.syncBlockCache.get(result.resourceId);
258
261
  if (cachedData) {
259
262
  cachedData.isDirty = true;
263
+ this.notifyLocalSource(result.resourceId);
260
264
  }
261
265
  }
262
266
  });
@@ -270,6 +274,7 @@ export class SourceSyncBlockStoreManager {
270
274
  const cachedData = this.syncBlockCache.get(result.resourceId);
271
275
  if (cachedData && result.status) {
272
276
  cachedData.status = result.status;
277
+ this.notifyLocalSource(result.resourceId);
273
278
  }
274
279
  (_this$fireAnalyticsEv2 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv2 === void 0 ? void 0 : _this$fireAnalyticsEv2.call(this, updateSuccessPayload(result.resourceId, false, getSourceProductFromResourceIdSafe(result.resourceId)));
275
280
  }
@@ -330,6 +335,48 @@ export class SourceSyncBlockStoreManager {
330
335
  var _this$syncBlockCache$;
331
336
  return (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.status;
332
337
  }
338
+ getLocalSourceSnapshot(resourceId) {
339
+ if (!isExperimentEnabled('editor-synced-block-same-page-sync')) {
340
+ return undefined;
341
+ }
342
+ const cached = this.syncBlockCache.get(resourceId);
343
+ if (!cached || cached.status === 'deleted') {
344
+ return undefined;
345
+ }
346
+ const {
347
+ contentFragment: _contentFragment,
348
+ isDirty: _isDirty,
349
+ ...data
350
+ } = cached;
351
+ return {
352
+ ...data,
353
+ content: [...data.content]
354
+ };
355
+ }
356
+ subscribeToLocalSource(resourceId, callback) {
357
+ if (!isExperimentEnabled('editor-synced-block-same-page-sync')) {
358
+ return () => {};
359
+ }
360
+ let subscribers = this.localSourceSubscribers.get(resourceId);
361
+ if (!subscribers) {
362
+ subscribers = new Set();
363
+ this.localSourceSubscribers.set(resourceId, subscribers);
364
+ }
365
+ subscribers.add(callback);
366
+ callback(this.getLocalSourceSnapshot(resourceId));
367
+ return () => {
368
+ const currentSubscribers = this.localSourceSubscribers.get(resourceId);
369
+ currentSubscribers === null || currentSubscribers === void 0 ? void 0 : currentSubscribers.delete(callback);
370
+ if ((currentSubscribers === null || currentSubscribers === void 0 ? void 0 : currentSubscribers.size) === 0) {
371
+ this.localSourceSubscribers.delete(resourceId);
372
+ }
373
+ };
374
+ }
375
+ notifyLocalSource(resourceId) {
376
+ var _this$localSourceSubs;
377
+ const snapshot = this.getLocalSourceSnapshot(resourceId);
378
+ (_this$localSourceSubs = this.localSourceSubscribers.get(resourceId)) === null || _this$localSourceSubs === void 0 ? void 0 : _this$localSourceSubs.forEach(callback => callback(snapshot));
379
+ }
333
380
 
334
381
  /**
335
382
  * Fires callback to insert node (if creation is successful) and clears pending creation data
@@ -374,6 +421,7 @@ export class SourceSyncBlockStoreManager {
374
421
  var _this$fireAnalyticsEv8;
375
422
  // Delete the node from cache if fail to create so it's not flushed to BE
376
423
  this.syncBlockCache.delete(resourceId || '');
424
+ this.notifyLocalSource(resourceId);
377
425
  this.newSourceBlockResourceIds.delete(resourceId || '');
378
426
  // Creation failed, so there is no block to add content to — drop the
379
427
  // first-content tracking entry so a later unrelated edit cannot fire it.
@@ -471,6 +519,7 @@ export class SourceSyncBlockStoreManager {
471
519
  const cached = this.syncBlockCache.get(resourceId);
472
520
  if (cached) {
473
521
  cached.status = 'unpublished';
522
+ this.notifyLocalSource(resourceId);
474
523
  }
475
524
  this.creationCompletionCallbacks.set(resourceId, onCompletion);
476
525
  (_this$createExperienc = this.createExperience) === null || _this$createExperienc === void 0 ? void 0 : _this$createExperienc.start({});
@@ -578,7 +627,10 @@ export class SourceSyncBlockStoreManager {
578
627
  results.forEach(result => {
579
628
  this.emitDeleteSuccess(result.resourceId, reason, mechanism);
580
629
  });
581
- callback = Ids => this.syncBlockCache.delete(Ids.resourceId);
630
+ callback = Ids => {
631
+ this.syncBlockCache.delete(Ids.resourceId);
632
+ this.notifyLocalSource(Ids.resourceId);
633
+ };
582
634
  this.clearPendingDeletion();
583
635
  (_this$deleteExperienc2 = this.deleteExperience) === null || _this$deleteExperienc2 === void 0 ? void 0 : _this$deleteExperienc2.success();
584
636
  } else {
@@ -678,6 +730,7 @@ export class SourceSyncBlockStoreManager {
678
730
  const cached = this.syncBlockCache.get(sourceResourceId);
679
731
  if (cached && (_result$data = result.data) !== null && _result$data !== void 0 && _result$data.status) {
680
732
  cached.status = result.data.status;
733
+ this.notifyLocalSource(sourceResourceId);
681
734
  }
682
735
  }
683
736
  } catch {
@@ -792,7 +845,10 @@ export class SourceSyncBlockStoreManager {
792
845
  }
793
846
  destroy() {
794
847
  var _this$saveExperience4, _this$createExperienc5, _this$deleteExperienc4, _this$fetchSourceInfo4;
848
+ const subscribedResourceIds = [...this.localSourceSubscribers.keys()];
795
849
  this.syncBlockCache.clear();
850
+ subscribedResourceIds.forEach(resourceId => this.notifyLocalSource(resourceId));
851
+ this.localSourceSubscribers.clear();
796
852
  this.confirmationCallback = undefined;
797
853
  this.creationCompletionCallbacks.clear();
798
854
  this.flushCompletionCallback = undefined;
@@ -16,10 +16,8 @@ import { SourceSyncBlockStoreManager } from './sourceSyncBlockStoreManager';
16
16
 
17
17
  export class SyncBlockStoreManager {
18
18
  constructor(dataProvider, viewMode, isLivePage) {
19
- // In future, if reference manager needs to reach to source manager and read its current in memory cache
20
- // we can pass the source manager as a parameter to the reference manager constructor
21
19
  this.sourceSyncBlockStoreManager = new SourceSyncBlockStoreManager(dataProvider, viewMode, isLivePage);
22
- this.referenceSyncBlockStoreManager = new ReferenceSyncBlockStoreManager(dataProvider, viewMode);
20
+ this.referenceSyncBlockStoreManager = new ReferenceSyncBlockStoreManager(dataProvider, viewMode, this.sourceSyncBlockStoreManager);
23
21
  this.dataProvider = dataProvider;
24
22
  this.referenceSyncBlockStoreManager.setRealTimeSubscriptionsEnabled(true);
25
23
  }
@@ -1,5 +1,6 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
3
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
4
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
4
5
  import _createClass from "@babel/runtime/helpers/createClass";
5
6
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
@@ -11,9 +12,11 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
11
12
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
12
13
  import isEqual from 'lodash/isEqual';
13
14
  import { logException } from '@atlaskit/editor-common/monitoring';
15
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
14
16
  import { isProviderNotReadyError, ProviderNotReadyError, SyncBlockError } from '../common/types';
15
17
  import { buildErrorAttribution, buildFetchErrorAttribution, cacheDeletionForcedPayload, fetchErrorPayload, fetchSuccessPayload, getSourceInfoErrorPayload, sourceInfoOrphanedPayload, updateReferenceErrorPayload } from '../utils/errorHandling';
16
18
  import { getFetchExperience, getFetchSourceInfoExperience, getSaveReferenceExperience } from '../utils/experienceTracking';
19
+ import { parseResourceId } from '../utils/resourceId';
17
20
  import { resolveSyncBlockInstance } from '../utils/resolveSyncBlockInstance';
18
21
  import { createSyncBlockNode, getSourceProductFromResourceIdSafe, normalizeSyncBlockJSONContent } from '../utils/utils';
19
22
  import { SyncBlockBatchFetcher } from './syncBlockBatchFetcher';
@@ -37,7 +40,7 @@ var CACHE_DELETION_MAX_RESCHEDULES = 10;
37
40
  // Handles fetching source URL and title for sync blocks.
38
41
  // Can be used in both editor and renderer contexts.
39
42
  export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
40
- function ReferenceSyncBlockStoreManager(dataProvider, viewMode) {
43
+ function ReferenceSyncBlockStoreManager(dataProvider, viewMode, sourceManager) {
41
44
  var _this = this,
42
45
  _this$dataProvider;
43
46
  _classCallCheck(this, ReferenceSyncBlockStoreManager);
@@ -62,6 +65,7 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
62
65
  _defineProperty(this, "isDestroyed", false);
63
66
  this.dataProvider = dataProvider;
64
67
  this.viewMode = viewMode;
68
+ this.sourceManager = sourceManager;
65
69
  this.syncBlockFetchDataRequests = new Map();
66
70
  this.syncBlockSourceInfoRequests = new Map();
67
71
  this.newlyAddedSyncBlocks = new Set();
@@ -238,6 +242,10 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
238
242
  key: "getInitialSyncBlockData",
239
243
  value: function getInitialSyncBlockData(resourceId) {
240
244
  var _this$dataProvider2;
245
+ var localReference = this.getLocalReference(resourceId);
246
+ if (localReference) {
247
+ return localReference;
248
+ }
241
249
  var syncBlockNode = createSyncBlockNode('', resourceId);
242
250
  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;
243
251
  if (providerData) {
@@ -247,6 +255,41 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
247
255
  }
248
256
  return this.getFromSessionCache(resourceId);
249
257
  }
258
+ }, {
259
+ key: "getSameDocumentReferenceParts",
260
+ value: function getSameDocumentReferenceParts(resourceId) {
261
+ var parsed = parseResourceId(resourceId);
262
+ if (!parsed || !this.dataProvider || this.dataProvider.generateResourceIdForReference(parsed.uuid) !== resourceId) {
263
+ return undefined;
264
+ }
265
+ return parsed;
266
+ }
267
+ }, {
268
+ key: "getLocalReference",
269
+ value: function getLocalReference(resourceId) {
270
+ var _this$sourceManager;
271
+ if (!isExperimentEnabled('editor-synced-block-same-page-sync')) {
272
+ return undefined;
273
+ }
274
+ var parsed = this.getSameDocumentReferenceParts(resourceId);
275
+ if (!parsed) {
276
+ return undefined;
277
+ }
278
+ var snapshot = (_this$sourceManager = this.sourceManager) === null || _this$sourceManager === void 0 ? void 0 : _this$sourceManager.getLocalSourceSnapshot(parsed.uuid);
279
+ if (!snapshot) {
280
+ return undefined;
281
+ }
282
+ return {
283
+ resourceId: resourceId,
284
+ data: _objectSpread(_objectSpread({}, snapshot), {}, {
285
+ content: _toConsumableArray(snapshot.content)
286
+ }),
287
+ localSameDocumentSource: {
288
+ sourceBlockInstanceId: snapshot.blockInstanceId,
289
+ sourceProduct: parsed.product
290
+ }
291
+ };
292
+ }
250
293
  }, {
251
294
  key: "normalizeReferenceData",
252
295
  value: function normalizeReferenceData(syncBlock) {
@@ -914,7 +957,35 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
914
957
  }, {
915
958
  key: "subscribeToSyncBlock",
916
959
  value: function subscribeToSyncBlock(resourceId, localId, callback) {
917
- return this._subscriptionManager.subscribeToSyncBlock(resourceId, localId, callback);
960
+ var _this$sourceManager2,
961
+ _this9 = this;
962
+ var isSamePageSyncEnabled = isExperimentEnabled('editor-synced-block-same-page-sync');
963
+ var sameDocumentParts = isSamePageSyncEnabled ? this.getSameDocumentReferenceParts(resourceId) : undefined;
964
+ var localReference = isSamePageSyncEnabled ? this.getLocalReference(resourceId) : undefined;
965
+ var unsubscribeRemote = this._subscriptionManager.subscribeToSyncBlock(resourceId, localId, function (instance) {
966
+ if (!localReference) {
967
+ callback(instance);
968
+ }
969
+ });
970
+ var unsubscribeLocal = sameDocumentParts ? (_this$sourceManager2 = this.sourceManager) === null || _this$sourceManager2 === void 0 ? void 0 : _this$sourceManager2.subscribeToLocalSource(sameDocumentParts.uuid, function () {
971
+ var hadLocalReference = Boolean(localReference);
972
+ localReference = _this9.getLocalReference(resourceId);
973
+ if (localReference) {
974
+ callback(localReference);
975
+ return;
976
+ }
977
+ if (!hadLocalReference) {
978
+ return;
979
+ }
980
+ // Source left the document: keep the last local instance on screen
981
+ // and fetch the current backend value. Do not replay a remote
982
+ // value captured while the local source was authoritative.
983
+ _this9.debouncedBatchedFetchSyncBlocks(resourceId);
984
+ }) : undefined;
985
+ return function () {
986
+ unsubscribeLocal === null || unsubscribeLocal === void 0 || unsubscribeLocal();
987
+ unsubscribeRemote();
988
+ };
918
989
  }
919
990
  }, {
920
991
  key: "subscribeToSourceTitle",
@@ -935,7 +1006,7 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
935
1006
  if (!localId || !resourceId) {
936
1007
  throw new Error('Missing local id or resource id');
937
1008
  }
938
- return this._subscriptionManager.subscribeToSyncBlock(resourceId, localId, callback);
1009
+ return this.subscribeToSyncBlock(resourceId, localId, callback);
939
1010
  } catch (error) {
940
1011
  var _this$fireAnalyticsEv7;
941
1012
  // EDITOR-7860: benign not-ready/torn-down case — suppress both the
@@ -1003,7 +1074,7 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
1003
1074
  key: "flush",
1004
1075
  value: (function () {
1005
1076
  var _flush = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
1006
- var _this9 = this;
1077
+ var _this0 = this;
1007
1078
  var success, syncedBlocksToFlush, _this$saveExperience, blocks, _iterator, _step, _loop, updateResult, _this$saveExperience2, _this$fireAnalyticsEv8, _this$saveExperience3, _this$fireAnalyticsEv9, _this$saveExperience4, _t2, _t3;
1008
1079
  return _regeneratorRuntime.wrap(function (_context4) {
1009
1080
  while (1) switch (_context4.prev = _context4.next) {
@@ -1147,8 +1218,8 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
1147
1218
  // Use setTimeout to avoid deep recursion and run queued flush asynchronously
1148
1219
  // Note: flush() handles all exceptions internally and never rejects
1149
1220
  this.queuedFlushTimeout = setTimeout(function () {
1150
- _this9.queuedFlushTimeout = undefined;
1151
- void _this9.flush();
1221
+ _this0.queuedFlushTimeout = undefined;
1222
+ void _this0.flush();
1152
1223
  }, 0);
1153
1224
  }
1154
1225
  return _context4.finish(17);
@@ -1,8 +1,11 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
+ import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
2
4
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
3
5
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
4
6
  import _createClass from "@babel/runtime/helpers/createClass";
5
7
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
8
+ var _excluded = ["contentFragment", "isDirty"];
6
9
  import _regeneratorRuntime from "@babel/runtime/regenerator";
7
10
  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; } } }; }
8
11
  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; } }
@@ -10,6 +13,7 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
10
13
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
11
14
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
12
15
  import { logException } from '@atlaskit/editor-common/monitoring';
16
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
13
17
  import { fg } from '@atlaskit/platform-feature-flags/fg';
14
18
  import { SyncBlockError } from '../common/types';
15
19
  import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, createSuccessOperationalPayload, addContentSuccessPayload, deleteSuccessPayload, fetchReferencesErrorPayload, buildErrorAttribution } from '../utils/errorHandling';
@@ -35,6 +39,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
35
39
  function SourceSyncBlockStoreManager(dataProvider, viewMode, isLivePage) {
36
40
  var _this = this;
37
41
  _classCallCheck(this, SourceSyncBlockStoreManager);
42
+ _defineProperty(this, "localSourceSubscribers", new Map());
38
43
  _defineProperty(this, "hasReceivedContentChange", false);
39
44
  /**
40
45
  * Promises for in-flight block creations, keyed by resourceId.
@@ -181,6 +186,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
181
186
  contentFragment: syncBlockNode.content,
182
187
  status: cachedBlock === null || cachedBlock === void 0 ? void 0 : cachedBlock.status
183
188
  }));
189
+ this.notifyLocalSource(resourceId);
184
190
  return true;
185
191
  } catch (error) {
186
192
  var _this$fireAnalyticsEv, _syncBlockNode$attrs2, _syncBlockNode$attrs3;
@@ -299,6 +305,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
299
305
  var cachedData = _this2.syncBlockCache.get(result.resourceId);
300
306
  if (cachedData) {
301
307
  cachedData.isDirty = true;
308
+ _this2.notifyLocalSource(result.resourceId);
302
309
  }
303
310
  }
304
311
  });
@@ -316,6 +323,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
316
323
  var cachedData = _this2.syncBlockCache.get(result.resourceId);
317
324
  if (cachedData && result.status) {
318
325
  cachedData.status = result.status;
326
+ _this2.notifyLocalSource(result.resourceId);
319
327
  }
320
328
  (_this2$fireAnalyticsE = _this2.fireAnalyticsEvent) === null || _this2$fireAnalyticsE === void 0 || _this2$fireAnalyticsE.call(_this2, updateSuccessPayload(result.resourceId, false, getSourceProductFromResourceIdSafe(result.resourceId)));
321
329
  }
@@ -400,6 +408,54 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
400
408
  var _this$syncBlockCache$;
401
409
  return (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.status;
402
410
  }
411
+ }, {
412
+ key: "getLocalSourceSnapshot",
413
+ value: function getLocalSourceSnapshot(resourceId) {
414
+ if (!isExperimentEnabled('editor-synced-block-same-page-sync')) {
415
+ return undefined;
416
+ }
417
+ var cached = this.syncBlockCache.get(resourceId);
418
+ if (!cached || cached.status === 'deleted') {
419
+ return undefined;
420
+ }
421
+ var _contentFragment = cached.contentFragment,
422
+ _isDirty = cached.isDirty,
423
+ data = _objectWithoutProperties(cached, _excluded);
424
+ return _objectSpread(_objectSpread({}, data), {}, {
425
+ content: _toConsumableArray(data.content)
426
+ });
427
+ }
428
+ }, {
429
+ key: "subscribeToLocalSource",
430
+ value: function subscribeToLocalSource(resourceId, callback) {
431
+ var _this3 = this;
432
+ if (!isExperimentEnabled('editor-synced-block-same-page-sync')) {
433
+ return function () {};
434
+ }
435
+ var subscribers = this.localSourceSubscribers.get(resourceId);
436
+ if (!subscribers) {
437
+ subscribers = new Set();
438
+ this.localSourceSubscribers.set(resourceId, subscribers);
439
+ }
440
+ subscribers.add(callback);
441
+ callback(this.getLocalSourceSnapshot(resourceId));
442
+ return function () {
443
+ var currentSubscribers = _this3.localSourceSubscribers.get(resourceId);
444
+ currentSubscribers === null || currentSubscribers === void 0 || currentSubscribers.delete(callback);
445
+ if ((currentSubscribers === null || currentSubscribers === void 0 ? void 0 : currentSubscribers.size) === 0) {
446
+ _this3.localSourceSubscribers.delete(resourceId);
447
+ }
448
+ };
449
+ }
450
+ }, {
451
+ key: "notifyLocalSource",
452
+ value: function notifyLocalSource(resourceId) {
453
+ var _this$localSourceSubs;
454
+ var snapshot = this.getLocalSourceSnapshot(resourceId);
455
+ (_this$localSourceSubs = this.localSourceSubscribers.get(resourceId)) === null || _this$localSourceSubs === void 0 || _this$localSourceSubs.forEach(function (callback) {
456
+ return callback(snapshot);
457
+ });
458
+ }
403
459
 
404
460
  /**
405
461
  * Fires callback to insert node (if creation is successful) and clears pending creation data
@@ -446,6 +502,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
446
502
  var _this$fireAnalyticsEv6;
447
503
  // Delete the node from cache if fail to create so it's not flushed to BE
448
504
  this.syncBlockCache.delete(resourceId || '');
505
+ this.notifyLocalSource(resourceId);
449
506
  this.newSourceBlockResourceIds.delete(resourceId || '');
450
507
  // Creation failed, so there is no block to add content to — drop the
451
508
  // first-content tracking entry so a later unrelated edit cannot fire it.
@@ -479,10 +536,10 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
479
536
  }, {
480
537
  key: "registerConfirmationCallback",
481
538
  value: function registerConfirmationCallback(callback) {
482
- var _this3 = this;
539
+ var _this4 = this;
483
540
  this.confirmationCallback = callback;
484
541
  return function () {
485
- _this3.confirmationCallback = undefined;
542
+ _this4.confirmationCallback = undefined;
486
543
  };
487
544
  }
488
545
  }, {
@@ -522,7 +579,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
522
579
  }, {
523
580
  key: "createBodiedSyncBlockNode",
524
581
  value: function createBodiedSyncBlockNode(attrs, node, onCompletion, enrichment) {
525
- var _this4 = this;
582
+ var _this5 = this;
526
583
  if (this.viewMode === 'view') {
527
584
  return;
528
585
  }
@@ -552,6 +609,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
552
609
  var cached = this.syncBlockCache.get(resourceId);
553
610
  if (cached) {
554
611
  cached.status = 'unpublished';
612
+ this.notifyLocalSource(resourceId);
555
613
  }
556
614
  this.creationCompletionCallbacks.set(resourceId, onCompletion);
557
615
  (_this$createExperienc = this.createExperience) === null || _this$createExperienc === void 0 || _this$createExperienc.start({});
@@ -562,29 +620,29 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
562
620
  }).then(function (result) {
563
621
  var resourceId = result.resourceId || '';
564
622
  if (resourceId && !result.error) {
565
- var _this4$createExperien;
566
- _this4.commitPendingCreation(true, resourceId);
567
- (_this4$createExperien = _this4.createExperience) === null || _this4$createExperien === void 0 || _this4$createExperien.success();
623
+ var _this5$createExperien;
624
+ _this5.commitPendingCreation(true, resourceId);
625
+ (_this5$createExperien = _this5.createExperience) === null || _this5$createExperien === void 0 || _this5$createExperien.success();
568
626
  } else {
569
- var _this4$createExperien2, _this4$fireAnalyticsE;
570
- _this4.commitPendingCreation(false, resourceId);
571
- (_this4$createExperien2 = _this4.createExperience) === null || _this4$createExperien2 === void 0 || _this4$createExperien2.failure({
627
+ var _this5$createExperien2, _this5$fireAnalyticsE;
628
+ _this5.commitPendingCreation(false, resourceId);
629
+ (_this5$createExperien2 = _this5.createExperience) === null || _this5$createExperien2 === void 0 || _this5$createExperien2.failure({
572
630
  reason: result.error || 'Failed to create bodied sync block'
573
631
  });
574
- (_this4$fireAnalyticsE = _this4.fireAnalyticsEvent) === null || _this4$fireAnalyticsE === void 0 || _this4$fireAnalyticsE.call(_this4, createErrorPayload(result.error || 'Failed to create bodied sync block', resourceId, getSourceProductFromResourceIdSafe(resourceId), buildErrorAttribution(result.error, result.statusCode)));
632
+ (_this5$fireAnalyticsE = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE === void 0 || _this5$fireAnalyticsE.call(_this5, createErrorPayload(result.error || 'Failed to create bodied sync block', resourceId, getSourceProductFromResourceIdSafe(resourceId), buildErrorAttribution(result.error, result.statusCode)));
575
633
  }
576
634
  }).catch(function (error) {
577
- var _this4$createExperien3, _this4$fireAnalyticsE2;
578
- _this4.commitPendingCreation(false, resourceId);
635
+ var _this5$createExperien3, _this5$fireAnalyticsE2;
636
+ _this5.commitPendingCreation(false, resourceId);
579
637
  logException(error, {
580
638
  location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
581
639
  });
582
- (_this4$createExperien3 = _this4.createExperience) === null || _this4$createExperien3 === void 0 || _this4$createExperien3.failure({
640
+ (_this5$createExperien3 = _this5.createExperience) === null || _this5$createExperien3 === void 0 || _this5$createExperien3.failure({
583
641
  reason: error.message
584
642
  });
585
- (_this4$fireAnalyticsE2 = _this4.fireAnalyticsEvent) === null || _this4$fireAnalyticsE2 === void 0 || _this4$fireAnalyticsE2.call(_this4, createErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
643
+ (_this5$fireAnalyticsE2 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE2 === void 0 || _this5$fireAnalyticsE2.call(_this5, createErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
586
644
  }).finally(function () {
587
- _this4.pendingCreationPromises.delete(resourceId);
645
+ _this5.pendingCreationPromises.delete(resourceId);
588
646
  });
589
647
  this.pendingCreationPromises.set(resourceId, creationPromise);
590
648
  } catch (error) {
@@ -654,7 +712,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
654
712
  key: "delete",
655
713
  value: function () {
656
714
  var _delete2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(syncBlockIds, onDelete, onDeleteCompleted, reason, mechanism) {
657
- var _this5 = this;
715
+ var _this6 = this;
658
716
  var _this$deleteExperienc, results, callback, isDeleteSuccessful, _this$deleteExperienc2, _this$deleteExperienc3, attribution, _t2;
659
717
  return _regeneratorRuntime.wrap(function (_context2) {
660
718
  while (1) switch (_context2.prev = _context2.next) {
@@ -673,7 +731,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
673
731
  throw new Error('Data provider not set');
674
732
  case 2:
675
733
  syncBlockIds.forEach(function (Ids) {
676
- _this5.setPendingDeletion(Ids, true);
734
+ _this6.setPendingDeletion(Ids, true);
677
735
  });
678
736
  (_this$deleteExperienc = this.deleteExperience) === null || _this$deleteExperienc === void 0 || _this$deleteExperienc.start({});
679
737
  _context2.next = 3;
@@ -691,24 +749,25 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
691
749
  // Emit before the cache entry is deleted below, so blockInstanceId
692
750
  // is still available to emitDeleteSuccess.
693
751
  results.forEach(function (result) {
694
- _this5.emitDeleteSuccess(result.resourceId, reason, mechanism);
752
+ _this6.emitDeleteSuccess(result.resourceId, reason, mechanism);
695
753
  });
696
754
  callback = function callback(Ids) {
697
- return _this5.syncBlockCache.delete(Ids.resourceId);
755
+ _this6.syncBlockCache.delete(Ids.resourceId);
756
+ _this6.notifyLocalSource(Ids.resourceId);
698
757
  };
699
758
  this.clearPendingDeletion();
700
759
  (_this$deleteExperienc2 = this.deleteExperience) === null || _this$deleteExperienc2 === void 0 || _this$deleteExperienc2.success();
701
760
  } else {
702
761
  callback = function callback(Ids) {
703
- _this5.setPendingDeletion(Ids, false);
762
+ _this6.setPendingDeletion(Ids, false);
704
763
  };
705
764
  (_this$deleteExperienc3 = this.deleteExperience) === null || _this$deleteExperienc3 === void 0 || _this$deleteExperienc3.failure();
706
765
  results.forEach(function (result) {
707
766
  if (result.success) {
708
- _this5.emitDeleteSuccess(result.resourceId, reason, mechanism);
767
+ _this6.emitDeleteSuccess(result.resourceId, reason, mechanism);
709
768
  } else {
710
- var _this5$fireAnalyticsE;
711
- (_this5$fireAnalyticsE = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE === void 0 || _this5$fireAnalyticsE.call(_this5, deleteErrorPayload(result.error || 'Failed to delete synced block', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId), buildErrorAttribution(result.error, result.statusCode)));
769
+ var _this6$fireAnalyticsE;
770
+ (_this6$fireAnalyticsE = _this6.fireAnalyticsEvent) === null || _this6$fireAnalyticsE === void 0 || _this6$fireAnalyticsE.call(_this6, deleteErrorPayload(result.error || 'Failed to delete synced block', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId), buildErrorAttribution(result.error, result.statusCode)));
712
771
  }
713
772
  });
714
773
  }
@@ -721,9 +780,9 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
721
780
  // so the attribution `reason` falls back to `unknown`.
722
781
  attribution = buildErrorAttribution();
723
782
  syncBlockIds.forEach(function (Ids) {
724
- var _this5$fireAnalyticsE2;
725
- _this5.setPendingDeletion(Ids, false);
726
- (_this5$fireAnalyticsE2 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE2 === void 0 || _this5$fireAnalyticsE2.call(_this5, deleteErrorPayload(_t2.message, Ids.resourceId, getSourceProductFromResourceIdSafe(Ids.resourceId), attribution));
783
+ var _this6$fireAnalyticsE2;
784
+ _this6.setPendingDeletion(Ids, false);
785
+ (_this6$fireAnalyticsE2 = _this6.fireAnalyticsEvent) === null || _this6$fireAnalyticsE2 === void 0 || _this6$fireAnalyticsE2.call(_this6, deleteErrorPayload(_t2.message, Ids.resourceId, getSourceProductFromResourceIdSafe(Ids.resourceId), attribution));
727
786
  });
728
787
  logException(_t2, {
729
788
  location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
@@ -801,7 +860,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
801
860
  key: "fetchAndCacheStatuses",
802
861
  value: (function () {
803
862
  var _fetchAndCacheStatuses = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
804
- var _this6 = this;
863
+ var _this7 = this;
805
864
  var sourceToReferenceMap, syncBlockNodes, results, _iterator3, _step3, _sourceToReferenceMap, _result$data, result, sourceResourceId, cached, _t3;
806
865
  return _regeneratorRuntime.wrap(function (_context4) {
807
866
  while (1) switch (_context4.prev = _context4.next) {
@@ -819,11 +878,11 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
819
878
  // so we can update the correct cache entries.
820
879
  sourceToReferenceMap = new Map();
821
880
  syncBlockNodes = Array.from(this.syncBlockCache.entries()).map(function (_ref) {
822
- var _this6$dataProvider$g, _this6$dataProvider;
881
+ var _this7$dataProvider$g, _this7$dataProvider;
823
882
  var _ref2 = _slicedToArray(_ref, 2),
824
883
  resourceId = _ref2[0],
825
884
  data = _ref2[1];
826
- 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;
885
+ 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;
827
886
  sourceToReferenceMap.set(referenceResourceId, resourceId);
828
887
  return {
829
888
  type: 'bodiedSyncBlock',
@@ -847,6 +906,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
847
906
  cached = this.syncBlockCache.get(sourceResourceId);
848
907
  if (cached && (_result$data = result.data) !== null && _result$data !== void 0 && _result$data.status) {
849
908
  cached.status = result.data.status;
909
+ this.notifyLocalSource(sourceResourceId);
850
910
  }
851
911
  }
852
912
  } catch (err) {
@@ -976,7 +1036,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
976
1036
  }, {
977
1037
  key: "getSyncBlockSourceInfo",
978
1038
  value: function getSyncBlockSourceInfo(localId) {
979
- var _this7 = this;
1039
+ var _this8 = this;
980
1040
  try {
981
1041
  var _this$fetchSourceInfo;
982
1042
  if (!this.dataProvider) {
@@ -985,13 +1045,13 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
985
1045
  (_this$fetchSourceInfo = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo === void 0 || _this$fetchSourceInfo.start();
986
1046
  return this.dataProvider.fetchSyncBlockSourceInfo(localId, undefined, undefined).then(function (sourceInfo) {
987
1047
  if (!sourceInfo) {
988
- var _this7$fetchSourceInf;
989
- (_this7$fetchSourceInf = _this7.fetchSourceInfoExperience) === null || _this7$fetchSourceInf === void 0 || _this7$fetchSourceInf.failure({
1048
+ var _this8$fetchSourceInf;
1049
+ (_this8$fetchSourceInf = _this8.fetchSourceInfoExperience) === null || _this8$fetchSourceInf === void 0 || _this8$fetchSourceInf.failure({
990
1050
  reason: 'No source info returned'
991
1051
  });
992
1052
  } else {
993
- var _this7$fetchSourceInf2;
994
- (_this7$fetchSourceInf2 = _this7.fetchSourceInfoExperience) === null || _this7$fetchSourceInf2 === void 0 || _this7$fetchSourceInf2.success();
1053
+ var _this8$fetchSourceInf2;
1054
+ (_this8$fetchSourceInf2 = _this8.fetchSourceInfoExperience) === null || _this8$fetchSourceInf2 === void 0 || _this8$fetchSourceInf2.success();
995
1055
  }
996
1056
  return sourceInfo;
997
1057
  });
@@ -1026,8 +1086,17 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
1026
1086
  }, {
1027
1087
  key: "destroy",
1028
1088
  value: function destroy() {
1029
- var _this$saveExperience4, _this$createExperienc2, _this$deleteExperienc4, _this$fetchSourceInfo2;
1089
+ var _this9 = this,
1090
+ _this$saveExperience4,
1091
+ _this$createExperienc2,
1092
+ _this$deleteExperienc4,
1093
+ _this$fetchSourceInfo2;
1094
+ var subscribedResourceIds = _toConsumableArray(this.localSourceSubscribers.keys());
1030
1095
  this.syncBlockCache.clear();
1096
+ subscribedResourceIds.forEach(function (resourceId) {
1097
+ return _this9.notifyLocalSource(resourceId);
1098
+ });
1099
+ this.localSourceSubscribers.clear();
1031
1100
  this.confirmationCallback = undefined;
1032
1101
  this.creationCompletionCallbacks.clear();
1033
1102
  this.flushCompletionCallback = undefined;
@@ -24,10 +24,8 @@ import { SourceSyncBlockStoreManager } from './sourceSyncBlockStoreManager';
24
24
  export var SyncBlockStoreManager = /*#__PURE__*/function () {
25
25
  function SyncBlockStoreManager(dataProvider, viewMode, isLivePage) {
26
26
  _classCallCheck(this, SyncBlockStoreManager);
27
- // In future, if reference manager needs to reach to source manager and read its current in memory cache
28
- // we can pass the source manager as a parameter to the reference manager constructor
29
27
  this.sourceSyncBlockStoreManager = new SourceSyncBlockStoreManager(dataProvider, viewMode, isLivePage);
30
- this.referenceSyncBlockStoreManager = new ReferenceSyncBlockStoreManager(dataProvider, viewMode);
28
+ this.referenceSyncBlockStoreManager = new ReferenceSyncBlockStoreManager(dataProvider, viewMode, this.sourceSyncBlockStoreManager);
31
29
  this.dataProvider = dataProvider;
32
30
  this.referenceSyncBlockStoreManager.setRealTimeSubscriptionsEnabled(true);
33
31
  }
@@ -1 +1,2 @@
1
1
  export { SyncBlockStoreManager, useMemoizedSyncBlockStoreManager, } from '../store-manager/syncBlockStoreManager';
2
+ export type { SourceSyncBlockStoreManager } from '../store-manager/sourceSyncBlockStoreManager';