@atlaskit/editor-synced-block-provider 3.12.0 → 3.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/cjs/clients/block-service/blockSubscription.js +124 -0
  3. package/dist/cjs/clients/jira/sourceInfo.js +152 -0
  4. package/dist/cjs/providers/block-service/blockServiceAPI.js +43 -6
  5. package/dist/cjs/providers/syncBlockProvider.js +40 -8
  6. package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +393 -204
  7. package/dist/cjs/store-manager/sourceSyncBlockStoreManager.js +95 -124
  8. package/dist/cjs/store-manager/syncBlockStoreManager.js +2 -2
  9. package/dist/cjs/utils/errorHandling.js +79 -19
  10. package/dist/cjs/utils/experienceTracking.js +119 -0
  11. package/dist/cjs/utils/resolveSyncBlockInstance.js +1 -1
  12. package/dist/es2019/clients/block-service/blockSubscription.js +125 -0
  13. package/dist/es2019/clients/jira/sourceInfo.js +87 -0
  14. package/dist/es2019/providers/block-service/blockServiceAPI.js +40 -5
  15. package/dist/es2019/providers/syncBlockProvider.js +26 -2
  16. package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +286 -139
  17. package/dist/es2019/store-manager/sourceSyncBlockStoreManager.js +80 -89
  18. package/dist/es2019/store-manager/syncBlockStoreManager.js +2 -2
  19. package/dist/es2019/utils/errorHandling.js +61 -10
  20. package/dist/es2019/utils/experienceTracking.js +113 -0
  21. package/dist/es2019/utils/resolveSyncBlockInstance.js +1 -1
  22. package/dist/esm/clients/block-service/blockSubscription.js +118 -0
  23. package/dist/esm/clients/jira/sourceInfo.js +147 -0
  24. package/dist/esm/providers/block-service/blockServiceAPI.js +43 -6
  25. package/dist/esm/providers/syncBlockProvider.js +38 -6
  26. package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +394 -205
  27. package/dist/esm/store-manager/sourceSyncBlockStoreManager.js +96 -125
  28. package/dist/esm/store-manager/syncBlockStoreManager.js +2 -2
  29. package/dist/esm/utils/errorHandling.js +77 -18
  30. package/dist/esm/utils/experienceTracking.js +113 -0
  31. package/dist/esm/utils/resolveSyncBlockInstance.js +1 -1
  32. package/dist/types/clients/block-service/blockService.d.ts +2 -2
  33. package/dist/types/clients/block-service/blockSubscription.d.ts +38 -0
  34. package/dist/types/clients/jira/sourceInfo.d.ts +2 -0
  35. package/dist/types/common/types.d.ts +4 -2
  36. package/dist/types/index.d.ts +2 -2
  37. package/dist/types/providers/block-service/blockServiceAPI.d.ts +8 -0
  38. package/dist/types/providers/syncBlockProvider.d.ts +9 -1
  39. package/dist/types/providers/types.d.ts +22 -6
  40. package/dist/types/store-manager/referenceSyncBlockStoreManager.d.ts +59 -1
  41. package/dist/types/store-manager/sourceSyncBlockStoreManager.d.ts +1 -5
  42. package/dist/types/utils/errorHandling.d.ts +14 -9
  43. package/dist/types/utils/experienceTracking.d.ts +51 -0
  44. package/dist/types-ts4.5/clients/block-service/blockService.d.ts +2 -2
  45. package/dist/types-ts4.5/clients/block-service/blockSubscription.d.ts +38 -0
  46. package/dist/types-ts4.5/clients/jira/sourceInfo.d.ts +2 -0
  47. package/dist/types-ts4.5/common/types.d.ts +4 -2
  48. package/dist/types-ts4.5/index.d.ts +2 -2
  49. package/dist/types-ts4.5/providers/block-service/blockServiceAPI.d.ts +8 -0
  50. package/dist/types-ts4.5/providers/syncBlockProvider.d.ts +9 -1
  51. package/dist/types-ts4.5/providers/types.d.ts +22 -6
  52. package/dist/types-ts4.5/store-manager/referenceSyncBlockStoreManager.d.ts +59 -1
  53. package/dist/types-ts4.5/store-manager/sourceSyncBlockStoreManager.d.ts +1 -5
  54. package/dist/types-ts4.5/utils/errorHandling.d.ts +14 -9
  55. package/dist/types-ts4.5/utils/experienceTracking.d.ts +51 -0
  56. package/package.json +2 -1
@@ -3,7 +3,8 @@ import { logException } from '@atlaskit/editor-common/monitoring';
3
3
  import { ProviderFactory } from '@atlaskit/editor-common/provider-factory';
4
4
  import { fg } from '@atlaskit/platform-feature-flags';
5
5
  import { SyncBlockError } from '../common/types';
6
- import { fetchErrorPayload, getSourceInfoErrorPayload, updateReferenceErrorPayload } from '../utils/errorHandling';
6
+ import { fetchErrorPayload, fetchSuccessPayload, getSourceInfoErrorPayload, updateReferenceErrorPayload } from '../utils/errorHandling';
7
+ import { getFetchExperience, getFetchSourceInfoExperience, getSaveReferenceExperience } from '../utils/experienceTracking';
7
8
  import { resolveSyncBlockInstance } from '../utils/resolveSyncBlockInstance';
8
9
  import { parseResourceId } from '../utils/resourceId';
9
10
  import { createSyncBlockNode } from '../utils/utils';
@@ -14,17 +15,16 @@ import { createSyncBlockNode } from '../utils/utils';
14
15
  // Handles fetching source URL and title for sync blocks.
15
16
  // Can be used in both editor and renderer contexts.
16
17
  export class ReferenceSyncBlockStoreManager {
17
- // Track pending cache deletions to handle block moves (unmount/remount)
18
- // When a block is moved, the old component unmounts before the new one mounts,
19
- // causing the cache to be deleted prematurely. We delay deletion to allow
20
- // the new component to subscribe and cancel the pending deletion.
18
+ // Listeners for subscription changes (used by React components to know when to update)
21
19
 
22
20
  constructor(dataProvider) {
23
21
  // Keeps track of addition and deletion of reference synced blocks on the document
24
22
  // This starts as true to always flush the cache when document is saved for the first time
25
- // to cater the case when a editor seesion is closed without document being updated right after reference block is deleted
23
+ // to cater the case when a editor session is closed without document being updated right after reference block is deleted
26
24
  _defineProperty(this, "isCacheDirty", true);
27
25
  _defineProperty(this, "isRefreshingSubscriptions", false);
26
+ // Flag to indicate if real-time subscriptions are enabled
27
+ _defineProperty(this, "useRealTimeSubscriptions", false);
28
28
  this.syncBlockCache = new Map();
29
29
  this.subscriptions = new Map();
30
30
  this.titleSubscriptions = new Map();
@@ -34,18 +34,96 @@ export class ReferenceSyncBlockStoreManager {
34
34
  this.syncBlockSourceInfoRequests = new Map();
35
35
  this.providerFactories = new Map();
36
36
  this.pendingCacheDeletions = new Map();
37
+ this.graphqlSubscriptions = new Map();
38
+ this.subscriptionChangeListeners = new Set();
37
39
  }
38
- setFireAnalyticsEvent(fireAnalyticsEvent) {
39
- this.fireAnalyticsEvent = fireAnalyticsEvent;
40
+
41
+ /**
42
+ * Enables or disables real-time GraphQL subscriptions for block updates.
43
+ * When enabled, the store manager will subscribe to real-time updates
44
+ * instead of relying on polling.
45
+ * @param enabled - Whether to enable real-time subscriptions
46
+ */
47
+ setRealTimeSubscriptionsEnabled(enabled) {
48
+ if (this.useRealTimeSubscriptions === enabled) {
49
+ return;
50
+ }
51
+ this.useRealTimeSubscriptions = enabled;
52
+ if (enabled) {
53
+ // Set up subscriptions for all currently subscribed blocks
54
+ this.setupGraphQLSubscriptionsForAllBlocks();
55
+ } else {
56
+ // Clean up all GraphQL subscriptions
57
+ this.cleanupAllGraphQLSubscriptions();
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Checks if real-time subscriptions are currently enabled.
63
+ */
64
+ isRealTimeSubscriptionsEnabled() {
65
+ return this.useRealTimeSubscriptions;
66
+ }
67
+
68
+ /**
69
+ * Returns all resource IDs that are currently subscribed to.
70
+ * Used by React components to render subscription components.
71
+ */
72
+ getSubscribedResourceIds() {
73
+ return Array.from(this.subscriptions.keys());
74
+ }
75
+
76
+ /**
77
+ * Registers a listener that will be called when subscriptions change.
78
+ * @param listener - Callback function to invoke when subscriptions change
79
+ * @returns Unsubscribe function to remove the listener
80
+ */
81
+ onSubscriptionsChanged(listener) {
82
+ this.subscriptionChangeListeners.add(listener);
83
+ return () => {
84
+ this.subscriptionChangeListeners.delete(listener);
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Notifies all subscription change listeners.
90
+ */
91
+ notifySubscriptionChangeListeners() {
92
+ this.subscriptionChangeListeners.forEach(listener => {
93
+ try {
94
+ listener();
95
+ } catch (error) {
96
+ var _this$fireAnalyticsEv;
97
+ logException(error, {
98
+ location: 'editor-synced-block-provider/referenceSyncBlockStoreManager/notifySubscriptionChangeListeners'
99
+ });
100
+ (_this$fireAnalyticsEv = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv === void 0 ? void 0 : _this$fireAnalyticsEv.call(this, fetchErrorPayload(error.message));
101
+ }
102
+ });
40
103
  }
41
- setExperiences(fetchExperience, fetchSourceInfoExperience, saveExperience) {
42
- // don't reset experiences after they have already been set
43
- if (!this.fetchExperience || !this.fetchSourceInfoExperience || !this.saveExperience) {
44
- this.fetchExperience = fetchExperience;
45
- this.fetchSourceInfoExperience = fetchSourceInfoExperience;
46
- this.saveExperience = saveExperience;
104
+
105
+ /**
106
+ * Handles incoming data from a GraphQL subscription.
107
+ * Called by React subscription components when they receive updates.
108
+ * @param syncBlockInstance - The updated sync block instance
109
+ */
110
+ handleSubscriptionUpdate(syncBlockInstance) {
111
+ if (!syncBlockInstance.resourceId) {
112
+ return;
113
+ }
114
+ const existingSyncBlock = this.getFromCache(syncBlockInstance.resourceId);
115
+ const resolvedSyncBlockInstance = existingSyncBlock ? resolveSyncBlockInstance(existingSyncBlock, syncBlockInstance) : syncBlockInstance;
116
+ this.updateCache(resolvedSyncBlockInstance);
117
+ if (!syncBlockInstance.error) {
118
+ this.fetchSyncBlockSourceInfo(resolvedSyncBlockInstance.resourceId);
47
119
  }
48
120
  }
121
+ setFireAnalyticsEvent(fireAnalyticsEvent) {
122
+ this.fireAnalyticsEvent = fireAnalyticsEvent;
123
+ this.fetchExperience = getFetchExperience(fireAnalyticsEvent);
124
+ this.fetchSourceInfoExperience = getFetchSourceInfoExperience(fireAnalyticsEvent);
125
+ this.saveExperience = getSaveReferenceExperience(fireAnalyticsEvent);
126
+ }
49
127
  generateResourceIdForReference(sourceId) {
50
128
  if (!this.dataProvider) {
51
129
  throw new Error('Data provider not set');
@@ -63,9 +141,14 @@ export class ReferenceSyncBlockStoreManager {
63
141
 
64
142
  /**
65
143
  * Refreshes the subscriptions for all sync blocks.
144
+ * This is a fallback polling mechanism when real-time subscriptions are not enabled.
66
145
  * @returns {Promise<void>}
67
146
  */
68
147
  async refreshSubscriptions() {
148
+ // Skip polling refresh if real-time subscriptions are enabled
149
+ if (this.useRealTimeSubscriptions) {
150
+ return;
151
+ }
69
152
  if (this.isRefreshingSubscriptions) {
70
153
  return;
71
154
  }
@@ -81,30 +164,93 @@ export class ReferenceSyncBlockStoreManager {
81
164
  // this function will update the cache and call the subscriptions
82
165
  await this.fetchSyncBlocksData(syncBlocks);
83
166
  } catch (error) {
167
+ var _this$fireAnalyticsEv2;
84
168
  logException(error, {
85
169
  location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
86
170
  });
87
- if (fg('platform_synced_block_dogfooding')) {
88
- var _this$fetchExperience;
89
- (_this$fetchExperience = this.fetchExperience) === null || _this$fetchExperience === void 0 ? void 0 : _this$fetchExperience.failure({
90
- reason: error.message
91
- });
92
- } else {
93
- var _this$fireAnalyticsEv;
94
- (_this$fireAnalyticsEv = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv === void 0 ? void 0 : _this$fireAnalyticsEv.call(this, fetchErrorPayload(error.message));
95
- }
171
+ (_this$fireAnalyticsEv2 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv2 === void 0 ? void 0 : _this$fireAnalyticsEv2.call(this, fetchErrorPayload(error.message));
96
172
  } finally {
97
173
  this.isRefreshingSubscriptions = false;
98
174
  }
99
175
  }
176
+
177
+ /**
178
+ * Sets up a GraphQL subscription for a specific block.
179
+ * @param resourceId - The resource ID of the block to subscribe to
180
+ */
181
+ setupGraphQLSubscription(resourceId) {
182
+ var _this$dataProvider2;
183
+ // Don't set up duplicate subscriptions
184
+ if (this.graphqlSubscriptions.has(resourceId)) {
185
+ return;
186
+ }
187
+ if (!((_this$dataProvider2 = this.dataProvider) !== null && _this$dataProvider2 !== void 0 && _this$dataProvider2.subscribeToBlockUpdates)) {
188
+ return;
189
+ }
190
+ const unsubscribe = this.dataProvider.subscribeToBlockUpdates(resourceId, syncBlockInstance => {
191
+ // Handle the subscription update
192
+ this.handleGraphQLSubscriptionUpdate(syncBlockInstance);
193
+ }, error => {
194
+ var _this$fireAnalyticsEv3;
195
+ logException(error, {
196
+ location: 'editor-synced-block-provider/referenceSyncBlockStoreManager/graphql-subscription'
197
+ });
198
+ (_this$fireAnalyticsEv3 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv3 === void 0 ? void 0 : _this$fireAnalyticsEv3.call(this, fetchErrorPayload(error.message));
199
+ });
200
+ if (unsubscribe) {
201
+ this.graphqlSubscriptions.set(resourceId, unsubscribe);
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Handles updates received from GraphQL subscriptions.
207
+ * @param syncBlockInstance - The updated sync block instance
208
+ */
209
+ handleGraphQLSubscriptionUpdate(syncBlockInstance) {
210
+ if (!syncBlockInstance.resourceId) {
211
+ return;
212
+ }
213
+ const existingSyncBlock = this.getFromCache(syncBlockInstance.resourceId);
214
+ const resolvedSyncBlockInstance = existingSyncBlock ? resolveSyncBlockInstance(existingSyncBlock, syncBlockInstance) : syncBlockInstance;
215
+ this.updateCache(resolvedSyncBlockInstance);
216
+ if (!syncBlockInstance.error) {
217
+ this.fetchSyncBlockSourceInfo(resolvedSyncBlockInstance.resourceId);
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Cleans up the GraphQL subscription for a specific block.
223
+ * @param resourceId - The resource ID of the block to unsubscribe from
224
+ */
225
+ cleanupGraphQLSubscription(resourceId) {
226
+ const unsubscribe = this.graphqlSubscriptions.get(resourceId);
227
+ if (unsubscribe) {
228
+ unsubscribe();
229
+ this.graphqlSubscriptions.delete(resourceId);
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Sets up GraphQL subscriptions for all currently subscribed blocks.
235
+ */
236
+ setupGraphQLSubscriptionsForAllBlocks() {
237
+ for (const resourceId of this.subscriptions.keys()) {
238
+ this.setupGraphQLSubscription(resourceId);
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Cleans up all GraphQL subscriptions.
244
+ */
245
+ cleanupAllGraphQLSubscriptions() {
246
+ for (const unsubscribe of this.graphqlSubscriptions.values()) {
247
+ unsubscribe();
248
+ }
249
+ this.graphqlSubscriptions.clear();
250
+ }
100
251
  fetchSyncBlockSourceInfo(resourceId) {
101
252
  try {
102
253
  if (!resourceId || !this.dataProvider) {
103
- // make sure experience has been started before throwing error
104
- if (fg('platform_synced_block_dogfooding')) {
105
- var _this$fetchSourceInfo;
106
- (_this$fetchSourceInfo = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo === void 0 ? void 0 : _this$fetchSourceInfo.start({});
107
- }
108
254
  throw new Error('Data provider or resourceId not set');
109
255
  }
110
256
  if (fg('platform_synced_block_dogfooding')) {
@@ -117,10 +263,6 @@ export class ReferenceSyncBlockStoreManager {
117
263
  }
118
264
  const existingSyncBlock = this.getFromCache(resourceId);
119
265
  if (!existingSyncBlock) {
120
- if (fg('platform_synced_block_dogfooding')) {
121
- var _this$fetchSourceInfo2;
122
- (_this$fetchSourceInfo2 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo2 === void 0 ? void 0 : _this$fetchSourceInfo2.start({});
123
- }
124
266
  throw new Error('No existing sync block to fetch source info for');
125
267
  }
126
268
  const {
@@ -129,7 +271,7 @@ export class ReferenceSyncBlockStoreManager {
129
271
  blockInstanceId,
130
272
  sourceURL,
131
273
  sourceTitle,
132
- onSamePage,
274
+ onSameDocument,
133
275
  sourceSubType
134
276
  } = existingSyncBlock.data || {};
135
277
  // skip if source URL and title are already present
@@ -140,38 +282,30 @@ export class ReferenceSyncBlockStoreManager {
140
282
  url: sourceURL,
141
283
  subType: sourceSubType,
142
284
  sourceAri: sourceAri || '',
143
- onSamePage,
285
+ onSameDocument,
144
286
  productType: product
145
287
  });
146
288
  } else {
147
289
  return Promise.resolve(undefined);
148
290
  }
149
291
  }
150
-
151
- // only start experience if there is data to fetch
152
- if (fg('platform_synced_block_dogfooding')) {
153
- var _this$fetchSourceInfo3;
154
- (_this$fetchSourceInfo3 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo3 === void 0 ? void 0 : _this$fetchSourceInfo3.start({});
155
- }
156
292
  if (!sourceAri || !product || !blockInstanceId) {
157
- if (fg('platform_synced_block_dogfooding')) {
158
- var _this$fetchSourceInfo4;
159
- (_this$fetchSourceInfo4 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo4 === void 0 ? void 0 : _this$fetchSourceInfo4.failure({
160
- reason: 'SourceAri, product or blockInstanceId missing'
161
- });
162
- } else {
163
- var _this$fireAnalyticsEv2;
164
- (_this$fireAnalyticsEv2 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv2 === void 0 ? void 0 : _this$fireAnalyticsEv2.call(this, getSourceInfoErrorPayload('SourceAri, product or blockInstanceId missing'));
165
- }
293
+ var _this$fireAnalyticsEv4;
294
+ (_this$fireAnalyticsEv4 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv4 === void 0 ? void 0 : _this$fireAnalyticsEv4.call(this, getSourceInfoErrorPayload('SourceAri, product or blockInstanceId missing', resourceId));
166
295
  return Promise.resolve(undefined);
167
296
  }
297
+ if (fg('platform_synced_block_dogfooding')) {
298
+ var _this$fetchSourceInfo;
299
+ (_this$fetchSourceInfo = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo === void 0 ? void 0 : _this$fetchSourceInfo.start({});
300
+ }
168
301
  const sourceInfoPromise = this.dataProvider.fetchSyncBlockSourceInfo(blockInstanceId, sourceAri, product, this.fireAnalyticsEvent).then(sourceInfo => {
169
302
  if (!sourceInfo) {
170
303
  if (fg('platform_synced_block_dogfooding')) {
171
- var _this$fetchSourceInfo5;
172
- (_this$fetchSourceInfo5 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo5 === void 0 ? void 0 : _this$fetchSourceInfo5.failure({
304
+ var _this$fetchSourceInfo2, _this$fireAnalyticsEv5;
305
+ (_this$fetchSourceInfo2 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo2 === void 0 ? void 0 : _this$fetchSourceInfo2.failure({
173
306
  reason: 'No source info returned'
174
307
  });
308
+ (_this$fireAnalyticsEv5 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv5 === void 0 ? void 0 : _this$fireAnalyticsEv5.call(this, getSourceInfoErrorPayload('No source info returned', resourceId));
175
309
  }
176
310
  return undefined;
177
311
  }
@@ -181,26 +315,26 @@ export class ReferenceSyncBlockStoreManager {
181
315
  }
182
316
  if (fg('platform_synced_block_dogfooding')) {
183
317
  if (sourceInfo.title && sourceInfo.url) {
184
- var _this$fetchSourceInfo6;
185
- (_this$fetchSourceInfo6 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo6 === void 0 ? void 0 : _this$fetchSourceInfo6.success();
318
+ var _this$fetchSourceInfo3;
319
+ (_this$fetchSourceInfo3 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo3 === void 0 ? void 0 : _this$fetchSourceInfo3.success();
186
320
  } else {
187
- var _this$fetchSourceInfo7;
188
- (_this$fetchSourceInfo7 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo7 === void 0 ? void 0 : _this$fetchSourceInfo7.failure({
321
+ var _this$fetchSourceInfo4, _this$fireAnalyticsEv6;
322
+ (_this$fetchSourceInfo4 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo4 === void 0 ? void 0 : _this$fetchSourceInfo4.failure({
189
323
  reason: 'Missing title or url'
190
324
  });
325
+ (_this$fireAnalyticsEv6 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv6 === void 0 ? void 0 : _this$fireAnalyticsEv6.call(this, getSourceInfoErrorPayload('Missing title or url', resourceId));
191
326
  }
192
327
  return sourceInfo;
193
328
  }
194
329
  }).catch(error => {
330
+ var _this$fireAnalyticsEv7;
195
331
  if (fg('platform_synced_block_dogfooding')) {
196
- var _this$fetchSourceInfo8;
197
- (_this$fetchSourceInfo8 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo8 === void 0 ? void 0 : _this$fetchSourceInfo8.failure({
332
+ var _this$fetchSourceInfo5;
333
+ (_this$fetchSourceInfo5 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo5 === void 0 ? void 0 : _this$fetchSourceInfo5.failure({
198
334
  reason: error.message
199
335
  });
200
- } else {
201
- var _this$fireAnalyticsEv3;
202
- (_this$fireAnalyticsEv3 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv3 === void 0 ? void 0 : _this$fireAnalyticsEv3.call(this, getSourceInfoErrorPayload(error.message));
203
336
  }
337
+ (_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 ? void 0 : _this$fireAnalyticsEv7.call(this, getSourceInfoErrorPayload(error.message, resourceId));
204
338
  return undefined;
205
339
  }).finally(() => {
206
340
  if (fg('platform_synced_block_dogfooding')) {
@@ -216,18 +350,11 @@ export class ReferenceSyncBlockStoreManager {
216
350
  this.syncBlockSourceInfoRequestsOld.set(resourceId, true);
217
351
  }
218
352
  } catch (error) {
353
+ var _this$fireAnalyticsEv8;
219
354
  logException(error, {
220
355
  location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
221
356
  });
222
- if (fg('platform_synced_block_dogfooding')) {
223
- var _this$fetchSourceInfo9;
224
- (_this$fetchSourceInfo9 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo9 === void 0 ? void 0 : _this$fetchSourceInfo9.failure({
225
- reason: error.message
226
- });
227
- } else {
228
- var _this$fireAnalyticsEv4;
229
- (_this$fireAnalyticsEv4 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv4 === void 0 ? void 0 : _this$fireAnalyticsEv4.call(this, getSourceInfoErrorPayload(error.message));
230
- }
357
+ (_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 ? void 0 : _this$fireAnalyticsEv8.call(this, getSourceInfoErrorPayload(error.message, resourceId));
231
358
  }
232
359
  return Promise.resolve(undefined);
233
360
  }
@@ -257,36 +384,28 @@ export class ReferenceSyncBlockStoreManager {
257
384
  if (nodesToFetch.length === 0) {
258
385
  return;
259
386
  }
260
-
261
- // only start fetch experience if there is data to fetch
262
- if (fg('platform_synced_block_dogfooding')) {
263
- var _this$fetchExperience2;
264
- (_this$fetchExperience2 = this.fetchExperience) === null || _this$fetchExperience2 === void 0 ? void 0 : _this$fetchExperience2.start({});
265
- }
266
387
  if (!this.dataProvider) {
267
388
  throw new Error('Data provider not set');
268
389
  }
269
390
  nodesToFetch.forEach(node => {
270
391
  this.syncBlockFetchDataRequests.set(node.attrs.resourceId, true);
271
392
  });
393
+ if (fg('platform_synced_block_dogfooding')) {
394
+ var _this$fetchExperience;
395
+ (_this$fetchExperience = this.fetchExperience) === null || _this$fetchExperience === void 0 ? void 0 : _this$fetchExperience.start({});
396
+ }
272
397
  const data = await this.dataProvider.fetchNodesData(nodesToFetch).finally(() => {
273
398
  nodesToFetch.forEach(node => {
274
399
  this.syncBlockFetchDataRequests.delete(node.attrs.resourceId);
275
400
  });
276
401
  });
277
402
  const resolvedData = [];
278
- const successfulFetched = [];
279
- const failedFetch = [];
403
+ let hasUnexpectedError = false;
404
+ let hasExpectedError = false;
280
405
  data.forEach(syncBlockInstance => {
281
406
  if (!syncBlockInstance.resourceId) {
282
- if (fg('platform_synced_block_dogfooding')) {
283
- failedFetch.push({
284
- reason: syncBlockInstance.error || 'Returned sync block instance does not have resource id'
285
- });
286
- } else {
287
- var _this$fireAnalyticsEv5;
288
- (_this$fireAnalyticsEv5 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv5 === void 0 ? void 0 : _this$fireAnalyticsEv5.call(this, fetchErrorPayload(syncBlockInstance.error || 'Returned sync block instance does not have resource id'));
289
- }
407
+ var _this$fireAnalyticsEv9;
408
+ (_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 ? void 0 : _this$fireAnalyticsEv9.call(this, fetchErrorPayload(syncBlockInstance.error || 'Returned sync block instance does not have resource id'));
290
409
  return;
291
410
  }
292
411
  const existingSyncBlock = this.getFromCache(syncBlockInstance.resourceId);
@@ -294,36 +413,34 @@ export class ReferenceSyncBlockStoreManager {
294
413
  this.updateCache(resolvedSyncBlockInstance);
295
414
  resolvedData.push(resolvedSyncBlockInstance);
296
415
  if (syncBlockInstance.error) {
297
- if (fg('platform_synced_block_dogfooding')) {
298
- failedFetch.push({
299
- reason: syncBlockInstance.error,
300
- resourceId: syncBlockInstance.resourceId
301
- });
302
- } else {
303
- var _this$fireAnalyticsEv6;
304
- (_this$fireAnalyticsEv6 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv6 === void 0 ? void 0 : _this$fireAnalyticsEv6.call(this, fetchErrorPayload(syncBlockInstance.error));
416
+ var _this$fireAnalyticsEv0;
417
+ (_this$fireAnalyticsEv0 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv0 === void 0 ? void 0 : _this$fireAnalyticsEv0.call(this, fetchErrorPayload(syncBlockInstance.error, syncBlockInstance.resourceId));
418
+ if (syncBlockInstance.error === SyncBlockError.NotFound || syncBlockInstance.error === SyncBlockError.Forbidden) {
419
+ hasExpectedError = true;
420
+ } else if (syncBlockInstance.error) {
421
+ hasUnexpectedError = true;
305
422
  }
306
423
  return;
424
+ } else if (fg('platform_synced_block_dogfooding')) {
425
+ var _this$fireAnalyticsEv1, _syncBlockInstance$da, _syncBlockInstance$da2;
426
+ (_this$fireAnalyticsEv1 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv1 === void 0 ? void 0 : _this$fireAnalyticsEv1.call(this, fetchSuccessPayload(syncBlockInstance.resourceId, (_syncBlockInstance$da = syncBlockInstance.data) === null || _syncBlockInstance$da === void 0 ? void 0 : _syncBlockInstance$da.blockInstanceId, (_syncBlockInstance$da2 = syncBlockInstance.data) === null || _syncBlockInstance$da2 === void 0 ? void 0 : _syncBlockInstance$da2.product));
307
427
  }
308
- successfulFetched.push(syncBlockInstance.resourceId);
309
428
  this.fetchSyncBlockSourceInfo(resolvedSyncBlockInstance.resourceId);
310
429
  });
311
430
  if (fg('platform_synced_block_dogfooding')) {
312
- if (data.every(syncBlockInstance => syncBlockInstance.resourceId && !syncBlockInstance.error)) {
431
+ if (hasUnexpectedError) {
432
+ var _this$fetchExperience2;
433
+ (_this$fetchExperience2 = this.fetchExperience) === null || _this$fetchExperience2 === void 0 ? void 0 : _this$fetchExperience2.failure({
434
+ reason: 'Unexpected error during fetch'
435
+ });
436
+ } else if (hasExpectedError) {
313
437
  var _this$fetchExperience3;
314
- (_this$fetchExperience3 = this.fetchExperience) === null || _this$fetchExperience3 === void 0 ? void 0 : _this$fetchExperience3.success({
315
- metadata: {
316
- successfulFetched
317
- }
438
+ (_this$fetchExperience3 = this.fetchExperience) === null || _this$fetchExperience3 === void 0 ? void 0 : _this$fetchExperience3.abort({
439
+ reason: 'Expected error: NotFound or PermissionDenied'
318
440
  });
319
441
  } else {
320
442
  var _this$fetchExperience4;
321
- (_this$fetchExperience4 = this.fetchExperience) === null || _this$fetchExperience4 === void 0 ? void 0 : _this$fetchExperience4.failure({
322
- metadata: {
323
- successfulFetched,
324
- failedFetch
325
- }
326
- });
443
+ (_this$fetchExperience4 = this.fetchExperience) === null || _this$fetchExperience4 === void 0 ? void 0 : _this$fetchExperience4.success();
327
444
  }
328
445
  }
329
446
  }
@@ -335,7 +452,7 @@ export class ReferenceSyncBlockStoreManager {
335
452
  ...existingSyncBlock.data,
336
453
  sourceURL: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.url,
337
454
  sourceTitle: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.title,
338
- onSamePage: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.onSamePage,
455
+ onSameDocument: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.onSameDocument,
339
456
  sourceSubType: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.subType
340
457
  };
341
458
  this.updateCache(existingSyncBlock);
@@ -371,7 +488,7 @@ export class ReferenceSyncBlockStoreManager {
371
488
  this.providerFactories.delete(resourceId);
372
489
  }
373
490
  subscribeToSyncBlock(resourceId, localId, callback) {
374
- var _this$dataProvider2, _this$dataProvider2$g;
491
+ var _this$dataProvider3, _this$dataProvider3$g;
375
492
  // Cancel any pending cache deletion for this resourceId.
376
493
  // This handles the case where a block is moved - the old component unmounts
377
494
  // (scheduling deletion) but the new component mounts and subscribes before
@@ -384,6 +501,7 @@ export class ReferenceSyncBlockStoreManager {
384
501
 
385
502
  // add to subscriptions map
386
503
  const resourceSubscriptions = this.subscriptions.get(resourceId) || {};
504
+ const isNewResourceSubscription = Object.keys(resourceSubscriptions).length === 0;
387
505
  this.subscriptions.set(resourceId, {
388
506
  ...resourceSubscriptions,
389
507
  [localId]: callback
@@ -391,29 +509,32 @@ export class ReferenceSyncBlockStoreManager {
391
509
 
392
510
  // New subscription means new reference synced block is added to the document
393
511
  this.isCacheDirty = true;
512
+
513
+ // Notify listeners if this is a new resource subscription
514
+ if (isNewResourceSubscription) {
515
+ this.notifySubscriptionChangeListeners();
516
+ }
394
517
  const syncBlockNode = createSyncBlockNode(localId, resourceId);
395
518
 
396
519
  // call the callback immediately if we have cached data
397
520
  // prefer cache from store manager first, should update data provider to use the same cache
398
- const cachedData = this.getFromCache(resourceId) || ((_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);
521
+ const cachedData = this.getFromCache(resourceId) || ((_this$dataProvider3 = this.dataProvider) === null || _this$dataProvider3 === void 0 ? void 0 : (_this$dataProvider3$g = _this$dataProvider3.getNodeDataFromCache(syncBlockNode)) === null || _this$dataProvider3$g === void 0 ? void 0 : _this$dataProvider3$g.data);
399
522
  if (cachedData) {
400
523
  callback(cachedData);
401
524
  } else {
402
525
  this.fetchSyncBlocksData([syncBlockNode]).catch(error => {
526
+ var _this$fireAnalyticsEv10;
403
527
  logException(error, {
404
528
  location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
405
529
  });
406
- if (fg('platform_synced_block_dogfooding')) {
407
- var _this$fetchExperience5;
408
- (_this$fetchExperience5 = this.fetchExperience) === null || _this$fetchExperience5 === void 0 ? void 0 : _this$fetchExperience5.failure({
409
- reason: error.message
410
- });
411
- } else {
412
- var _this$fireAnalyticsEv7;
413
- (_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 ? void 0 : _this$fireAnalyticsEv7.call(this, fetchErrorPayload(error.message));
414
- }
530
+ (_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 ? void 0 : _this$fireAnalyticsEv10.call(this, fetchErrorPayload(error.message, resourceId));
415
531
  });
416
532
  }
533
+
534
+ // Set up GraphQL subscription if real-time subscriptions are enabled
535
+ if (this.useRealTimeSubscriptions) {
536
+ this.setupGraphQLSubscription(resourceId);
537
+ }
417
538
  return () => {
418
539
  const resourceSubscriptions = this.subscriptions.get(resourceId);
419
540
  if (resourceSubscriptions) {
@@ -422,6 +543,13 @@ export class ReferenceSyncBlockStoreManager {
422
543
  delete resourceSubscriptions[localId];
423
544
  if (Object.keys(resourceSubscriptions).length === 0) {
424
545
  this.subscriptions.delete(resourceId);
546
+
547
+ // Clean up GraphQL subscription when no more local subscribers
548
+ this.cleanupGraphQLSubscription(resourceId);
549
+
550
+ // Notify listeners that subscription was removed
551
+ this.notifySubscriptionChangeListeners();
552
+
425
553
  // Delay cache deletion to handle block moves (unmount/remount).
426
554
  // When a block is moved, the old component unmounts before the new one mounts.
427
555
  // By delaying deletion, we give the new component time to subscribe and
@@ -492,11 +620,11 @@ export class ReferenceSyncBlockStoreManager {
492
620
  }
493
621
  return this.subscribeToSyncBlock(resourceId, localId, callback);
494
622
  } catch (error) {
495
- var _this$fireAnalyticsEv8;
623
+ var _this$fireAnalyticsEv11;
496
624
  logException(error, {
497
625
  location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
498
626
  });
499
- (_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 ? void 0 : _this$fireAnalyticsEv8.call(this, fetchErrorPayload(error.message));
627
+ (_this$fireAnalyticsEv11 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv11 === void 0 ? void 0 : _this$fireAnalyticsEv11.call(this, fetchErrorPayload(error.message));
500
628
  return () => {};
501
629
  }
502
630
  }
@@ -516,12 +644,12 @@ export class ReferenceSyncBlockStoreManager {
516
644
  }
517
645
  getProviderFactory(resourceId) {
518
646
  if (!this.dataProvider) {
519
- var _this$fireAnalyticsEv9;
647
+ var _this$fireAnalyticsEv12;
520
648
  const error = new Error('Data provider not set');
521
649
  logException(error, {
522
650
  location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
523
651
  });
524
- (_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 ? void 0 : _this$fireAnalyticsEv9.call(this, fetchErrorPayload(error.message));
652
+ (_this$fireAnalyticsEv12 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv12 === void 0 ? void 0 : _this$fireAnalyticsEv12.call(this, fetchErrorPayload(error.message));
525
653
  return undefined;
526
654
  }
527
655
  const {
@@ -551,11 +679,11 @@ export class ReferenceSyncBlockStoreManager {
551
679
  try {
552
680
  this.retrieveDynamicProviders(resourceId, providerFactory, providerCreator);
553
681
  } catch (error) {
554
- var _this$fireAnalyticsEv0;
682
+ var _this$fireAnalyticsEv13;
555
683
  logException(error, {
556
684
  location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
557
685
  });
558
- (_this$fireAnalyticsEv0 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv0 === void 0 ? void 0 : _this$fireAnalyticsEv0.call(this, fetchErrorPayload(error.message));
686
+ (_this$fireAnalyticsEv13 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv13 === void 0 ? void 0 : _this$fireAnalyticsEv13.call(this, fetchErrorPayload(error.message, resourceId));
559
687
  }
560
688
  }
561
689
  return providerFactory;
@@ -610,9 +738,12 @@ export class ReferenceSyncBlockStoreManager {
610
738
  return;
611
739
  }
612
740
  const syncBlock = this.getFromCache(resourceId);
613
- if (!syncBlock || !((_syncBlock$data2 = syncBlock.data) !== null && _syncBlock$data2 !== void 0 && _syncBlock$data2.sourceAri) || !((_syncBlock$data3 = syncBlock.data) !== null && _syncBlock$data3 !== void 0 && _syncBlock$data3.product)) {
614
- var _this$fireAnalyticsEv1;
615
- (_this$fireAnalyticsEv1 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv1 === void 0 ? void 0 : _this$fireAnalyticsEv1.call(this, fetchErrorPayload('Sync block or source ari or product not found'));
741
+ if (!syncBlock) {
742
+ return;
743
+ }
744
+ if (!((_syncBlock$data2 = syncBlock.data) !== null && _syncBlock$data2 !== void 0 && _syncBlock$data2.sourceAri) || !((_syncBlock$data3 = syncBlock.data) !== null && _syncBlock$data3 !== void 0 && _syncBlock$data3.product)) {
745
+ var _this$fireAnalyticsEv14;
746
+ (_this$fireAnalyticsEv14 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv14 === void 0 ? void 0 : _this$fireAnalyticsEv14.call(this, fetchErrorPayload('Sync block source ari or product not found'));
616
747
  return;
617
748
  }
618
749
  const parentInfo = this.dataProvider.retrieveSyncBlockParentInfo((_syncBlock$data4 = syncBlock.data) === null || _syncBlock$data4 === void 0 ? void 0 : _syncBlock$data4.sourceAri, (_syncBlock$data5 = syncBlock.data) === null || _syncBlock$data5 === void 0 ? void 0 : _syncBlock$data5.product);
@@ -677,13 +808,13 @@ export class ReferenceSyncBlockStoreManager {
677
808
  });
678
809
  });
679
810
  });
680
- if (blocks.length === 0) {
681
- this.isCacheDirty = false;
682
- return true;
683
- }
684
- if (fg('platform_synced_block_dogfooding')) {
685
- var _this$saveExperience;
686
- (_this$saveExperience = this.saveExperience) === null || _this$saveExperience === void 0 ? void 0 : _this$saveExperience.start();
811
+ if (!fg('platform_synced_block_dogfooding')) {
812
+ // It's possible that the last reference block on the document was just deleted,
813
+ // we still want to write to BE to update reference count
814
+ if (blocks.length === 0) {
815
+ this.isCacheDirty = false;
816
+ return true;
817
+ }
687
818
  }
688
819
  if (!this.dataProvider) {
689
820
  throw new Error('Data provider not set');
@@ -695,20 +826,24 @@ export class ReferenceSyncBlockStoreManager {
695
826
  // is still making changes, the new changes might not be saved if they all happen
696
827
  // exactly at a time when the updateReferenceData is being executed asynchronously.
697
828
  this.isCacheDirty = false;
829
+ if (fg('platform_synced_block_dogfooding')) {
830
+ var _this$saveExperience;
831
+ (_this$saveExperience = this.saveExperience) === null || _this$saveExperience === void 0 ? void 0 : _this$saveExperience.start();
832
+ }
698
833
  const updateResult = await this.dataProvider.updateReferenceData(blocks);
699
834
  if (!updateResult.success) {
835
+ var _this$fireAnalyticsEv15;
700
836
  success = false;
701
837
  if (fg('platform_synced_block_dogfooding')) {
702
838
  var _this$saveExperience2;
703
839
  (_this$saveExperience2 = this.saveExperience) === null || _this$saveExperience2 === void 0 ? void 0 : _this$saveExperience2.failure({
704
840
  reason: updateResult.error || 'Failed to update reference synced blocks on the document'
705
841
  });
706
- } else {
707
- var _this$fireAnalyticsEv10;
708
- (_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 ? void 0 : _this$fireAnalyticsEv10.call(this, updateReferenceErrorPayload(updateResult.error || 'Failed to update reference synced blocks on the document'));
709
842
  }
843
+ (_this$fireAnalyticsEv15 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv15 === void 0 ? void 0 : _this$fireAnalyticsEv15.call(this, updateReferenceErrorPayload(updateResult.error || 'Failed to update reference synced blocks on the document'));
710
844
  }
711
845
  } catch (error) {
846
+ var _this$fireAnalyticsEv16;
712
847
  success = false;
713
848
  logException(error, {
714
849
  location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
@@ -718,10 +853,8 @@ export class ReferenceSyncBlockStoreManager {
718
853
  (_this$saveExperience3 = this.saveExperience) === null || _this$saveExperience3 === void 0 ? void 0 : _this$saveExperience3.failure({
719
854
  reason: error.message
720
855
  });
721
- } else {
722
- var _this$fireAnalyticsEv11;
723
- (_this$fireAnalyticsEv11 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv11 === void 0 ? void 0 : _this$fireAnalyticsEv11.call(this, updateReferenceErrorPayload(error.message));
724
856
  }
857
+ (_this$fireAnalyticsEv16 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv16 === void 0 ? void 0 : _this$fireAnalyticsEv16.call(this, updateReferenceErrorPayload(error.message));
725
858
  } finally {
726
859
  if (!success) {
727
860
  // set isCacheDirty back to true for cases where it failed to update the reference synced blocks on the BE
@@ -734,6 +867,9 @@ export class ReferenceSyncBlockStoreManager {
734
867
  return success;
735
868
  }
736
869
  destroy() {
870
+ var _this$saveExperience5, _this$fetchExperience5, _this$fetchSourceInfo6;
871
+ // Clean up all GraphQL subscriptions first
872
+ this.cleanupAllGraphQLSubscriptions();
737
873
  this.dataProvider = undefined;
738
874
  this.syncBlockCache.clear();
739
875
  this.subscriptions.clear();
@@ -743,10 +879,21 @@ export class ReferenceSyncBlockStoreManager {
743
879
  this.syncBlockSourceInfoRequests.clear();
744
880
  this.providerFactories.clear();
745
881
  this.isRefreshingSubscriptions = false;
882
+ this.useRealTimeSubscriptions = false;
883
+ this.subscriptionChangeListeners.clear();
746
884
  this.providerFactories.forEach(providerFactory => {
747
885
  providerFactory.destroy();
748
886
  });
749
887
  this.providerFactories.clear();
888
+ (_this$saveExperience5 = this.saveExperience) === null || _this$saveExperience5 === void 0 ? void 0 : _this$saveExperience5.abort({
889
+ reason: 'editor-destroyed'
890
+ });
891
+ (_this$fetchExperience5 = this.fetchExperience) === null || _this$fetchExperience5 === void 0 ? void 0 : _this$fetchExperience5.abort({
892
+ reason: 'editor-destroyed'
893
+ });
894
+ (_this$fetchSourceInfo6 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo6 === void 0 ? void 0 : _this$fetchSourceInfo6.abort({
895
+ reason: 'editor-destroyed'
896
+ });
750
897
  this.fireAnalyticsEvent = undefined;
751
898
  }
752
899
  }