@atlaskit/collab-provider 22.0.2 → 22.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # @atlaskit/collab-provider
2
2
 
3
+ ## 22.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`83cf312c45f2f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/83cf312c45f2f) -
8
+ Detect process suspension on reconnect so catchup runs after a device wakes from sleep, where
9
+ `disconnectedAt` is only stamped once the process resumes. Behind
10
+ `collab_check_sleep_detection_experiment`, read through `@atlaskit/platform-feature-experiments`.
11
+
12
+ `@atlaskit/collab-provider` adds no public API. `SleepDetector` is internal: it is not reachable
13
+ from the `.`, `./provider`, `./socket-io-provider`, `./types` or `./version-wrapper` entry points
14
+ and does not appear in `report.api.md`. The provider constructs and drives it internally, so
15
+ consumers need no migration and no call sites change.
16
+
17
+ `@atlaskit/tmp-editor-statsig` drops `collab_bypass_out_of_sync_period_experiment` from
18
+ `editorExperimentsConfig`, which is part of its typed surface. The key had no call sites outside
19
+ `@atlaskit/collab-provider`, which no longer reads it, so no consumer migration is expected in
20
+ practice. Any remaining caller must move to the new experiment:
21
+
22
+ ```ts
23
+ // before
24
+ if (expValEquals('collab_bypass_out_of_sync_period_experiment', 'isEnabled', true, false)) {
25
+ // catch up regardless of how long the client was offline
26
+ }
27
+
28
+ // after
29
+ if (isExperimentEnabled('collab_check_sleep_detection_experiment')) {
30
+ // catch up when a suspension of at least OUT_OF_SYNC_PERIOD was detected
31
+ }
32
+ ```
33
+
34
+ ### Patch Changes
35
+
36
+ - Updated dependencies
37
+
3
38
  ## 22.0.2
4
39
 
5
40
  ### Patch Changes
@@ -46,6 +46,9 @@
46
46
  {
47
47
  "path": "../../../measurement/feature-gate-js-client/afm-cc/tsconfig.json"
48
48
  },
49
+ {
50
+ "path": "../../../platform/feature-experiments/afm-cc/tsconfig.json"
51
+ },
49
52
  {
50
53
  "path": "../../../platform/feature-flags/afm-cc/tsconfig.json"
51
54
  },
@@ -17,6 +17,7 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
17
17
  var _uuid = require("uuid");
18
18
  var _emitter = require("../emitter");
19
19
  var _channel = require("../channel");
20
+ var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
20
21
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
21
22
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
22
23
  var _utils = require("../helpers/utils");
@@ -36,6 +37,7 @@ var _api = require("../api/api");
36
37
  var _performance = require("../analytics/performance");
37
38
  var _nullApi = require("../api/null-api");
38
39
  var _getOfflineStepsLength = require("./get-offline-steps-length");
40
+ var _sleepDetector = require("./sleep-detector");
39
41
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
40
42
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
41
43
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
@@ -66,6 +68,7 @@ var Provider = exports.Provider = /*#__PURE__*/function (_Emitter) {
66
68
  // It determines if the provider should initialize immediately and will only be true if:
67
69
  // the feature flag is enabled and the initial draft fetched from NCS is also passed in the config.
68
70
  (0, _defineProperty2.default)(_this, "isBufferingEnabled", false);
71
+ (0, _defineProperty2.default)(_this, "sleepDetector", new _sleepDetector.SleepDetector());
69
72
  // Local IDs of active AI Providers
70
73
  (0, _defineProperty2.default)(_this, "aiProviderActiveIds", []);
71
74
  /**
@@ -151,12 +154,10 @@ var Provider = exports.Provider = /*#__PURE__*/function (_Emitter) {
151
154
  });
152
155
  }
153
156
  // If already initialized, `connected` means reconnected
154
- var shouldBypassOutOfSyncGracePeriod = (0, _expValEquals.expValEquals)('collab_bypass_out_of_sync_period_experiment', 'isEnabled', true, false);
155
- if (initialized && _this.disconnectedAt && (
156
- // Offline longer than `OUT_OF_SYNC_PERIOD`
157
- shouldBypassOutOfSyncGracePeriod || Date.now() - _this.disconnectedAt >= OUT_OF_SYNC_PERIOD)) {
157
+ var offlineDuration = initialized ? _this.getOutOfSyncDuration() : undefined;
158
+ if (offlineDuration !== undefined) {
158
159
  _this.documentService.throttledCatchupv2(_const.CatchupEventReason.RECONNECTED, {
159
- disconnectionPeriodSeconds: Math.floor((Date.now() - _this.disconnectedAt) / 1000),
160
+ disconnectionPeriodSeconds: Math.floor(offlineDuration / 1000),
160
161
  offlineStepsLength: (0, _experiments.editorExperiment)('platform_editor_offline_editing_web', true) ? (0, _getOfflineStepsLength.getOfflineStepsLength)(_this.documentService.getUnconfirmedSteps(), _this.documentService.getUnconfirmedStepsOrigins()) : undefined,
161
162
  offlineReplaceStepsLength: (0, _experiments.editorExperiment)('platform_editor_offline_editing_web', true) ? (0, _getOfflineStepsLength.getOfflineReplaceStepsLength)(_this.documentService.getUnconfirmedSteps(), _this.documentService.getUnconfirmedStepsOrigins()) : undefined,
162
163
  unconfirmedStepsLength: unconfirmedStepsLength
@@ -179,6 +180,7 @@ var Provider = exports.Provider = /*#__PURE__*/function (_Emitter) {
179
180
  _this.participantsService.initializeFirstBatchFetchUsers();
180
181
  }
181
182
  _this.disconnectedAt = undefined;
183
+ _this.sleepDetector.reset();
182
184
  }).on('init', function (_ref3) {
183
185
  var doc = _ref3.doc,
184
186
  version = _ref3.version,
@@ -266,6 +268,20 @@ var Provider = exports.Provider = /*#__PURE__*/function (_Emitter) {
266
268
  (0, _defineProperty2.default)(_this, "getAIProviderActiveIds", function () {
267
269
  return _this.aiProviderActiveIds;
268
270
  });
271
+ (0, _defineProperty2.default)(_this, "getOutOfSyncDuration", function () {
272
+ if (!_this.disconnectedAt) {
273
+ return undefined;
274
+ }
275
+ var disconnectedDuration = Date.now() - _this.disconnectedAt;
276
+ if (disconnectedDuration >= OUT_OF_SYNC_PERIOD) {
277
+ return disconnectedDuration;
278
+ }
279
+ var sleepDuration = _this.sleepDetector.getSleepDuration();
280
+ if (sleepDuration >= OUT_OF_SYNC_PERIOD && (0, _isExperimentEnabled.isExperimentEnabled)('collab_check_sleep_detection_experiment')) {
281
+ return sleepDuration;
282
+ }
283
+ return undefined;
284
+ });
269
285
  // Note: this gets triggered on page reload for Firefox (not other browsers) because of closeOnBeforeunload: false
270
286
  (0, _defineProperty2.default)(_this, "onDisconnected", function (_ref5) {
271
287
  var reason = _ref5.reason;
@@ -453,6 +469,7 @@ var Provider = exports.Provider = /*#__PURE__*/function (_Emitter) {
453
469
  logger('Intervally sendStepsFromCurrentState');
454
470
  _this.documentService.sendStepsFromCurrentState(true, undefined);
455
471
  }, 5000);
472
+ _this.sleepDetector.start();
456
473
  return _this;
457
474
  }
458
475
  (0, _inherits2.default)(Provider, _Emitter);
@@ -713,6 +730,7 @@ var Provider = exports.Provider = /*#__PURE__*/function (_Emitter) {
713
730
  clearInterval(this.sendStepsTimer);
714
731
  this.sendStepsTimer = undefined;
715
732
  }
733
+ this.sleepDetector.stop();
716
734
  } catch (error) {
717
735
  var _this$analyticsHelper10;
718
736
  (_this$analyticsHelper10 = this.analyticsHelper) === null || _this$analyticsHelper10 === void 0 || _this$analyticsHelper10.sendErrorEvent(error, 'Error while shutting down the collab provider');
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.SleepDetector = void 0;
8
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
10
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
11
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
12
+ var TICK_INTERVAL = 1000;
13
+ var GAP_HISTORY_SIZE = 20;
14
+ var SleepDetector = exports.SleepDetector = /*#__PURE__*/function () {
15
+ function SleepDetector() {
16
+ var _this = this;
17
+ (0, _classCallCheck2.default)(this, SleepDetector);
18
+ (0, _defineProperty2.default)(this, "lastTickAt", Date.now());
19
+ (0, _defineProperty2.default)(this, "gaps", []);
20
+ (0, _defineProperty2.default)(this, "tick", function () {
21
+ var now = Date.now();
22
+ _this.gaps.push(now - _this.lastTickAt);
23
+ if (_this.gaps.length > GAP_HISTORY_SIZE) {
24
+ _this.gaps.shift();
25
+ }
26
+ _this.lastTickAt = now;
27
+ });
28
+ }
29
+ return (0, _createClass2.default)(SleepDetector, [{
30
+ key: "start",
31
+ value: function start() {
32
+ if (this.sentinel) {
33
+ return;
34
+ }
35
+ this.reset();
36
+ this.sentinel = setInterval(this.tick, TICK_INTERVAL);
37
+ }
38
+ }, {
39
+ key: "stop",
40
+ value: function stop() {
41
+ if (this.sentinel) {
42
+ clearInterval(this.sentinel);
43
+ this.sentinel = undefined;
44
+ }
45
+ }
46
+ }, {
47
+ key: "reset",
48
+ value: function reset() {
49
+ this.gaps = [];
50
+ this.lastTickAt = Date.now();
51
+ }
52
+ }, {
53
+ key: "getSleepDuration",
54
+ value: function getSleepDuration() {
55
+ return this.gaps.length ? Math.max.apply(Math, (0, _toConsumableArray2.default)(this.gaps)) : 0;
56
+ }
57
+ }]);
58
+ }();
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.version = exports.nextMajorVersion = exports.name = void 0;
7
7
  var name = exports.name = "@atlaskit/collab-provider";
8
- var version = exports.version = "22.0.1";
8
+ var version = exports.version = "22.0.2";
9
9
  var nextMajorVersion = exports.nextMajorVersion = function nextMajorVersion() {
10
10
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
11
11
  };
@@ -3,6 +3,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
3
  import { v4 as uuidv4 } from 'uuid';
4
4
  import { Emitter } from '../emitter';
5
5
  import { Channel } from '../channel';
6
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
6
7
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
7
8
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
8
9
  import { createLogger, logObfuscatedSteps } from '../helpers/utils';
@@ -22,6 +23,7 @@ import { Api } from '../api/api';
22
23
  import { shouldTelepointerBeSampled } from '../analytics/performance';
23
24
  import { NullApi } from '../api/null-api';
24
25
  import { getOfflineStepsLength, getOfflineReplaceStepsLength } from './get-offline-steps-length';
26
+ import { SleepDetector } from './sleep-detector';
25
27
  const logger = createLogger('Provider', 'black');
26
28
  const OUT_OF_SYNC_PERIOD = 3 * 1000; // 3 seconds
27
29
 
@@ -45,6 +47,7 @@ export class Provider extends Emitter {
45
47
  // It determines if the provider should initialize immediately and will only be true if:
46
48
  // the feature flag is enabled and the initial draft fetched from NCS is also passed in the config.
47
49
  _defineProperty(this, "isBufferingEnabled", false);
50
+ _defineProperty(this, "sleepDetector", new SleepDetector());
48
51
  // Local IDs of active AI Providers
49
52
  _defineProperty(this, "aiProviderActiveIds", []);
50
53
  /**
@@ -131,12 +134,10 @@ export class Provider extends Emitter {
131
134
  });
132
135
  }
133
136
  // If already initialized, `connected` means reconnected
134
- const shouldBypassOutOfSyncGracePeriod = expValEquals('collab_bypass_out_of_sync_period_experiment', 'isEnabled', true, false);
135
- if (initialized && this.disconnectedAt && (
136
- // Offline longer than `OUT_OF_SYNC_PERIOD`
137
- shouldBypassOutOfSyncGracePeriod || Date.now() - this.disconnectedAt >= OUT_OF_SYNC_PERIOD)) {
137
+ const offlineDuration = initialized ? this.getOutOfSyncDuration() : undefined;
138
+ if (offlineDuration !== undefined) {
138
139
  this.documentService.throttledCatchupv2(CatchupEventReason.RECONNECTED, {
139
- disconnectionPeriodSeconds: Math.floor((Date.now() - this.disconnectedAt) / 1000),
140
+ disconnectionPeriodSeconds: Math.floor(offlineDuration / 1000),
140
141
  offlineStepsLength: editorExperiment('platform_editor_offline_editing_web', true) ? getOfflineStepsLength(this.documentService.getUnconfirmedSteps(), this.documentService.getUnconfirmedStepsOrigins()) : undefined,
141
142
  offlineReplaceStepsLength: editorExperiment('platform_editor_offline_editing_web', true) ? getOfflineReplaceStepsLength(this.documentService.getUnconfirmedSteps(), this.documentService.getUnconfirmedStepsOrigins()) : undefined,
142
143
  unconfirmedStepsLength: unconfirmedStepsLength
@@ -159,6 +160,7 @@ export class Provider extends Emitter {
159
160
  this.participantsService.initializeFirstBatchFetchUsers();
160
161
  }
161
162
  this.disconnectedAt = undefined;
163
+ this.sleepDetector.reset();
162
164
  }).on('init', ({
163
165
  doc,
164
166
  version,
@@ -229,6 +231,20 @@ export class Provider extends Emitter {
229
231
  _defineProperty(this, "getAIProviderActiveIds", () => {
230
232
  return this.aiProviderActiveIds;
231
233
  });
234
+ _defineProperty(this, "getOutOfSyncDuration", () => {
235
+ if (!this.disconnectedAt) {
236
+ return undefined;
237
+ }
238
+ const disconnectedDuration = Date.now() - this.disconnectedAt;
239
+ if (disconnectedDuration >= OUT_OF_SYNC_PERIOD) {
240
+ return disconnectedDuration;
241
+ }
242
+ const sleepDuration = this.sleepDetector.getSleepDuration();
243
+ if (sleepDuration >= OUT_OF_SYNC_PERIOD && isExperimentEnabled('collab_check_sleep_detection_experiment')) {
244
+ return sleepDuration;
245
+ }
246
+ return undefined;
247
+ });
232
248
  // Note: this gets triggered on page reload for Firefox (not other browsers) because of closeOnBeforeunload: false
233
249
  _defineProperty(this, "onDisconnected", ({
234
250
  reason
@@ -368,6 +384,7 @@ export class Provider extends Emitter {
368
384
  logger('Intervally sendStepsFromCurrentState');
369
385
  this.documentService.sendStepsFromCurrentState(true, undefined);
370
386
  }, 5000);
387
+ this.sleepDetector.start();
371
388
  }
372
389
  // eslint-disable-next-line @repo/internal/deprecations/deprecation-ticket-required -- Ignored via go/ED-25883
373
390
  /**
@@ -609,6 +626,7 @@ export class Provider extends Emitter {
609
626
  clearInterval(this.sendStepsTimer);
610
627
  this.sendStepsTimer = undefined;
611
628
  }
629
+ this.sleepDetector.stop();
612
630
  } catch (error) {
613
631
  var _this$analyticsHelper12;
614
632
  (_this$analyticsHelper12 = this.analyticsHelper) === null || _this$analyticsHelper12 === void 0 ? void 0 : _this$analyticsHelper12.sendErrorEvent(error, 'Error while shutting down the collab provider');
@@ -0,0 +1,37 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ const TICK_INTERVAL = 1000;
3
+ const GAP_HISTORY_SIZE = 20;
4
+ export class SleepDetector {
5
+ constructor() {
6
+ _defineProperty(this, "lastTickAt", Date.now());
7
+ _defineProperty(this, "gaps", []);
8
+ _defineProperty(this, "tick", () => {
9
+ const now = Date.now();
10
+ this.gaps.push(now - this.lastTickAt);
11
+ if (this.gaps.length > GAP_HISTORY_SIZE) {
12
+ this.gaps.shift();
13
+ }
14
+ this.lastTickAt = now;
15
+ });
16
+ }
17
+ start() {
18
+ if (this.sentinel) {
19
+ return;
20
+ }
21
+ this.reset();
22
+ this.sentinel = setInterval(this.tick, TICK_INTERVAL);
23
+ }
24
+ stop() {
25
+ if (this.sentinel) {
26
+ clearInterval(this.sentinel);
27
+ this.sentinel = undefined;
28
+ }
29
+ }
30
+ reset() {
31
+ this.gaps = [];
32
+ this.lastTickAt = Date.now();
33
+ }
34
+ getSleepDuration() {
35
+ return this.gaps.length ? Math.max(...this.gaps) : 0;
36
+ }
37
+ }
@@ -1,5 +1,5 @@
1
1
  export const name = "@atlaskit/collab-provider";
2
- export const version = "22.0.1";
2
+ export const version = "22.0.2";
3
3
  export const nextMajorVersion = () => {
4
4
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
5
5
  };
@@ -16,6 +16,7 @@ function _superPropGet(t, o, e, r) { var p = _get(_getPrototypeOf(1 & r ? t.prot
16
16
  import { v4 as uuidv4 } from 'uuid';
17
17
  import { Emitter } from '../emitter';
18
18
  import { Channel } from '../channel';
19
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
19
20
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
20
21
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
21
22
  import { createLogger, logObfuscatedSteps } from '../helpers/utils';
@@ -35,6 +36,7 @@ import { Api } from '../api/api';
35
36
  import { shouldTelepointerBeSampled } from '../analytics/performance';
36
37
  import { NullApi } from '../api/null-api';
37
38
  import { getOfflineStepsLength, getOfflineReplaceStepsLength } from './get-offline-steps-length';
39
+ import { SleepDetector } from './sleep-detector';
38
40
  var logger = createLogger('Provider', 'black');
39
41
  var OUT_OF_SYNC_PERIOD = 3 * 1000; // 3 seconds
40
42
 
@@ -60,6 +62,7 @@ export var Provider = /*#__PURE__*/function (_Emitter) {
60
62
  // It determines if the provider should initialize immediately and will only be true if:
61
63
  // the feature flag is enabled and the initial draft fetched from NCS is also passed in the config.
62
64
  _defineProperty(_this, "isBufferingEnabled", false);
65
+ _defineProperty(_this, "sleepDetector", new SleepDetector());
63
66
  // Local IDs of active AI Providers
64
67
  _defineProperty(_this, "aiProviderActiveIds", []);
65
68
  /**
@@ -145,12 +148,10 @@ export var Provider = /*#__PURE__*/function (_Emitter) {
145
148
  });
146
149
  }
147
150
  // If already initialized, `connected` means reconnected
148
- var shouldBypassOutOfSyncGracePeriod = expValEquals('collab_bypass_out_of_sync_period_experiment', 'isEnabled', true, false);
149
- if (initialized && _this.disconnectedAt && (
150
- // Offline longer than `OUT_OF_SYNC_PERIOD`
151
- shouldBypassOutOfSyncGracePeriod || Date.now() - _this.disconnectedAt >= OUT_OF_SYNC_PERIOD)) {
151
+ var offlineDuration = initialized ? _this.getOutOfSyncDuration() : undefined;
152
+ if (offlineDuration !== undefined) {
152
153
  _this.documentService.throttledCatchupv2(CatchupEventReason.RECONNECTED, {
153
- disconnectionPeriodSeconds: Math.floor((Date.now() - _this.disconnectedAt) / 1000),
154
+ disconnectionPeriodSeconds: Math.floor(offlineDuration / 1000),
154
155
  offlineStepsLength: editorExperiment('platform_editor_offline_editing_web', true) ? getOfflineStepsLength(_this.documentService.getUnconfirmedSteps(), _this.documentService.getUnconfirmedStepsOrigins()) : undefined,
155
156
  offlineReplaceStepsLength: editorExperiment('platform_editor_offline_editing_web', true) ? getOfflineReplaceStepsLength(_this.documentService.getUnconfirmedSteps(), _this.documentService.getUnconfirmedStepsOrigins()) : undefined,
156
157
  unconfirmedStepsLength: unconfirmedStepsLength
@@ -173,6 +174,7 @@ export var Provider = /*#__PURE__*/function (_Emitter) {
173
174
  _this.participantsService.initializeFirstBatchFetchUsers();
174
175
  }
175
176
  _this.disconnectedAt = undefined;
177
+ _this.sleepDetector.reset();
176
178
  }).on('init', function (_ref3) {
177
179
  var doc = _ref3.doc,
178
180
  version = _ref3.version,
@@ -260,6 +262,20 @@ export var Provider = /*#__PURE__*/function (_Emitter) {
260
262
  _defineProperty(_this, "getAIProviderActiveIds", function () {
261
263
  return _this.aiProviderActiveIds;
262
264
  });
265
+ _defineProperty(_this, "getOutOfSyncDuration", function () {
266
+ if (!_this.disconnectedAt) {
267
+ return undefined;
268
+ }
269
+ var disconnectedDuration = Date.now() - _this.disconnectedAt;
270
+ if (disconnectedDuration >= OUT_OF_SYNC_PERIOD) {
271
+ return disconnectedDuration;
272
+ }
273
+ var sleepDuration = _this.sleepDetector.getSleepDuration();
274
+ if (sleepDuration >= OUT_OF_SYNC_PERIOD && isExperimentEnabled('collab_check_sleep_detection_experiment')) {
275
+ return sleepDuration;
276
+ }
277
+ return undefined;
278
+ });
263
279
  // Note: this gets triggered on page reload for Firefox (not other browsers) because of closeOnBeforeunload: false
264
280
  _defineProperty(_this, "onDisconnected", function (_ref5) {
265
281
  var reason = _ref5.reason;
@@ -447,6 +463,7 @@ export var Provider = /*#__PURE__*/function (_Emitter) {
447
463
  logger('Intervally sendStepsFromCurrentState');
448
464
  _this.documentService.sendStepsFromCurrentState(true, undefined);
449
465
  }, 5000);
466
+ _this.sleepDetector.start();
450
467
  return _this;
451
468
  }
452
469
  _inherits(Provider, _Emitter);
@@ -707,6 +724,7 @@ export var Provider = /*#__PURE__*/function (_Emitter) {
707
724
  clearInterval(this.sendStepsTimer);
708
725
  this.sendStepsTimer = undefined;
709
726
  }
727
+ this.sleepDetector.stop();
710
728
  } catch (error) {
711
729
  var _this$analyticsHelper10;
712
730
  (_this$analyticsHelper10 = this.analyticsHelper) === null || _this$analyticsHelper10 === void 0 || _this$analyticsHelper10.sendErrorEvent(error, 'Error while shutting down the collab provider');
@@ -0,0 +1,51 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
3
+ import _createClass from "@babel/runtime/helpers/createClass";
4
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
5
+ var TICK_INTERVAL = 1000;
6
+ var GAP_HISTORY_SIZE = 20;
7
+ export var SleepDetector = /*#__PURE__*/function () {
8
+ function SleepDetector() {
9
+ var _this = this;
10
+ _classCallCheck(this, SleepDetector);
11
+ _defineProperty(this, "lastTickAt", Date.now());
12
+ _defineProperty(this, "gaps", []);
13
+ _defineProperty(this, "tick", function () {
14
+ var now = Date.now();
15
+ _this.gaps.push(now - _this.lastTickAt);
16
+ if (_this.gaps.length > GAP_HISTORY_SIZE) {
17
+ _this.gaps.shift();
18
+ }
19
+ _this.lastTickAt = now;
20
+ });
21
+ }
22
+ return _createClass(SleepDetector, [{
23
+ key: "start",
24
+ value: function start() {
25
+ if (this.sentinel) {
26
+ return;
27
+ }
28
+ this.reset();
29
+ this.sentinel = setInterval(this.tick, TICK_INTERVAL);
30
+ }
31
+ }, {
32
+ key: "stop",
33
+ value: function stop() {
34
+ if (this.sentinel) {
35
+ clearInterval(this.sentinel);
36
+ this.sentinel = undefined;
37
+ }
38
+ }
39
+ }, {
40
+ key: "reset",
41
+ value: function reset() {
42
+ this.gaps = [];
43
+ this.lastTickAt = Date.now();
44
+ }
45
+ }, {
46
+ key: "getSleepDuration",
47
+ value: function getSleepDuration() {
48
+ return this.gaps.length ? Math.max.apply(Math, _toConsumableArray(this.gaps)) : 0;
49
+ }
50
+ }]);
51
+ }();
@@ -1,5 +1,5 @@
1
1
  export var name = "@atlaskit/collab-provider";
2
- export var version = "22.0.1";
2
+ export var version = "22.0.2";
3
3
  export var nextMajorVersion = function nextMajorVersion() {
4
4
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
5
5
  };
@@ -35,6 +35,7 @@ export declare class Provider extends Emitter<CollabEvents> implements CollabEdi
35
35
  private presenceUpdateTimeout?;
36
36
  private disconnectedAt?;
37
37
  private sendStepsTimer?;
38
+ private readonly sleepDetector;
38
39
  private readonly participantsService;
39
40
  private readonly metadataService;
40
41
  private readonly documentService;
@@ -108,6 +109,7 @@ export declare class Provider extends Emitter<CollabEvents> implements CollabEdi
108
109
  sendMessage(data: CollabTelepointerPayload | CollabActivityAIProviderChangedPayload | CollabPresenceActivityChangePayload): void;
109
110
  setAIProviderActiveIds(ids?: string[]): void;
110
111
  private getAIProviderActiveIds;
112
+ private getOutOfSyncDuration;
111
113
  private onDisconnected;
112
114
  /**
113
115
  * "Destroy" the provider, disconnect it's connection to the back-end service and unsubscribe all event listeners on the provider.
@@ -0,0 +1,10 @@
1
+ export declare class SleepDetector {
2
+ private lastTickAt;
3
+ private gaps;
4
+ private sentinel?;
5
+ start(): void;
6
+ stop(): void;
7
+ reset(): void;
8
+ getSleepDuration(): number;
9
+ private tick;
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/collab-provider",
3
- "version": "22.0.2",
3
+ "version": "22.1.0",
4
4
  "description": "A provider for collaborative editing.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -26,10 +26,11 @@
26
26
  "@atlaskit/editor-json-transformer": "^9.2.0",
27
27
  "@atlaskit/editor-prosemirror": "^8.0.0",
28
28
  "@atlaskit/feature-gate-js-client": "^6.0.0",
29
+ "@atlaskit/platform-feature-experiments": "^0.3.0",
29
30
  "@atlaskit/platform-feature-flags": "^2.1.0",
30
31
  "@atlaskit/prosemirror-collab": "^1.0.0",
31
32
  "@atlaskit/react-ufo": "^7.3.0",
32
- "@atlaskit/tmp-editor-statsig": "^139.0.0",
33
+ "@atlaskit/tmp-editor-statsig": "^140.0.0",
33
34
  "@atlaskit/ufo": "^1.0.0",
34
35
  "@atlaskit/util-service-support": "^7.2.0",
35
36
  "@babel/runtime": "^7.0.0",
@@ -57,6 +58,7 @@
57
58
  "devDependencies": {
58
59
  "@atlaskit/adf-schema": "^56.5.0",
59
60
  "@atlassian/a11y-jest-testing": "^0.14.0",
61
+ "@atlassian/experiment-test-utils": "^0.2.0",
60
62
  "@atlassian/feature-flags-test-utils": "^1.2.0"
61
63
  },
62
64
  "platform-feature-flags": {
@@ -71,6 +73,6 @@
71
73
  }
72
74
  },
73
75
  "peerDependencies": {
74
- "@atlaskit/editor-common": "^117.1.0"
76
+ "@atlaskit/editor-common": "^117.2.0"
75
77
  }
76
78
  }