@atlaskit/editor-synced-block-provider 3.27.1 → 3.28.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 +15 -0
- package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +155 -34
- package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +91 -11
- package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +155 -34
- package/dist/types/store-manager/referenceSyncBlockStoreManager.d.ts +4 -0
- package/dist/types-ts4.5/store-manager/referenceSyncBlockStoreManager.d.ts +4 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @atlaskit/editor-synced-block-provider
|
|
2
2
|
|
|
3
|
+
## 3.28.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`2d04d83eba130`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/2d04d83eba130) -
|
|
8
|
+
EDITOR-4997 update cache dirty logic to reduce request
|
|
9
|
+
|
|
10
|
+
## 3.27.2
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- [`7b605d0a82c41`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/7b605d0a82c41) -
|
|
15
|
+
Add some tests + remove pending fetch requests for resourceId if reference blocks deleted
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
|
|
3
18
|
## 3.27.1
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
|
@@ -11,6 +11,7 @@ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/
|
|
|
11
11
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
12
12
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
13
13
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
14
|
+
var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
|
|
14
15
|
var _rafSchd = _interopRequireDefault(require("raf-schd"));
|
|
15
16
|
var _coreUtils = require("@atlaskit/editor-common/core-utils");
|
|
16
17
|
var _monitoring = require("@atlaskit/editor-common/monitoring");
|
|
@@ -43,6 +44,12 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
43
44
|
(0, _defineProperty2.default)(this, "isRefreshingSubscriptions", false);
|
|
44
45
|
// Flag to indicate if real-time subscriptions are enabled
|
|
45
46
|
(0, _defineProperty2.default)(this, "useRealTimeSubscriptions", false);
|
|
47
|
+
// Keep track of the last flushed subscriptions to optimize cache flushing on document save
|
|
48
|
+
(0, _defineProperty2.default)(this, "lastFlushedSyncedBlocks", {});
|
|
49
|
+
// Track if a flush operation is currently in progress
|
|
50
|
+
(0, _defineProperty2.default)(this, "isFlushInProgress", false);
|
|
51
|
+
// Track if another flush is needed after the current one completes
|
|
52
|
+
(0, _defineProperty2.default)(this, "flushNeededAfterCurrent", false);
|
|
46
53
|
(0, _defineProperty2.default)(this, "pendingFetchRequests", new Set());
|
|
47
54
|
(0, _defineProperty2.default)(this, "scheduledBatchFetch", (0, _rafSchd.default)(function () {
|
|
48
55
|
if (_this.pendingFetchRequests.size === 0) {
|
|
@@ -833,8 +840,18 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
833
840
|
}, {
|
|
834
841
|
key: "debouncedBatchedFetchSyncBlocks",
|
|
835
842
|
value: function debouncedBatchedFetchSyncBlocks(resourceId) {
|
|
836
|
-
|
|
837
|
-
|
|
843
|
+
if ((0, _platformFeatureFlags.fg)('platform_synced_block_patch_2')) {
|
|
844
|
+
// Only add to pending requests if there are active subscriptions for this resource
|
|
845
|
+
if (this.subscriptions.has(resourceId) && Object.keys(this.subscriptions.get(resourceId) || {}).length > 0) {
|
|
846
|
+
this.pendingFetchRequests.add(resourceId);
|
|
847
|
+
this.scheduledBatchFetch();
|
|
848
|
+
} else {
|
|
849
|
+
this.pendingFetchRequests.delete(resourceId);
|
|
850
|
+
}
|
|
851
|
+
} else {
|
|
852
|
+
this.pendingFetchRequests.add(resourceId);
|
|
853
|
+
this.scheduledBatchFetch();
|
|
854
|
+
}
|
|
838
855
|
}
|
|
839
856
|
}, {
|
|
840
857
|
key: "subscribeToSyncBlock",
|
|
@@ -1150,19 +1167,104 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
1150
1167
|
key: "flush",
|
|
1151
1168
|
value: (function () {
|
|
1152
1169
|
var _flush = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4() {
|
|
1153
|
-
var
|
|
1154
|
-
|
|
1155
|
-
|
|
1170
|
+
var _this10 = this;
|
|
1171
|
+
var success, syncedBlocksToFlush, _this$saveExperience, blocks, _iterator4, _step4, _loop2, updateResult, _this$saveExperience2, _this$fireAnalyticsEv1, _this$saveExperience3, _this$fireAnalyticsEv10, _this$saveExperience4;
|
|
1172
|
+
return _regenerator.default.wrap(function _callee4$(_context6) {
|
|
1173
|
+
while (1) switch (_context6.prev = _context6.next) {
|
|
1156
1174
|
case 0:
|
|
1157
1175
|
if (this.isCacheDirty) {
|
|
1158
|
-
|
|
1176
|
+
_context6.next = 2;
|
|
1159
1177
|
break;
|
|
1160
1178
|
}
|
|
1161
|
-
return
|
|
1179
|
+
return _context6.abrupt("return", true);
|
|
1162
1180
|
case 2:
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1181
|
+
if (!(0, _platformFeatureFlags.fg)('platform_synced_block_patch_2')) {
|
|
1182
|
+
_context6.next = 9;
|
|
1183
|
+
break;
|
|
1184
|
+
}
|
|
1185
|
+
if (!this.isFlushInProgress) {
|
|
1186
|
+
_context6.next = 8;
|
|
1187
|
+
break;
|
|
1188
|
+
}
|
|
1189
|
+
// Mark that another flush is needed after the current one completes
|
|
1190
|
+
this.flushNeededAfterCurrent = true;
|
|
1191
|
+
|
|
1192
|
+
// We return true here because we know the pending flush will handle the dirty cache
|
|
1193
|
+
return _context6.abrupt("return", true);
|
|
1194
|
+
case 8:
|
|
1195
|
+
this.isFlushInProgress = true;
|
|
1196
|
+
case 9:
|
|
1197
|
+
success = true; // a copy of the subscriptions STRUCTURE (without the callbacks)
|
|
1198
|
+
// To be saved as the last flushed structure if the flush is successful
|
|
1199
|
+
syncedBlocksToFlush = {};
|
|
1200
|
+
_context6.prev = 11;
|
|
1201
|
+
if (this.dataProvider) {
|
|
1202
|
+
_context6.next = 14;
|
|
1203
|
+
break;
|
|
1204
|
+
}
|
|
1205
|
+
throw new Error('Data provider not set');
|
|
1206
|
+
case 14:
|
|
1207
|
+
blocks = [];
|
|
1208
|
+
if (!(0, _platformFeatureFlags.fg)('platform_synced_block_patch_2')) {
|
|
1209
|
+
_context6.next = 37;
|
|
1210
|
+
break;
|
|
1211
|
+
}
|
|
1212
|
+
// First, build the complete subscription structure
|
|
1213
|
+
_iterator4 = _createForOfIteratorHelper(this.subscriptions.entries());
|
|
1214
|
+
_context6.prev = 17;
|
|
1215
|
+
_loop2 = /*#__PURE__*/_regenerator.default.mark(function _loop2() {
|
|
1216
|
+
var _step4$value, resourceId, callbacks;
|
|
1217
|
+
return _regenerator.default.wrap(function _loop2$(_context5) {
|
|
1218
|
+
while (1) switch (_context5.prev = _context5.next) {
|
|
1219
|
+
case 0:
|
|
1220
|
+
_step4$value = (0, _slicedToArray2.default)(_step4.value, 2), resourceId = _step4$value[0], callbacks = _step4$value[1];
|
|
1221
|
+
syncedBlocksToFlush[resourceId] = {};
|
|
1222
|
+
Object.keys(callbacks).forEach(function (localId) {
|
|
1223
|
+
blocks.push({
|
|
1224
|
+
resourceId: resourceId,
|
|
1225
|
+
localId: localId
|
|
1226
|
+
});
|
|
1227
|
+
syncedBlocksToFlush[resourceId][localId] = true;
|
|
1228
|
+
});
|
|
1229
|
+
case 3:
|
|
1230
|
+
case "end":
|
|
1231
|
+
return _context5.stop();
|
|
1232
|
+
}
|
|
1233
|
+
}, _loop2);
|
|
1234
|
+
});
|
|
1235
|
+
_iterator4.s();
|
|
1236
|
+
case 20:
|
|
1237
|
+
if ((_step4 = _iterator4.n()).done) {
|
|
1238
|
+
_context6.next = 24;
|
|
1239
|
+
break;
|
|
1240
|
+
}
|
|
1241
|
+
return _context6.delegateYield(_loop2(), "t0", 22);
|
|
1242
|
+
case 22:
|
|
1243
|
+
_context6.next = 20;
|
|
1244
|
+
break;
|
|
1245
|
+
case 24:
|
|
1246
|
+
_context6.next = 29;
|
|
1247
|
+
break;
|
|
1248
|
+
case 26:
|
|
1249
|
+
_context6.prev = 26;
|
|
1250
|
+
_context6.t1 = _context6["catch"](17);
|
|
1251
|
+
_iterator4.e(_context6.t1);
|
|
1252
|
+
case 29:
|
|
1253
|
+
_context6.prev = 29;
|
|
1254
|
+
_iterator4.f();
|
|
1255
|
+
return _context6.finish(29);
|
|
1256
|
+
case 32:
|
|
1257
|
+
if (!(0, _isEqual.default)(syncedBlocksToFlush, this.lastFlushedSyncedBlocks)) {
|
|
1258
|
+
_context6.next = 35;
|
|
1259
|
+
break;
|
|
1260
|
+
}
|
|
1261
|
+
this.isCacheDirty = false; // Reset since we're considering this a successful no-op flush
|
|
1262
|
+
return _context6.abrupt("return", true);
|
|
1263
|
+
case 35:
|
|
1264
|
+
_context6.next = 38;
|
|
1265
|
+
break;
|
|
1266
|
+
case 37:
|
|
1267
|
+
// Collect all reference synced blocks on the current document
|
|
1166
1268
|
Array.from(this.subscriptions.entries()).forEach(function (_ref2) {
|
|
1167
1269
|
var _ref3 = (0, _slicedToArray2.default)(_ref2, 2),
|
|
1168
1270
|
resourceId = _ref3[0],
|
|
@@ -1174,12 +1276,7 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
1174
1276
|
});
|
|
1175
1277
|
});
|
|
1176
1278
|
});
|
|
1177
|
-
|
|
1178
|
-
_context5.next = 8;
|
|
1179
|
-
break;
|
|
1180
|
-
}
|
|
1181
|
-
throw new Error('Data provider not set');
|
|
1182
|
-
case 8:
|
|
1279
|
+
case 38:
|
|
1183
1280
|
// reset isCacheDirty early to prevent race condition
|
|
1184
1281
|
// There is a race condition where if a user makes changes (create/delete) to a reference sync block
|
|
1185
1282
|
// on a live page and the reference sync block is being saved while the user
|
|
@@ -1187,10 +1284,10 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
1187
1284
|
// exactly at a time when the updateReferenceData is being executed asynchronously.
|
|
1188
1285
|
this.isCacheDirty = false;
|
|
1189
1286
|
(_this$saveExperience = this.saveExperience) === null || _this$saveExperience === void 0 || _this$saveExperience.start();
|
|
1190
|
-
|
|
1287
|
+
_context6.next = 42;
|
|
1191
1288
|
return this.dataProvider.updateReferenceData(blocks);
|
|
1192
|
-
case
|
|
1193
|
-
updateResult =
|
|
1289
|
+
case 42:
|
|
1290
|
+
updateResult = _context6.sent;
|
|
1194
1291
|
if (!updateResult.success) {
|
|
1195
1292
|
success = false;
|
|
1196
1293
|
(_this$saveExperience2 = this.saveExperience) === null || _this$saveExperience2 === void 0 || _this$saveExperience2.failure({
|
|
@@ -1198,35 +1295,53 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
1198
1295
|
});
|
|
1199
1296
|
(_this$fireAnalyticsEv1 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv1 === void 0 || _this$fireAnalyticsEv1.call(this, (0, _errorHandling.updateReferenceErrorPayload)(updateResult.error || 'Failed to update reference synced blocks on the document'));
|
|
1200
1297
|
}
|
|
1201
|
-
|
|
1298
|
+
_context6.next = 52;
|
|
1202
1299
|
break;
|
|
1203
|
-
case
|
|
1204
|
-
|
|
1205
|
-
|
|
1300
|
+
case 46:
|
|
1301
|
+
_context6.prev = 46;
|
|
1302
|
+
_context6.t2 = _context6["catch"](11);
|
|
1206
1303
|
success = false;
|
|
1207
|
-
(0, _monitoring.logException)(
|
|
1304
|
+
(0, _monitoring.logException)(_context6.t2, {
|
|
1208
1305
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
1209
1306
|
});
|
|
1210
1307
|
(_this$saveExperience3 = this.saveExperience) === null || _this$saveExperience3 === void 0 || _this$saveExperience3.failure({
|
|
1211
|
-
reason:
|
|
1308
|
+
reason: _context6.t2.message
|
|
1212
1309
|
});
|
|
1213
|
-
(_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 || _this$fireAnalyticsEv10.call(this, (0, _errorHandling.updateReferenceErrorPayload)(
|
|
1214
|
-
case
|
|
1215
|
-
|
|
1310
|
+
(_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 || _this$fireAnalyticsEv10.call(this, (0, _errorHandling.updateReferenceErrorPayload)(_context6.t2.message));
|
|
1311
|
+
case 52:
|
|
1312
|
+
_context6.prev = 52;
|
|
1216
1313
|
if (!success) {
|
|
1217
1314
|
// set isCacheDirty back to true for cases where it failed to update the reference synced blocks on the BE
|
|
1218
1315
|
this.isCacheDirty = true;
|
|
1219
1316
|
} else {
|
|
1317
|
+
if ((0, _platformFeatureFlags.fg)('platform_synced_block_patch_2')) {
|
|
1318
|
+
this.lastFlushedSyncedBlocks = syncedBlocksToFlush;
|
|
1319
|
+
}
|
|
1220
1320
|
(_this$saveExperience4 = this.saveExperience) === null || _this$saveExperience4 === void 0 || _this$saveExperience4.success();
|
|
1221
1321
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1322
|
+
if ((0, _platformFeatureFlags.fg)('platform_synced_block_patch_2')) {
|
|
1323
|
+
// Always reset isFlushInProgress regardless of feature flag
|
|
1324
|
+
this.isFlushInProgress = false;
|
|
1325
|
+
|
|
1326
|
+
// If another flush was requested while this one was in progress, execute it now
|
|
1327
|
+
if (this.flushNeededAfterCurrent) {
|
|
1328
|
+
this.flushNeededAfterCurrent = false;
|
|
1329
|
+
// Use setTimeout to avoid deep recursion and run queued flush asynchronously
|
|
1330
|
+
// Note: flush() handles all exceptions internally and never rejects
|
|
1331
|
+
this.queuedFlushTimeout = setTimeout(function () {
|
|
1332
|
+
_this10.queuedFlushTimeout = undefined;
|
|
1333
|
+
void _this10.flush();
|
|
1334
|
+
}, 0);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
return _context6.finish(52);
|
|
1338
|
+
case 56:
|
|
1339
|
+
return _context6.abrupt("return", success);
|
|
1340
|
+
case 57:
|
|
1226
1341
|
case "end":
|
|
1227
|
-
return
|
|
1342
|
+
return _context6.stop();
|
|
1228
1343
|
}
|
|
1229
|
-
}, _callee4, this, [[
|
|
1344
|
+
}, _callee4, this, [[11, 46, 52, 56], [17, 26, 29, 32]]);
|
|
1230
1345
|
}));
|
|
1231
1346
|
function flush() {
|
|
1232
1347
|
return _flush.apply(this, arguments);
|
|
@@ -1237,6 +1352,12 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
1237
1352
|
key: "destroy",
|
|
1238
1353
|
value: function destroy() {
|
|
1239
1354
|
var _this$saveExperience5, _this$fetchExperience0, _this$fetchSourceInfo2;
|
|
1355
|
+
// Cancel any queued flush to prevent it from running after destroy
|
|
1356
|
+
if (this.queuedFlushTimeout) {
|
|
1357
|
+
clearTimeout(this.queuedFlushTimeout);
|
|
1358
|
+
this.queuedFlushTimeout = undefined;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1240
1361
|
// Clean up all GraphQL subscriptions first
|
|
1241
1362
|
this.cleanupAllGraphQLSubscriptions();
|
|
1242
1363
|
if ((0, _platformFeatureFlags.fg)('platform_synced_block_patch_1')) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
2
3
|
import rafSchedule from 'raf-schd';
|
|
3
4
|
import { isSSR } from '@atlaskit/editor-common/core-utils';
|
|
4
5
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
@@ -25,6 +26,12 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
25
26
|
_defineProperty(this, "isRefreshingSubscriptions", false);
|
|
26
27
|
// Flag to indicate if real-time subscriptions are enabled
|
|
27
28
|
_defineProperty(this, "useRealTimeSubscriptions", false);
|
|
29
|
+
// Keep track of the last flushed subscriptions to optimize cache flushing on document save
|
|
30
|
+
_defineProperty(this, "lastFlushedSyncedBlocks", {});
|
|
31
|
+
// Track if a flush operation is currently in progress
|
|
32
|
+
_defineProperty(this, "isFlushInProgress", false);
|
|
33
|
+
// Track if another flush is needed after the current one completes
|
|
34
|
+
_defineProperty(this, "flushNeededAfterCurrent", false);
|
|
28
35
|
_defineProperty(this, "pendingFetchRequests", new Set());
|
|
29
36
|
_defineProperty(this, "scheduledBatchFetch", rafSchedule(() => {
|
|
30
37
|
if (this.pendingFetchRequests.size === 0) {
|
|
@@ -639,8 +646,18 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
639
646
|
this.providerFactories.delete(resourceId);
|
|
640
647
|
}
|
|
641
648
|
debouncedBatchedFetchSyncBlocks(resourceId) {
|
|
642
|
-
|
|
643
|
-
|
|
649
|
+
if (fg('platform_synced_block_patch_2')) {
|
|
650
|
+
// Only add to pending requests if there are active subscriptions for this resource
|
|
651
|
+
if (this.subscriptions.has(resourceId) && Object.keys(this.subscriptions.get(resourceId) || {}).length > 0) {
|
|
652
|
+
this.pendingFetchRequests.add(resourceId);
|
|
653
|
+
this.scheduledBatchFetch();
|
|
654
|
+
} else {
|
|
655
|
+
this.pendingFetchRequests.delete(resourceId);
|
|
656
|
+
}
|
|
657
|
+
} else {
|
|
658
|
+
this.pendingFetchRequests.add(resourceId);
|
|
659
|
+
this.scheduledBatchFetch();
|
|
660
|
+
}
|
|
644
661
|
}
|
|
645
662
|
subscribeToSyncBlock(resourceId, localId, callback) {
|
|
646
663
|
var _this$dataProvider7, _this$dataProvider7$g, _this$dataProvider8, _this$dataProvider8$g, _this$dataProvider9, _this$dataProvider9$g;
|
|
@@ -950,24 +967,63 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
950
967
|
*/
|
|
951
968
|
async flush() {
|
|
952
969
|
if (!this.isCacheDirty) {
|
|
970
|
+
// we use the isCacheDirty flag as a quick check.
|
|
953
971
|
return true;
|
|
954
972
|
}
|
|
973
|
+
|
|
974
|
+
// Prevent concurrent flushes to avoid race conditions with lastFlushedSyncedBlocks
|
|
975
|
+
if (fg('platform_synced_block_patch_2')) {
|
|
976
|
+
if (this.isFlushInProgress) {
|
|
977
|
+
// Mark that another flush is needed after the current one completes
|
|
978
|
+
this.flushNeededAfterCurrent = true;
|
|
979
|
+
|
|
980
|
+
// We return true here because we know the pending flush will handle the dirty cache
|
|
981
|
+
return true;
|
|
982
|
+
} else {
|
|
983
|
+
this.isFlushInProgress = true;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
955
986
|
let success = true;
|
|
987
|
+
// a copy of the subscriptions STRUCTURE (without the callbacks)
|
|
988
|
+
// To be saved as the last flushed structure if the flush is successful
|
|
989
|
+
const syncedBlocksToFlush = {};
|
|
956
990
|
try {
|
|
957
991
|
var _this$saveExperience;
|
|
992
|
+
if (!this.dataProvider) {
|
|
993
|
+
throw new Error('Data provider not set');
|
|
994
|
+
}
|
|
958
995
|
const blocks = [];
|
|
996
|
+
if (fg('platform_synced_block_patch_2')) {
|
|
997
|
+
// First, build the complete subscription structure
|
|
998
|
+
for (const [resourceId, callbacks] of this.subscriptions.entries()) {
|
|
999
|
+
syncedBlocksToFlush[resourceId] = {};
|
|
1000
|
+
Object.keys(callbacks).forEach(localId => {
|
|
1001
|
+
blocks.push({
|
|
1002
|
+
resourceId,
|
|
1003
|
+
localId
|
|
1004
|
+
});
|
|
1005
|
+
syncedBlocksToFlush[resourceId][localId] = true;
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
959
1008
|
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1009
|
+
// Then, compare with the last flushed structure to detect changes
|
|
1010
|
+
// We check against the last flushed structure to prevent unnecessary flushes
|
|
1011
|
+
// Note that we will always flush at least once when editor starts
|
|
1012
|
+
// This is useful for eventual consistency between the editor and the BE.
|
|
1013
|
+
if (isEqual(syncedBlocksToFlush, this.lastFlushedSyncedBlocks)) {
|
|
1014
|
+
this.isCacheDirty = false; // Reset since we're considering this a successful no-op flush
|
|
1015
|
+
return true;
|
|
1016
|
+
}
|
|
1017
|
+
} else {
|
|
1018
|
+
// Collect all reference synced blocks on the current document
|
|
1019
|
+
Array.from(this.subscriptions.entries()).forEach(([resourceId, callbacks]) => {
|
|
1020
|
+
Object.keys(callbacks).forEach(localId => {
|
|
1021
|
+
blocks.push({
|
|
1022
|
+
resourceId,
|
|
1023
|
+
localId
|
|
1024
|
+
});
|
|
966
1025
|
});
|
|
967
1026
|
});
|
|
968
|
-
});
|
|
969
|
-
if (!this.dataProvider) {
|
|
970
|
-
throw new Error('Data provider not set');
|
|
971
1027
|
}
|
|
972
1028
|
|
|
973
1029
|
// reset isCacheDirty early to prevent race condition
|
|
@@ -1002,13 +1058,37 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
1002
1058
|
this.isCacheDirty = true;
|
|
1003
1059
|
} else {
|
|
1004
1060
|
var _this$saveExperience4;
|
|
1061
|
+
if (fg('platform_synced_block_patch_2')) {
|
|
1062
|
+
this.lastFlushedSyncedBlocks = syncedBlocksToFlush;
|
|
1063
|
+
}
|
|
1005
1064
|
(_this$saveExperience4 = this.saveExperience) === null || _this$saveExperience4 === void 0 ? void 0 : _this$saveExperience4.success();
|
|
1006
1065
|
}
|
|
1066
|
+
if (fg('platform_synced_block_patch_2')) {
|
|
1067
|
+
// Always reset isFlushInProgress regardless of feature flag
|
|
1068
|
+
this.isFlushInProgress = false;
|
|
1069
|
+
|
|
1070
|
+
// If another flush was requested while this one was in progress, execute it now
|
|
1071
|
+
if (this.flushNeededAfterCurrent) {
|
|
1072
|
+
this.flushNeededAfterCurrent = false;
|
|
1073
|
+
// Use setTimeout to avoid deep recursion and run queued flush asynchronously
|
|
1074
|
+
// Note: flush() handles all exceptions internally and never rejects
|
|
1075
|
+
this.queuedFlushTimeout = setTimeout(() => {
|
|
1076
|
+
this.queuedFlushTimeout = undefined;
|
|
1077
|
+
void this.flush();
|
|
1078
|
+
}, 0);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1007
1081
|
}
|
|
1008
1082
|
return success;
|
|
1009
1083
|
}
|
|
1010
1084
|
destroy() {
|
|
1011
1085
|
var _this$saveExperience5, _this$fetchExperience0, _this$fetchSourceInfo6;
|
|
1086
|
+
// Cancel any queued flush to prevent it from running after destroy
|
|
1087
|
+
if (this.queuedFlushTimeout) {
|
|
1088
|
+
clearTimeout(this.queuedFlushTimeout);
|
|
1089
|
+
this.queuedFlushTimeout = undefined;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1012
1092
|
// Clean up all GraphQL subscriptions first
|
|
1013
1093
|
this.cleanupAllGraphQLSubscriptions();
|
|
1014
1094
|
if (fg('platform_synced_block_patch_1')) {
|
|
@@ -9,6 +9,7 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
|
9
9
|
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; } } }; }
|
|
10
10
|
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; } }
|
|
11
11
|
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
12
|
+
import isEqual from 'lodash/isEqual';
|
|
12
13
|
import rafSchedule from 'raf-schd';
|
|
13
14
|
import { isSSR } from '@atlaskit/editor-common/core-utils';
|
|
14
15
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
@@ -37,6 +38,12 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
37
38
|
_defineProperty(this, "isRefreshingSubscriptions", false);
|
|
38
39
|
// Flag to indicate if real-time subscriptions are enabled
|
|
39
40
|
_defineProperty(this, "useRealTimeSubscriptions", false);
|
|
41
|
+
// Keep track of the last flushed subscriptions to optimize cache flushing on document save
|
|
42
|
+
_defineProperty(this, "lastFlushedSyncedBlocks", {});
|
|
43
|
+
// Track if a flush operation is currently in progress
|
|
44
|
+
_defineProperty(this, "isFlushInProgress", false);
|
|
45
|
+
// Track if another flush is needed after the current one completes
|
|
46
|
+
_defineProperty(this, "flushNeededAfterCurrent", false);
|
|
40
47
|
_defineProperty(this, "pendingFetchRequests", new Set());
|
|
41
48
|
_defineProperty(this, "scheduledBatchFetch", rafSchedule(function () {
|
|
42
49
|
if (_this.pendingFetchRequests.size === 0) {
|
|
@@ -827,8 +834,18 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
827
834
|
}, {
|
|
828
835
|
key: "debouncedBatchedFetchSyncBlocks",
|
|
829
836
|
value: function debouncedBatchedFetchSyncBlocks(resourceId) {
|
|
830
|
-
|
|
831
|
-
|
|
837
|
+
if (fg('platform_synced_block_patch_2')) {
|
|
838
|
+
// Only add to pending requests if there are active subscriptions for this resource
|
|
839
|
+
if (this.subscriptions.has(resourceId) && Object.keys(this.subscriptions.get(resourceId) || {}).length > 0) {
|
|
840
|
+
this.pendingFetchRequests.add(resourceId);
|
|
841
|
+
this.scheduledBatchFetch();
|
|
842
|
+
} else {
|
|
843
|
+
this.pendingFetchRequests.delete(resourceId);
|
|
844
|
+
}
|
|
845
|
+
} else {
|
|
846
|
+
this.pendingFetchRequests.add(resourceId);
|
|
847
|
+
this.scheduledBatchFetch();
|
|
848
|
+
}
|
|
832
849
|
}
|
|
833
850
|
}, {
|
|
834
851
|
key: "subscribeToSyncBlock",
|
|
@@ -1144,19 +1161,104 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
1144
1161
|
key: "flush",
|
|
1145
1162
|
value: (function () {
|
|
1146
1163
|
var _flush = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
|
|
1147
|
-
var
|
|
1148
|
-
|
|
1149
|
-
|
|
1164
|
+
var _this10 = this;
|
|
1165
|
+
var success, syncedBlocksToFlush, _this$saveExperience, blocks, _iterator4, _step4, _loop2, updateResult, _this$saveExperience2, _this$fireAnalyticsEv1, _this$saveExperience3, _this$fireAnalyticsEv10, _this$saveExperience4;
|
|
1166
|
+
return _regeneratorRuntime.wrap(function _callee4$(_context6) {
|
|
1167
|
+
while (1) switch (_context6.prev = _context6.next) {
|
|
1150
1168
|
case 0:
|
|
1151
1169
|
if (this.isCacheDirty) {
|
|
1152
|
-
|
|
1170
|
+
_context6.next = 2;
|
|
1153
1171
|
break;
|
|
1154
1172
|
}
|
|
1155
|
-
return
|
|
1173
|
+
return _context6.abrupt("return", true);
|
|
1156
1174
|
case 2:
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1175
|
+
if (!fg('platform_synced_block_patch_2')) {
|
|
1176
|
+
_context6.next = 9;
|
|
1177
|
+
break;
|
|
1178
|
+
}
|
|
1179
|
+
if (!this.isFlushInProgress) {
|
|
1180
|
+
_context6.next = 8;
|
|
1181
|
+
break;
|
|
1182
|
+
}
|
|
1183
|
+
// Mark that another flush is needed after the current one completes
|
|
1184
|
+
this.flushNeededAfterCurrent = true;
|
|
1185
|
+
|
|
1186
|
+
// We return true here because we know the pending flush will handle the dirty cache
|
|
1187
|
+
return _context6.abrupt("return", true);
|
|
1188
|
+
case 8:
|
|
1189
|
+
this.isFlushInProgress = true;
|
|
1190
|
+
case 9:
|
|
1191
|
+
success = true; // a copy of the subscriptions STRUCTURE (without the callbacks)
|
|
1192
|
+
// To be saved as the last flushed structure if the flush is successful
|
|
1193
|
+
syncedBlocksToFlush = {};
|
|
1194
|
+
_context6.prev = 11;
|
|
1195
|
+
if (this.dataProvider) {
|
|
1196
|
+
_context6.next = 14;
|
|
1197
|
+
break;
|
|
1198
|
+
}
|
|
1199
|
+
throw new Error('Data provider not set');
|
|
1200
|
+
case 14:
|
|
1201
|
+
blocks = [];
|
|
1202
|
+
if (!fg('platform_synced_block_patch_2')) {
|
|
1203
|
+
_context6.next = 37;
|
|
1204
|
+
break;
|
|
1205
|
+
}
|
|
1206
|
+
// First, build the complete subscription structure
|
|
1207
|
+
_iterator4 = _createForOfIteratorHelper(this.subscriptions.entries());
|
|
1208
|
+
_context6.prev = 17;
|
|
1209
|
+
_loop2 = /*#__PURE__*/_regeneratorRuntime.mark(function _loop2() {
|
|
1210
|
+
var _step4$value, resourceId, callbacks;
|
|
1211
|
+
return _regeneratorRuntime.wrap(function _loop2$(_context5) {
|
|
1212
|
+
while (1) switch (_context5.prev = _context5.next) {
|
|
1213
|
+
case 0:
|
|
1214
|
+
_step4$value = _slicedToArray(_step4.value, 2), resourceId = _step4$value[0], callbacks = _step4$value[1];
|
|
1215
|
+
syncedBlocksToFlush[resourceId] = {};
|
|
1216
|
+
Object.keys(callbacks).forEach(function (localId) {
|
|
1217
|
+
blocks.push({
|
|
1218
|
+
resourceId: resourceId,
|
|
1219
|
+
localId: localId
|
|
1220
|
+
});
|
|
1221
|
+
syncedBlocksToFlush[resourceId][localId] = true;
|
|
1222
|
+
});
|
|
1223
|
+
case 3:
|
|
1224
|
+
case "end":
|
|
1225
|
+
return _context5.stop();
|
|
1226
|
+
}
|
|
1227
|
+
}, _loop2);
|
|
1228
|
+
});
|
|
1229
|
+
_iterator4.s();
|
|
1230
|
+
case 20:
|
|
1231
|
+
if ((_step4 = _iterator4.n()).done) {
|
|
1232
|
+
_context6.next = 24;
|
|
1233
|
+
break;
|
|
1234
|
+
}
|
|
1235
|
+
return _context6.delegateYield(_loop2(), "t0", 22);
|
|
1236
|
+
case 22:
|
|
1237
|
+
_context6.next = 20;
|
|
1238
|
+
break;
|
|
1239
|
+
case 24:
|
|
1240
|
+
_context6.next = 29;
|
|
1241
|
+
break;
|
|
1242
|
+
case 26:
|
|
1243
|
+
_context6.prev = 26;
|
|
1244
|
+
_context6.t1 = _context6["catch"](17);
|
|
1245
|
+
_iterator4.e(_context6.t1);
|
|
1246
|
+
case 29:
|
|
1247
|
+
_context6.prev = 29;
|
|
1248
|
+
_iterator4.f();
|
|
1249
|
+
return _context6.finish(29);
|
|
1250
|
+
case 32:
|
|
1251
|
+
if (!isEqual(syncedBlocksToFlush, this.lastFlushedSyncedBlocks)) {
|
|
1252
|
+
_context6.next = 35;
|
|
1253
|
+
break;
|
|
1254
|
+
}
|
|
1255
|
+
this.isCacheDirty = false; // Reset since we're considering this a successful no-op flush
|
|
1256
|
+
return _context6.abrupt("return", true);
|
|
1257
|
+
case 35:
|
|
1258
|
+
_context6.next = 38;
|
|
1259
|
+
break;
|
|
1260
|
+
case 37:
|
|
1261
|
+
// Collect all reference synced blocks on the current document
|
|
1160
1262
|
Array.from(this.subscriptions.entries()).forEach(function (_ref2) {
|
|
1161
1263
|
var _ref3 = _slicedToArray(_ref2, 2),
|
|
1162
1264
|
resourceId = _ref3[0],
|
|
@@ -1168,12 +1270,7 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
1168
1270
|
});
|
|
1169
1271
|
});
|
|
1170
1272
|
});
|
|
1171
|
-
|
|
1172
|
-
_context5.next = 8;
|
|
1173
|
-
break;
|
|
1174
|
-
}
|
|
1175
|
-
throw new Error('Data provider not set');
|
|
1176
|
-
case 8:
|
|
1273
|
+
case 38:
|
|
1177
1274
|
// reset isCacheDirty early to prevent race condition
|
|
1178
1275
|
// There is a race condition where if a user makes changes (create/delete) to a reference sync block
|
|
1179
1276
|
// on a live page and the reference sync block is being saved while the user
|
|
@@ -1181,10 +1278,10 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
1181
1278
|
// exactly at a time when the updateReferenceData is being executed asynchronously.
|
|
1182
1279
|
this.isCacheDirty = false;
|
|
1183
1280
|
(_this$saveExperience = this.saveExperience) === null || _this$saveExperience === void 0 || _this$saveExperience.start();
|
|
1184
|
-
|
|
1281
|
+
_context6.next = 42;
|
|
1185
1282
|
return this.dataProvider.updateReferenceData(blocks);
|
|
1186
|
-
case
|
|
1187
|
-
updateResult =
|
|
1283
|
+
case 42:
|
|
1284
|
+
updateResult = _context6.sent;
|
|
1188
1285
|
if (!updateResult.success) {
|
|
1189
1286
|
success = false;
|
|
1190
1287
|
(_this$saveExperience2 = this.saveExperience) === null || _this$saveExperience2 === void 0 || _this$saveExperience2.failure({
|
|
@@ -1192,35 +1289,53 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
1192
1289
|
});
|
|
1193
1290
|
(_this$fireAnalyticsEv1 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv1 === void 0 || _this$fireAnalyticsEv1.call(this, updateReferenceErrorPayload(updateResult.error || 'Failed to update reference synced blocks on the document'));
|
|
1194
1291
|
}
|
|
1195
|
-
|
|
1292
|
+
_context6.next = 52;
|
|
1196
1293
|
break;
|
|
1197
|
-
case
|
|
1198
|
-
|
|
1199
|
-
|
|
1294
|
+
case 46:
|
|
1295
|
+
_context6.prev = 46;
|
|
1296
|
+
_context6.t2 = _context6["catch"](11);
|
|
1200
1297
|
success = false;
|
|
1201
|
-
logException(
|
|
1298
|
+
logException(_context6.t2, {
|
|
1202
1299
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
1203
1300
|
});
|
|
1204
1301
|
(_this$saveExperience3 = this.saveExperience) === null || _this$saveExperience3 === void 0 || _this$saveExperience3.failure({
|
|
1205
|
-
reason:
|
|
1302
|
+
reason: _context6.t2.message
|
|
1206
1303
|
});
|
|
1207
|
-
(_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 || _this$fireAnalyticsEv10.call(this, updateReferenceErrorPayload(
|
|
1208
|
-
case
|
|
1209
|
-
|
|
1304
|
+
(_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 || _this$fireAnalyticsEv10.call(this, updateReferenceErrorPayload(_context6.t2.message));
|
|
1305
|
+
case 52:
|
|
1306
|
+
_context6.prev = 52;
|
|
1210
1307
|
if (!success) {
|
|
1211
1308
|
// set isCacheDirty back to true for cases where it failed to update the reference synced blocks on the BE
|
|
1212
1309
|
this.isCacheDirty = true;
|
|
1213
1310
|
} else {
|
|
1311
|
+
if (fg('platform_synced_block_patch_2')) {
|
|
1312
|
+
this.lastFlushedSyncedBlocks = syncedBlocksToFlush;
|
|
1313
|
+
}
|
|
1214
1314
|
(_this$saveExperience4 = this.saveExperience) === null || _this$saveExperience4 === void 0 || _this$saveExperience4.success();
|
|
1215
1315
|
}
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1316
|
+
if (fg('platform_synced_block_patch_2')) {
|
|
1317
|
+
// Always reset isFlushInProgress regardless of feature flag
|
|
1318
|
+
this.isFlushInProgress = false;
|
|
1319
|
+
|
|
1320
|
+
// If another flush was requested while this one was in progress, execute it now
|
|
1321
|
+
if (this.flushNeededAfterCurrent) {
|
|
1322
|
+
this.flushNeededAfterCurrent = false;
|
|
1323
|
+
// Use setTimeout to avoid deep recursion and run queued flush asynchronously
|
|
1324
|
+
// Note: flush() handles all exceptions internally and never rejects
|
|
1325
|
+
this.queuedFlushTimeout = setTimeout(function () {
|
|
1326
|
+
_this10.queuedFlushTimeout = undefined;
|
|
1327
|
+
void _this10.flush();
|
|
1328
|
+
}, 0);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
return _context6.finish(52);
|
|
1332
|
+
case 56:
|
|
1333
|
+
return _context6.abrupt("return", success);
|
|
1334
|
+
case 57:
|
|
1220
1335
|
case "end":
|
|
1221
|
-
return
|
|
1336
|
+
return _context6.stop();
|
|
1222
1337
|
}
|
|
1223
|
-
}, _callee4, this, [[
|
|
1338
|
+
}, _callee4, this, [[11, 46, 52, 56], [17, 26, 29, 32]]);
|
|
1224
1339
|
}));
|
|
1225
1340
|
function flush() {
|
|
1226
1341
|
return _flush.apply(this, arguments);
|
|
@@ -1231,6 +1346,12 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
1231
1346
|
key: "destroy",
|
|
1232
1347
|
value: function destroy() {
|
|
1233
1348
|
var _this$saveExperience5, _this$fetchExperience0, _this$fetchSourceInfo2;
|
|
1349
|
+
// Cancel any queued flush to prevent it from running after destroy
|
|
1350
|
+
if (this.queuedFlushTimeout) {
|
|
1351
|
+
clearTimeout(this.queuedFlushTimeout);
|
|
1352
|
+
this.queuedFlushTimeout = undefined;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1234
1355
|
// Clean up all GraphQL subscriptions first
|
|
1235
1356
|
this.cleanupAllGraphQLSubscriptions();
|
|
1236
1357
|
if (fg('platform_synced_block_patch_1')) {
|
|
@@ -21,7 +21,11 @@ export declare class ReferenceSyncBlockStoreManager {
|
|
|
21
21
|
private useRealTimeSubscriptions;
|
|
22
22
|
private subscriptionChangeListeners;
|
|
23
23
|
private newlyAddedSyncBlocks;
|
|
24
|
+
private lastFlushedSyncedBlocks;
|
|
24
25
|
private onUnpublishedSyncBlockDetected?;
|
|
26
|
+
private isFlushInProgress;
|
|
27
|
+
private flushNeededAfterCurrent;
|
|
28
|
+
private queuedFlushTimeout?;
|
|
25
29
|
fetchExperience: Experience | undefined;
|
|
26
30
|
private fetchSourceInfoExperience;
|
|
27
31
|
private saveExperience;
|
|
@@ -21,7 +21,11 @@ export declare class ReferenceSyncBlockStoreManager {
|
|
|
21
21
|
private useRealTimeSubscriptions;
|
|
22
22
|
private subscriptionChangeListeners;
|
|
23
23
|
private newlyAddedSyncBlocks;
|
|
24
|
+
private lastFlushedSyncedBlocks;
|
|
24
25
|
private onUnpublishedSyncBlockDetected?;
|
|
26
|
+
private isFlushInProgress;
|
|
27
|
+
private flushNeededAfterCurrent;
|
|
28
|
+
private queuedFlushTimeout?;
|
|
25
29
|
fetchExperience: Experience | undefined;
|
|
26
30
|
private fetchSourceInfoExperience;
|
|
27
31
|
private saveExperience;
|
package/package.json
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"@babel/runtime": "^7.0.0",
|
|
33
33
|
"@compiled/react": "^0.18.6",
|
|
34
34
|
"graphql-ws": "^5.14.2",
|
|
35
|
+
"lodash": "^4.17.21",
|
|
35
36
|
"raf-schd": "^4.0.3",
|
|
36
37
|
"uuid": "^3.1.0"
|
|
37
38
|
},
|
|
@@ -79,7 +80,7 @@
|
|
|
79
80
|
}
|
|
80
81
|
},
|
|
81
82
|
"name": "@atlaskit/editor-synced-block-provider",
|
|
82
|
-
"version": "3.
|
|
83
|
+
"version": "3.28.0",
|
|
83
84
|
"description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
|
|
84
85
|
"author": "Atlassian Pty Ltd",
|
|
85
86
|
"license": "Apache-2.0",
|