@atlaskit/editor-synced-block-provider 3.27.2 → 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 +7 -0
- package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +143 -32
- package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +79 -9
- package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +143 -32
- 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,12 @@
|
|
|
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
|
+
|
|
3
10
|
## 3.27.2
|
|
4
11
|
|
|
5
12
|
### 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) {
|
|
@@ -1160,19 +1167,104 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
1160
1167
|
key: "flush",
|
|
1161
1168
|
value: (function () {
|
|
1162
1169
|
var _flush = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4() {
|
|
1163
|
-
var
|
|
1164
|
-
|
|
1165
|
-
|
|
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) {
|
|
1166
1174
|
case 0:
|
|
1167
1175
|
if (this.isCacheDirty) {
|
|
1168
|
-
|
|
1176
|
+
_context6.next = 2;
|
|
1169
1177
|
break;
|
|
1170
1178
|
}
|
|
1171
|
-
return
|
|
1179
|
+
return _context6.abrupt("return", true);
|
|
1172
1180
|
case 2:
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
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
|
|
1176
1268
|
Array.from(this.subscriptions.entries()).forEach(function (_ref2) {
|
|
1177
1269
|
var _ref3 = (0, _slicedToArray2.default)(_ref2, 2),
|
|
1178
1270
|
resourceId = _ref3[0],
|
|
@@ -1184,12 +1276,7 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
1184
1276
|
});
|
|
1185
1277
|
});
|
|
1186
1278
|
});
|
|
1187
|
-
|
|
1188
|
-
_context5.next = 8;
|
|
1189
|
-
break;
|
|
1190
|
-
}
|
|
1191
|
-
throw new Error('Data provider not set');
|
|
1192
|
-
case 8:
|
|
1279
|
+
case 38:
|
|
1193
1280
|
// reset isCacheDirty early to prevent race condition
|
|
1194
1281
|
// There is a race condition where if a user makes changes (create/delete) to a reference sync block
|
|
1195
1282
|
// on a live page and the reference sync block is being saved while the user
|
|
@@ -1197,10 +1284,10 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
1197
1284
|
// exactly at a time when the updateReferenceData is being executed asynchronously.
|
|
1198
1285
|
this.isCacheDirty = false;
|
|
1199
1286
|
(_this$saveExperience = this.saveExperience) === null || _this$saveExperience === void 0 || _this$saveExperience.start();
|
|
1200
|
-
|
|
1287
|
+
_context6.next = 42;
|
|
1201
1288
|
return this.dataProvider.updateReferenceData(blocks);
|
|
1202
|
-
case
|
|
1203
|
-
updateResult =
|
|
1289
|
+
case 42:
|
|
1290
|
+
updateResult = _context6.sent;
|
|
1204
1291
|
if (!updateResult.success) {
|
|
1205
1292
|
success = false;
|
|
1206
1293
|
(_this$saveExperience2 = this.saveExperience) === null || _this$saveExperience2 === void 0 || _this$saveExperience2.failure({
|
|
@@ -1208,35 +1295,53 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
1208
1295
|
});
|
|
1209
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'));
|
|
1210
1297
|
}
|
|
1211
|
-
|
|
1298
|
+
_context6.next = 52;
|
|
1212
1299
|
break;
|
|
1213
|
-
case
|
|
1214
|
-
|
|
1215
|
-
|
|
1300
|
+
case 46:
|
|
1301
|
+
_context6.prev = 46;
|
|
1302
|
+
_context6.t2 = _context6["catch"](11);
|
|
1216
1303
|
success = false;
|
|
1217
|
-
(0, _monitoring.logException)(
|
|
1304
|
+
(0, _monitoring.logException)(_context6.t2, {
|
|
1218
1305
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
1219
1306
|
});
|
|
1220
1307
|
(_this$saveExperience3 = this.saveExperience) === null || _this$saveExperience3 === void 0 || _this$saveExperience3.failure({
|
|
1221
|
-
reason:
|
|
1308
|
+
reason: _context6.t2.message
|
|
1222
1309
|
});
|
|
1223
|
-
(_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 || _this$fireAnalyticsEv10.call(this, (0, _errorHandling.updateReferenceErrorPayload)(
|
|
1224
|
-
case
|
|
1225
|
-
|
|
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;
|
|
1226
1313
|
if (!success) {
|
|
1227
1314
|
// set isCacheDirty back to true for cases where it failed to update the reference synced blocks on the BE
|
|
1228
1315
|
this.isCacheDirty = true;
|
|
1229
1316
|
} else {
|
|
1317
|
+
if ((0, _platformFeatureFlags.fg)('platform_synced_block_patch_2')) {
|
|
1318
|
+
this.lastFlushedSyncedBlocks = syncedBlocksToFlush;
|
|
1319
|
+
}
|
|
1230
1320
|
(_this$saveExperience4 = this.saveExperience) === null || _this$saveExperience4 === void 0 || _this$saveExperience4.success();
|
|
1231
1321
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
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:
|
|
1236
1341
|
case "end":
|
|
1237
|
-
return
|
|
1342
|
+
return _context6.stop();
|
|
1238
1343
|
}
|
|
1239
|
-
}, _callee4, this, [[
|
|
1344
|
+
}, _callee4, this, [[11, 46, 52, 56], [17, 26, 29, 32]]);
|
|
1240
1345
|
}));
|
|
1241
1346
|
function flush() {
|
|
1242
1347
|
return _flush.apply(this, arguments);
|
|
@@ -1247,6 +1352,12 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
1247
1352
|
key: "destroy",
|
|
1248
1353
|
value: function destroy() {
|
|
1249
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
|
+
|
|
1250
1361
|
// Clean up all GraphQL subscriptions first
|
|
1251
1362
|
this.cleanupAllGraphQLSubscriptions();
|
|
1252
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) {
|
|
@@ -960,24 +967,63 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
960
967
|
*/
|
|
961
968
|
async flush() {
|
|
962
969
|
if (!this.isCacheDirty) {
|
|
970
|
+
// we use the isCacheDirty flag as a quick check.
|
|
963
971
|
return true;
|
|
964
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
|
+
}
|
|
965
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 = {};
|
|
966
990
|
try {
|
|
967
991
|
var _this$saveExperience;
|
|
992
|
+
if (!this.dataProvider) {
|
|
993
|
+
throw new Error('Data provider not set');
|
|
994
|
+
}
|
|
968
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
|
+
}
|
|
969
1008
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
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
|
+
});
|
|
976
1025
|
});
|
|
977
1026
|
});
|
|
978
|
-
});
|
|
979
|
-
if (!this.dataProvider) {
|
|
980
|
-
throw new Error('Data provider not set');
|
|
981
1027
|
}
|
|
982
1028
|
|
|
983
1029
|
// reset isCacheDirty early to prevent race condition
|
|
@@ -1012,13 +1058,37 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
1012
1058
|
this.isCacheDirty = true;
|
|
1013
1059
|
} else {
|
|
1014
1060
|
var _this$saveExperience4;
|
|
1061
|
+
if (fg('platform_synced_block_patch_2')) {
|
|
1062
|
+
this.lastFlushedSyncedBlocks = syncedBlocksToFlush;
|
|
1063
|
+
}
|
|
1015
1064
|
(_this$saveExperience4 = this.saveExperience) === null || _this$saveExperience4 === void 0 ? void 0 : _this$saveExperience4.success();
|
|
1016
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
|
+
}
|
|
1017
1081
|
}
|
|
1018
1082
|
return success;
|
|
1019
1083
|
}
|
|
1020
1084
|
destroy() {
|
|
1021
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
|
+
|
|
1022
1092
|
// Clean up all GraphQL subscriptions first
|
|
1023
1093
|
this.cleanupAllGraphQLSubscriptions();
|
|
1024
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) {
|
|
@@ -1154,19 +1161,104 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
1154
1161
|
key: "flush",
|
|
1155
1162
|
value: (function () {
|
|
1156
1163
|
var _flush = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
|
|
1157
|
-
var
|
|
1158
|
-
|
|
1159
|
-
|
|
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) {
|
|
1160
1168
|
case 0:
|
|
1161
1169
|
if (this.isCacheDirty) {
|
|
1162
|
-
|
|
1170
|
+
_context6.next = 2;
|
|
1163
1171
|
break;
|
|
1164
1172
|
}
|
|
1165
|
-
return
|
|
1173
|
+
return _context6.abrupt("return", true);
|
|
1166
1174
|
case 2:
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
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
|
|
1170
1262
|
Array.from(this.subscriptions.entries()).forEach(function (_ref2) {
|
|
1171
1263
|
var _ref3 = _slicedToArray(_ref2, 2),
|
|
1172
1264
|
resourceId = _ref3[0],
|
|
@@ -1178,12 +1270,7 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
1178
1270
|
});
|
|
1179
1271
|
});
|
|
1180
1272
|
});
|
|
1181
|
-
|
|
1182
|
-
_context5.next = 8;
|
|
1183
|
-
break;
|
|
1184
|
-
}
|
|
1185
|
-
throw new Error('Data provider not set');
|
|
1186
|
-
case 8:
|
|
1273
|
+
case 38:
|
|
1187
1274
|
// reset isCacheDirty early to prevent race condition
|
|
1188
1275
|
// There is a race condition where if a user makes changes (create/delete) to a reference sync block
|
|
1189
1276
|
// on a live page and the reference sync block is being saved while the user
|
|
@@ -1191,10 +1278,10 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
1191
1278
|
// exactly at a time when the updateReferenceData is being executed asynchronously.
|
|
1192
1279
|
this.isCacheDirty = false;
|
|
1193
1280
|
(_this$saveExperience = this.saveExperience) === null || _this$saveExperience === void 0 || _this$saveExperience.start();
|
|
1194
|
-
|
|
1281
|
+
_context6.next = 42;
|
|
1195
1282
|
return this.dataProvider.updateReferenceData(blocks);
|
|
1196
|
-
case
|
|
1197
|
-
updateResult =
|
|
1283
|
+
case 42:
|
|
1284
|
+
updateResult = _context6.sent;
|
|
1198
1285
|
if (!updateResult.success) {
|
|
1199
1286
|
success = false;
|
|
1200
1287
|
(_this$saveExperience2 = this.saveExperience) === null || _this$saveExperience2 === void 0 || _this$saveExperience2.failure({
|
|
@@ -1202,35 +1289,53 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
1202
1289
|
});
|
|
1203
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'));
|
|
1204
1291
|
}
|
|
1205
|
-
|
|
1292
|
+
_context6.next = 52;
|
|
1206
1293
|
break;
|
|
1207
|
-
case
|
|
1208
|
-
|
|
1209
|
-
|
|
1294
|
+
case 46:
|
|
1295
|
+
_context6.prev = 46;
|
|
1296
|
+
_context6.t2 = _context6["catch"](11);
|
|
1210
1297
|
success = false;
|
|
1211
|
-
logException(
|
|
1298
|
+
logException(_context6.t2, {
|
|
1212
1299
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
1213
1300
|
});
|
|
1214
1301
|
(_this$saveExperience3 = this.saveExperience) === null || _this$saveExperience3 === void 0 || _this$saveExperience3.failure({
|
|
1215
|
-
reason:
|
|
1302
|
+
reason: _context6.t2.message
|
|
1216
1303
|
});
|
|
1217
|
-
(_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 || _this$fireAnalyticsEv10.call(this, updateReferenceErrorPayload(
|
|
1218
|
-
case
|
|
1219
|
-
|
|
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;
|
|
1220
1307
|
if (!success) {
|
|
1221
1308
|
// set isCacheDirty back to true for cases where it failed to update the reference synced blocks on the BE
|
|
1222
1309
|
this.isCacheDirty = true;
|
|
1223
1310
|
} else {
|
|
1311
|
+
if (fg('platform_synced_block_patch_2')) {
|
|
1312
|
+
this.lastFlushedSyncedBlocks = syncedBlocksToFlush;
|
|
1313
|
+
}
|
|
1224
1314
|
(_this$saveExperience4 = this.saveExperience) === null || _this$saveExperience4 === void 0 || _this$saveExperience4.success();
|
|
1225
1315
|
}
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
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:
|
|
1230
1335
|
case "end":
|
|
1231
|
-
return
|
|
1336
|
+
return _context6.stop();
|
|
1232
1337
|
}
|
|
1233
|
-
}, _callee4, this, [[
|
|
1338
|
+
}, _callee4, this, [[11, 46, 52, 56], [17, 26, 29, 32]]);
|
|
1234
1339
|
}));
|
|
1235
1340
|
function flush() {
|
|
1236
1341
|
return _flush.apply(this, arguments);
|
|
@@ -1241,6 +1346,12 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
1241
1346
|
key: "destroy",
|
|
1242
1347
|
value: function destroy() {
|
|
1243
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
|
+
|
|
1244
1355
|
// Clean up all GraphQL subscriptions first
|
|
1245
1356
|
this.cleanupAllGraphQLSubscriptions();
|
|
1246
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",
|