@atlaskit/editor-synced-block-provider 3.12.1 → 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.
- package/CHANGELOG.md +16 -0
- package/dist/cjs/clients/block-service/blockSubscription.js +124 -0
- package/dist/cjs/clients/jira/sourceInfo.js +152 -0
- package/dist/cjs/providers/block-service/blockServiceAPI.js +43 -6
- package/dist/cjs/providers/syncBlockProvider.js +40 -8
- package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +347 -114
- package/dist/cjs/store-manager/syncBlockStoreManager.js +2 -2
- package/dist/cjs/utils/resolveSyncBlockInstance.js +1 -1
- package/dist/es2019/clients/block-service/blockSubscription.js +125 -0
- package/dist/es2019/clients/jira/sourceInfo.js +87 -0
- package/dist/es2019/providers/block-service/blockServiceAPI.js +40 -5
- package/dist/es2019/providers/syncBlockProvider.js +26 -2
- package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +233 -45
- package/dist/es2019/store-manager/syncBlockStoreManager.js +2 -2
- package/dist/es2019/utils/resolveSyncBlockInstance.js +1 -1
- package/dist/esm/clients/block-service/blockSubscription.js +118 -0
- package/dist/esm/clients/jira/sourceInfo.js +147 -0
- package/dist/esm/providers/block-service/blockServiceAPI.js +43 -6
- package/dist/esm/providers/syncBlockProvider.js +38 -6
- package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +347 -114
- package/dist/esm/store-manager/syncBlockStoreManager.js +2 -2
- package/dist/esm/utils/resolveSyncBlockInstance.js +1 -1
- package/dist/types/clients/block-service/blockService.d.ts +2 -2
- package/dist/types/clients/block-service/blockSubscription.d.ts +38 -0
- package/dist/types/clients/jira/sourceInfo.d.ts +2 -0
- package/dist/types/common/types.d.ts +4 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/providers/block-service/blockServiceAPI.d.ts +8 -0
- package/dist/types/providers/syncBlockProvider.d.ts +9 -1
- package/dist/types/providers/types.d.ts +22 -6
- package/dist/types/store-manager/referenceSyncBlockStoreManager.d.ts +59 -0
- package/dist/types-ts4.5/clients/block-service/blockService.d.ts +2 -2
- package/dist/types-ts4.5/clients/block-service/blockSubscription.d.ts +38 -0
- package/dist/types-ts4.5/clients/jira/sourceInfo.d.ts +2 -0
- package/dist/types-ts4.5/common/types.d.ts +4 -2
- package/dist/types-ts4.5/index.d.ts +2 -2
- package/dist/types-ts4.5/providers/block-service/blockServiceAPI.d.ts +8 -0
- package/dist/types-ts4.5/providers/syncBlockProvider.d.ts +9 -1
- package/dist/types-ts4.5/providers/types.d.ts +22 -6
- package/dist/types-ts4.5/store-manager/referenceSyncBlockStoreManager.d.ts +59 -0
- package/package.json +2 -1
|
@@ -15,17 +15,16 @@ import { createSyncBlockNode } from '../utils/utils';
|
|
|
15
15
|
// Handles fetching source URL and title for sync blocks.
|
|
16
16
|
// Can be used in both editor and renderer contexts.
|
|
17
17
|
export class ReferenceSyncBlockStoreManager {
|
|
18
|
-
//
|
|
19
|
-
// When a block is moved, the old component unmounts before the new one mounts,
|
|
20
|
-
// causing the cache to be deleted prematurely. We delay deletion to allow
|
|
21
|
-
// the new component to subscribe and cancel the pending deletion.
|
|
18
|
+
// Listeners for subscription changes (used by React components to know when to update)
|
|
22
19
|
|
|
23
20
|
constructor(dataProvider) {
|
|
24
21
|
// Keeps track of addition and deletion of reference synced blocks on the document
|
|
25
22
|
// This starts as true to always flush the cache when document is saved for the first time
|
|
26
|
-
// to cater the case when a editor
|
|
23
|
+
// to cater the case when a editor session is closed without document being updated right after reference block is deleted
|
|
27
24
|
_defineProperty(this, "isCacheDirty", true);
|
|
28
25
|
_defineProperty(this, "isRefreshingSubscriptions", false);
|
|
26
|
+
// Flag to indicate if real-time subscriptions are enabled
|
|
27
|
+
_defineProperty(this, "useRealTimeSubscriptions", false);
|
|
29
28
|
this.syncBlockCache = new Map();
|
|
30
29
|
this.subscriptions = new Map();
|
|
31
30
|
this.titleSubscriptions = new Map();
|
|
@@ -35,6 +34,89 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
35
34
|
this.syncBlockSourceInfoRequests = new Map();
|
|
36
35
|
this.providerFactories = new Map();
|
|
37
36
|
this.pendingCacheDeletions = new Map();
|
|
37
|
+
this.graphqlSubscriptions = new Map();
|
|
38
|
+
this.subscriptionChangeListeners = new Set();
|
|
39
|
+
}
|
|
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
|
+
});
|
|
103
|
+
}
|
|
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);
|
|
119
|
+
}
|
|
38
120
|
}
|
|
39
121
|
setFireAnalyticsEvent(fireAnalyticsEvent) {
|
|
40
122
|
this.fireAnalyticsEvent = fireAnalyticsEvent;
|
|
@@ -59,9 +141,14 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
59
141
|
|
|
60
142
|
/**
|
|
61
143
|
* Refreshes the subscriptions for all sync blocks.
|
|
144
|
+
* This is a fallback polling mechanism when real-time subscriptions are not enabled.
|
|
62
145
|
* @returns {Promise<void>}
|
|
63
146
|
*/
|
|
64
147
|
async refreshSubscriptions() {
|
|
148
|
+
// Skip polling refresh if real-time subscriptions are enabled
|
|
149
|
+
if (this.useRealTimeSubscriptions) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
65
152
|
if (this.isRefreshingSubscriptions) {
|
|
66
153
|
return;
|
|
67
154
|
}
|
|
@@ -77,15 +164,90 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
77
164
|
// this function will update the cache and call the subscriptions
|
|
78
165
|
await this.fetchSyncBlocksData(syncBlocks);
|
|
79
166
|
} catch (error) {
|
|
80
|
-
var _this$
|
|
167
|
+
var _this$fireAnalyticsEv2;
|
|
81
168
|
logException(error, {
|
|
82
169
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
83
170
|
});
|
|
84
|
-
(_this$
|
|
171
|
+
(_this$fireAnalyticsEv2 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv2 === void 0 ? void 0 : _this$fireAnalyticsEv2.call(this, fetchErrorPayload(error.message));
|
|
85
172
|
} finally {
|
|
86
173
|
this.isRefreshingSubscriptions = false;
|
|
87
174
|
}
|
|
88
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
|
+
}
|
|
89
251
|
fetchSyncBlockSourceInfo(resourceId) {
|
|
90
252
|
try {
|
|
91
253
|
if (!resourceId || !this.dataProvider) {
|
|
@@ -109,7 +271,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
109
271
|
blockInstanceId,
|
|
110
272
|
sourceURL,
|
|
111
273
|
sourceTitle,
|
|
112
|
-
|
|
274
|
+
onSameDocument,
|
|
113
275
|
sourceSubType
|
|
114
276
|
} = existingSyncBlock.data || {};
|
|
115
277
|
// skip if source URL and title are already present
|
|
@@ -120,7 +282,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
120
282
|
url: sourceURL,
|
|
121
283
|
subType: sourceSubType,
|
|
122
284
|
sourceAri: sourceAri || '',
|
|
123
|
-
|
|
285
|
+
onSameDocument,
|
|
124
286
|
productType: product
|
|
125
287
|
});
|
|
126
288
|
} else {
|
|
@@ -128,8 +290,8 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
128
290
|
}
|
|
129
291
|
}
|
|
130
292
|
if (!sourceAri || !product || !blockInstanceId) {
|
|
131
|
-
var _this$
|
|
132
|
-
(_this$
|
|
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));
|
|
133
295
|
return Promise.resolve(undefined);
|
|
134
296
|
}
|
|
135
297
|
if (fg('platform_synced_block_dogfooding')) {
|
|
@@ -139,11 +301,11 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
139
301
|
const sourceInfoPromise = this.dataProvider.fetchSyncBlockSourceInfo(blockInstanceId, sourceAri, product, this.fireAnalyticsEvent).then(sourceInfo => {
|
|
140
302
|
if (!sourceInfo) {
|
|
141
303
|
if (fg('platform_synced_block_dogfooding')) {
|
|
142
|
-
var _this$fetchSourceInfo2, _this$
|
|
304
|
+
var _this$fetchSourceInfo2, _this$fireAnalyticsEv5;
|
|
143
305
|
(_this$fetchSourceInfo2 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo2 === void 0 ? void 0 : _this$fetchSourceInfo2.failure({
|
|
144
306
|
reason: 'No source info returned'
|
|
145
307
|
});
|
|
146
|
-
(_this$
|
|
308
|
+
(_this$fireAnalyticsEv5 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv5 === void 0 ? void 0 : _this$fireAnalyticsEv5.call(this, getSourceInfoErrorPayload('No source info returned', resourceId));
|
|
147
309
|
}
|
|
148
310
|
return undefined;
|
|
149
311
|
}
|
|
@@ -156,23 +318,23 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
156
318
|
var _this$fetchSourceInfo3;
|
|
157
319
|
(_this$fetchSourceInfo3 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo3 === void 0 ? void 0 : _this$fetchSourceInfo3.success();
|
|
158
320
|
} else {
|
|
159
|
-
var _this$fetchSourceInfo4, _this$
|
|
321
|
+
var _this$fetchSourceInfo4, _this$fireAnalyticsEv6;
|
|
160
322
|
(_this$fetchSourceInfo4 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo4 === void 0 ? void 0 : _this$fetchSourceInfo4.failure({
|
|
161
323
|
reason: 'Missing title or url'
|
|
162
324
|
});
|
|
163
|
-
(_this$
|
|
325
|
+
(_this$fireAnalyticsEv6 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv6 === void 0 ? void 0 : _this$fireAnalyticsEv6.call(this, getSourceInfoErrorPayload('Missing title or url', resourceId));
|
|
164
326
|
}
|
|
165
327
|
return sourceInfo;
|
|
166
328
|
}
|
|
167
329
|
}).catch(error => {
|
|
168
|
-
var _this$
|
|
330
|
+
var _this$fireAnalyticsEv7;
|
|
169
331
|
if (fg('platform_synced_block_dogfooding')) {
|
|
170
332
|
var _this$fetchSourceInfo5;
|
|
171
333
|
(_this$fetchSourceInfo5 = this.fetchSourceInfoExperience) === null || _this$fetchSourceInfo5 === void 0 ? void 0 : _this$fetchSourceInfo5.failure({
|
|
172
334
|
reason: error.message
|
|
173
335
|
});
|
|
174
336
|
}
|
|
175
|
-
(_this$
|
|
337
|
+
(_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 ? void 0 : _this$fireAnalyticsEv7.call(this, getSourceInfoErrorPayload(error.message, resourceId));
|
|
176
338
|
return undefined;
|
|
177
339
|
}).finally(() => {
|
|
178
340
|
if (fg('platform_synced_block_dogfooding')) {
|
|
@@ -188,11 +350,11 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
188
350
|
this.syncBlockSourceInfoRequestsOld.set(resourceId, true);
|
|
189
351
|
}
|
|
190
352
|
} catch (error) {
|
|
191
|
-
var _this$
|
|
353
|
+
var _this$fireAnalyticsEv8;
|
|
192
354
|
logException(error, {
|
|
193
355
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
194
356
|
});
|
|
195
|
-
(_this$
|
|
357
|
+
(_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 ? void 0 : _this$fireAnalyticsEv8.call(this, getSourceInfoErrorPayload(error.message, resourceId));
|
|
196
358
|
}
|
|
197
359
|
return Promise.resolve(undefined);
|
|
198
360
|
}
|
|
@@ -242,8 +404,8 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
242
404
|
let hasExpectedError = false;
|
|
243
405
|
data.forEach(syncBlockInstance => {
|
|
244
406
|
if (!syncBlockInstance.resourceId) {
|
|
245
|
-
var _this$
|
|
246
|
-
(_this$
|
|
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'));
|
|
247
409
|
return;
|
|
248
410
|
}
|
|
249
411
|
const existingSyncBlock = this.getFromCache(syncBlockInstance.resourceId);
|
|
@@ -251,8 +413,8 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
251
413
|
this.updateCache(resolvedSyncBlockInstance);
|
|
252
414
|
resolvedData.push(resolvedSyncBlockInstance);
|
|
253
415
|
if (syncBlockInstance.error) {
|
|
254
|
-
var _this$
|
|
255
|
-
(_this$
|
|
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));
|
|
256
418
|
if (syncBlockInstance.error === SyncBlockError.NotFound || syncBlockInstance.error === SyncBlockError.Forbidden) {
|
|
257
419
|
hasExpectedError = true;
|
|
258
420
|
} else if (syncBlockInstance.error) {
|
|
@@ -260,8 +422,8 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
260
422
|
}
|
|
261
423
|
return;
|
|
262
424
|
} else if (fg('platform_synced_block_dogfooding')) {
|
|
263
|
-
var _this$
|
|
264
|
-
(_this$
|
|
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));
|
|
265
427
|
}
|
|
266
428
|
this.fetchSyncBlockSourceInfo(resolvedSyncBlockInstance.resourceId);
|
|
267
429
|
});
|
|
@@ -290,7 +452,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
290
452
|
...existingSyncBlock.data,
|
|
291
453
|
sourceURL: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.url,
|
|
292
454
|
sourceTitle: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.title,
|
|
293
|
-
|
|
455
|
+
onSameDocument: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.onSameDocument,
|
|
294
456
|
sourceSubType: sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.subType
|
|
295
457
|
};
|
|
296
458
|
this.updateCache(existingSyncBlock);
|
|
@@ -326,7 +488,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
326
488
|
this.providerFactories.delete(resourceId);
|
|
327
489
|
}
|
|
328
490
|
subscribeToSyncBlock(resourceId, localId, callback) {
|
|
329
|
-
var _this$
|
|
491
|
+
var _this$dataProvider3, _this$dataProvider3$g;
|
|
330
492
|
// Cancel any pending cache deletion for this resourceId.
|
|
331
493
|
// This handles the case where a block is moved - the old component unmounts
|
|
332
494
|
// (scheduling deletion) but the new component mounts and subscribes before
|
|
@@ -339,6 +501,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
339
501
|
|
|
340
502
|
// add to subscriptions map
|
|
341
503
|
const resourceSubscriptions = this.subscriptions.get(resourceId) || {};
|
|
504
|
+
const isNewResourceSubscription = Object.keys(resourceSubscriptions).length === 0;
|
|
342
505
|
this.subscriptions.set(resourceId, {
|
|
343
506
|
...resourceSubscriptions,
|
|
344
507
|
[localId]: callback
|
|
@@ -346,22 +509,32 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
346
509
|
|
|
347
510
|
// New subscription means new reference synced block is added to the document
|
|
348
511
|
this.isCacheDirty = true;
|
|
512
|
+
|
|
513
|
+
// Notify listeners if this is a new resource subscription
|
|
514
|
+
if (isNewResourceSubscription) {
|
|
515
|
+
this.notifySubscriptionChangeListeners();
|
|
516
|
+
}
|
|
349
517
|
const syncBlockNode = createSyncBlockNode(localId, resourceId);
|
|
350
518
|
|
|
351
519
|
// call the callback immediately if we have cached data
|
|
352
520
|
// prefer cache from store manager first, should update data provider to use the same cache
|
|
353
|
-
const cachedData = this.getFromCache(resourceId) || ((_this$
|
|
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);
|
|
354
522
|
if (cachedData) {
|
|
355
523
|
callback(cachedData);
|
|
356
524
|
} else {
|
|
357
525
|
this.fetchSyncBlocksData([syncBlockNode]).catch(error => {
|
|
358
|
-
var _this$
|
|
526
|
+
var _this$fireAnalyticsEv10;
|
|
359
527
|
logException(error, {
|
|
360
528
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
361
529
|
});
|
|
362
|
-
(_this$
|
|
530
|
+
(_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 ? void 0 : _this$fireAnalyticsEv10.call(this, fetchErrorPayload(error.message, resourceId));
|
|
363
531
|
});
|
|
364
532
|
}
|
|
533
|
+
|
|
534
|
+
// Set up GraphQL subscription if real-time subscriptions are enabled
|
|
535
|
+
if (this.useRealTimeSubscriptions) {
|
|
536
|
+
this.setupGraphQLSubscription(resourceId);
|
|
537
|
+
}
|
|
365
538
|
return () => {
|
|
366
539
|
const resourceSubscriptions = this.subscriptions.get(resourceId);
|
|
367
540
|
if (resourceSubscriptions) {
|
|
@@ -370,6 +543,13 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
370
543
|
delete resourceSubscriptions[localId];
|
|
371
544
|
if (Object.keys(resourceSubscriptions).length === 0) {
|
|
372
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
|
+
|
|
373
553
|
// Delay cache deletion to handle block moves (unmount/remount).
|
|
374
554
|
// When a block is moved, the old component unmounts before the new one mounts.
|
|
375
555
|
// By delaying deletion, we give the new component time to subscribe and
|
|
@@ -440,11 +620,11 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
440
620
|
}
|
|
441
621
|
return this.subscribeToSyncBlock(resourceId, localId, callback);
|
|
442
622
|
} catch (error) {
|
|
443
|
-
var _this$
|
|
623
|
+
var _this$fireAnalyticsEv11;
|
|
444
624
|
logException(error, {
|
|
445
625
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
446
626
|
});
|
|
447
|
-
(_this$
|
|
627
|
+
(_this$fireAnalyticsEv11 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv11 === void 0 ? void 0 : _this$fireAnalyticsEv11.call(this, fetchErrorPayload(error.message));
|
|
448
628
|
return () => {};
|
|
449
629
|
}
|
|
450
630
|
}
|
|
@@ -464,12 +644,12 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
464
644
|
}
|
|
465
645
|
getProviderFactory(resourceId) {
|
|
466
646
|
if (!this.dataProvider) {
|
|
467
|
-
var _this$
|
|
647
|
+
var _this$fireAnalyticsEv12;
|
|
468
648
|
const error = new Error('Data provider not set');
|
|
469
649
|
logException(error, {
|
|
470
650
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
471
651
|
});
|
|
472
|
-
(_this$
|
|
652
|
+
(_this$fireAnalyticsEv12 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv12 === void 0 ? void 0 : _this$fireAnalyticsEv12.call(this, fetchErrorPayload(error.message));
|
|
473
653
|
return undefined;
|
|
474
654
|
}
|
|
475
655
|
const {
|
|
@@ -499,11 +679,11 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
499
679
|
try {
|
|
500
680
|
this.retrieveDynamicProviders(resourceId, providerFactory, providerCreator);
|
|
501
681
|
} catch (error) {
|
|
502
|
-
var _this$
|
|
682
|
+
var _this$fireAnalyticsEv13;
|
|
503
683
|
logException(error, {
|
|
504
684
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
505
685
|
});
|
|
506
|
-
(_this$
|
|
686
|
+
(_this$fireAnalyticsEv13 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv13 === void 0 ? void 0 : _this$fireAnalyticsEv13.call(this, fetchErrorPayload(error.message, resourceId));
|
|
507
687
|
}
|
|
508
688
|
}
|
|
509
689
|
return providerFactory;
|
|
@@ -562,8 +742,8 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
562
742
|
return;
|
|
563
743
|
}
|
|
564
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)) {
|
|
565
|
-
var _this$
|
|
566
|
-
(_this$
|
|
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'));
|
|
567
747
|
return;
|
|
568
748
|
}
|
|
569
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);
|
|
@@ -628,9 +808,13 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
628
808
|
});
|
|
629
809
|
});
|
|
630
810
|
});
|
|
631
|
-
if (
|
|
632
|
-
|
|
633
|
-
|
|
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
|
+
}
|
|
634
818
|
}
|
|
635
819
|
if (!this.dataProvider) {
|
|
636
820
|
throw new Error('Data provider not set');
|
|
@@ -648,7 +832,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
648
832
|
}
|
|
649
833
|
const updateResult = await this.dataProvider.updateReferenceData(blocks);
|
|
650
834
|
if (!updateResult.success) {
|
|
651
|
-
var _this$
|
|
835
|
+
var _this$fireAnalyticsEv15;
|
|
652
836
|
success = false;
|
|
653
837
|
if (fg('platform_synced_block_dogfooding')) {
|
|
654
838
|
var _this$saveExperience2;
|
|
@@ -656,10 +840,10 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
656
840
|
reason: updateResult.error || 'Failed to update reference synced blocks on the document'
|
|
657
841
|
});
|
|
658
842
|
}
|
|
659
|
-
(_this$
|
|
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'));
|
|
660
844
|
}
|
|
661
845
|
} catch (error) {
|
|
662
|
-
var _this$
|
|
846
|
+
var _this$fireAnalyticsEv16;
|
|
663
847
|
success = false;
|
|
664
848
|
logException(error, {
|
|
665
849
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
@@ -670,7 +854,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
670
854
|
reason: error.message
|
|
671
855
|
});
|
|
672
856
|
}
|
|
673
|
-
(_this$
|
|
857
|
+
(_this$fireAnalyticsEv16 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv16 === void 0 ? void 0 : _this$fireAnalyticsEv16.call(this, updateReferenceErrorPayload(error.message));
|
|
674
858
|
} finally {
|
|
675
859
|
if (!success) {
|
|
676
860
|
// set isCacheDirty back to true for cases where it failed to update the reference synced blocks on the BE
|
|
@@ -684,6 +868,8 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
684
868
|
}
|
|
685
869
|
destroy() {
|
|
686
870
|
var _this$saveExperience5, _this$fetchExperience5, _this$fetchSourceInfo6;
|
|
871
|
+
// Clean up all GraphQL subscriptions first
|
|
872
|
+
this.cleanupAllGraphQLSubscriptions();
|
|
687
873
|
this.dataProvider = undefined;
|
|
688
874
|
this.syncBlockCache.clear();
|
|
689
875
|
this.subscriptions.clear();
|
|
@@ -693,6 +879,8 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
693
879
|
this.syncBlockSourceInfoRequests.clear();
|
|
694
880
|
this.providerFactories.clear();
|
|
695
881
|
this.isRefreshingSubscriptions = false;
|
|
882
|
+
this.useRealTimeSubscriptions = false;
|
|
883
|
+
this.subscriptionChangeListeners.clear();
|
|
696
884
|
this.providerFactories.forEach(providerFactory => {
|
|
697
885
|
providerFactory.destroy();
|
|
698
886
|
});
|
|
@@ -47,7 +47,7 @@ export class SyncBlockStoreManager {
|
|
|
47
47
|
}
|
|
48
48
|
return {
|
|
49
49
|
...sourceInfo,
|
|
50
|
-
|
|
50
|
+
onSameDocument: reference.onSameDocument,
|
|
51
51
|
hasAccess: reference.hasAccess,
|
|
52
52
|
productType: sourceInfo.productType
|
|
53
53
|
};
|
|
@@ -57,7 +57,7 @@ export class SyncBlockStoreManager {
|
|
|
57
57
|
if (sourceSyncBlockData) {
|
|
58
58
|
sourceInfos.push({
|
|
59
59
|
...sourceSyncBlockData,
|
|
60
|
-
|
|
60
|
+
onSameDocument: Boolean(sourceSyncBlockData === null || sourceSyncBlockData === void 0 ? void 0 : sourceSyncBlockData.onSameDocument),
|
|
61
61
|
hasAccess: true,
|
|
62
62
|
isSource: true,
|
|
63
63
|
productType: sourceSyncBlockData === null || sourceSyncBlockData === void 0 ? void 0 : sourceSyncBlockData.productType
|
|
@@ -33,7 +33,7 @@ export const resolveSyncBlockInstance = (oldResult, newResult) => {
|
|
|
33
33
|
sourceTitle: ((_newResult$data2 = newResult.data) === null || _newResult$data2 === void 0 ? void 0 : _newResult$data2.sourceTitle) || ((_oldResult$data2 = oldResult.data) === null || _oldResult$data2 === void 0 ? void 0 : _oldResult$data2.sourceTitle) || undefined,
|
|
34
34
|
...(fg('platform_synced_block_dogfooding') && {
|
|
35
35
|
sourceSubType: ((_newResult$data3 = newResult.data) === null || _newResult$data3 === void 0 ? void 0 : _newResult$data3.sourceSubType) || ((_oldResult$data3 = oldResult.data) === null || _oldResult$data3 === void 0 ? void 0 : _oldResult$data3.sourceSubType) || undefined,
|
|
36
|
-
|
|
36
|
+
onSameDocument: ((_newResult$data4 = newResult.data) === null || _newResult$data4 === void 0 ? void 0 : _newResult$data4.onSameDocument) || ((_oldResult$data4 = oldResult.data) === null || _oldResult$data4 === void 0 ? void 0 : _oldResult$data4.onSameDocument) || undefined
|
|
37
37
|
})
|
|
38
38
|
}
|
|
39
39
|
};
|