@atlaskit/collab-provider 10.15.0 → 10.16.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,17 @@
1
1
  # @atlaskit/collab-provider
2
2
 
3
+ ## 10.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#158254](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/158254)
8
+ [`9ce9448c0e11a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/9ce9448c0e11a) -
9
+ Add auto-close for Presence connections when window is in background
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+
3
15
  ## 10.15.0
4
16
 
5
17
  ### Minor Changes
@@ -25,6 +25,8 @@ var _network = _interopRequireDefault(require("./connectivity/network"));
25
25
  var _internalErrors = require("./errors/internal-errors");
26
26
  var _ncsErrors = require("./errors/ncs-errors");
27
27
  var _customErrors = require("./errors/custom-errors");
28
+ var _featureGateJsClient = _interopRequireDefault(require("@atlaskit/feature-gate-js-client"));
29
+ var _bindEventListener = require("bind-event-listener");
28
30
  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; } } }; }
29
31
  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; } }
30
32
  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; }
@@ -418,6 +420,40 @@ var Channel = exports.Channel = /*#__PURE__*/function (_Emitter) {
418
420
  (_this$socket3 = _this.socket) === null || _this$socket3 === void 0 || _this$socket3.connect();
419
421
  }
420
422
  });
423
+ /**
424
+ * Unbinds event listeners and timers used when handling connection auto-close when tab is hidden
425
+ */
426
+ (0, _defineProperty2.default)(_this, "cleanupAutoDisconnect", function () {
427
+ _this.unbindVisibilityListener();
428
+ if (_this.disconnectTimer) {
429
+ clearTimeout(_this.disconnectTimer);
430
+ _this.disconnectTimer = undefined;
431
+ }
432
+ });
433
+ /**
434
+ * Cleanup the visiblitychange listener upon Collab Provider destroy
435
+ * Value set when the listener is binded in addVisiblityListener
436
+ */
437
+ (0, _defineProperty2.default)(_this, "unbindVisibilityListener", function () {});
438
+ (0, _defineProperty2.default)(_this, "autoDisconnect", function (disconnectTimer, disconnectDelay) {
439
+ if (document.hidden) {
440
+ logger('visibilitychange: hidden');
441
+ return setTimeout(function () {
442
+ var _this$socket4;
443
+ logger('visibilitychange: closing connection');
444
+ (_this$socket4 = _this.socket) === null || _this$socket4 === void 0 || _this$socket4.close();
445
+ }, disconnectDelay || disconnectDelay === 0 ? disconnectDelay * 1000 : 60 * 1000);
446
+ } else {
447
+ var _this$socket5;
448
+ logger('visibilitychange: visible');
449
+ if (disconnectTimer) {
450
+ clearTimeout(disconnectTimer);
451
+ }
452
+ logger('visibilitychange: re-connecting');
453
+ (_this$socket5 = _this.socket) === null || _this$socket5 === void 0 || _this$socket5.connect();
454
+ return;
455
+ }
456
+ });
421
457
  _this.config = config;
422
458
  _this.analyticsHelper = analyticsHelper;
423
459
  _this.initExperience = (0, _ufo.createDocInitExp)(_this.analyticsHelper);
@@ -651,6 +687,10 @@ var Channel = exports.Channel = /*#__PURE__*/function (_Emitter) {
651
687
  this.reconnectHelper = new _reconnectHelper.default();
652
688
  // Fired upon a reconnection attempt error (from Socket.IO Manager)
653
689
  this.socket.io.on('reconnect_error', this.onReconnectError);
690
+
691
+ // Automatic disconnect on tab background with delay, to keep presence UX accurate
692
+ // and reduce wasted connections. Reconnects upon tab becoming active again
693
+ this.addVisiblityListener();
654
694
  }
655
695
 
656
696
  // Ignored via go/ees005
@@ -775,11 +815,32 @@ var Channel = exports.Channel = /*#__PURE__*/function (_Emitter) {
775
815
  value: function isLimitExceeded(stepLimit, stepSizeLimit, maxStepSizeLimit) {
776
816
  return stepLimit > 0 && this.stepCounter > stepLimit || stepSizeLimit > 0 && this.stepSizeCounter > stepSizeLimit || maxStepSizeLimit > 0 && this.maxStepSize > maxStepSizeLimit;
777
817
  }
818
+ }, {
819
+ key: "addVisiblityListener",
820
+ value:
821
+ /**
822
+ * Adds an event listener for visibilitychange events to handle auto-disconnection if tab is in background
823
+ */
824
+ function addVisiblityListener() {
825
+ var _FeatureGates$getExpe,
826
+ _this3 = this;
827
+ var disconnectDelay = (_FeatureGates$getExpe = _featureGateJsClient.default.getExperimentValue('platform_editor_connection_auto_disconnect_delay', 'delay', -1)) !== null && _FeatureGates$getExpe !== void 0 ? _FeatureGates$getExpe : -1;
828
+ var isAutoDisconnectEnabled = disconnectDelay >= 0;
829
+ if (isAutoDisconnectEnabled && this.config.isPresenceOnly) {
830
+ this.unbindVisibilityListener = (0, _bindEventListener.bind)(document, {
831
+ type: 'visibilitychange',
832
+ listener: function listener() {
833
+ _this3.disconnectTimer = _this3.autoDisconnect(_this3.disconnectTimer, disconnectDelay);
834
+ }
835
+ });
836
+ }
837
+ }
778
838
  }, {
779
839
  key: "disconnect",
780
840
  value: function disconnect() {
781
841
  var _this$network;
782
842
  this.unsubscribeAll();
843
+ this.cleanupAutoDisconnect();
783
844
  (_this$network = this.network) === null || _this$network === void 0 || _this$network.destroy();
784
845
  this.network = null;
785
846
  if (this.socket) {
@@ -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 = "10.15.0";
8
+ var version = exports.version = "10.16.0";
9
9
  var nextMajorVersion = exports.nextMajorVersion = function nextMajorVersion() {
10
10
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
11
11
  };
@@ -11,6 +11,8 @@ import Network from './connectivity/network';
11
11
  import { INTERNAL_ERROR_CODE } from './errors/internal-errors';
12
12
  import { NCS_ERROR_CODE } from './errors/ncs-errors';
13
13
  import { NotConnectedError, NotInitializedError } from './errors/custom-errors';
14
+ import FeatureGates from '@atlaskit/feature-gate-js-client';
15
+ import { bind } from 'bind-event-listener';
14
16
  const logger = createLogger('Channel', 'green');
15
17
  export class Channel extends Emitter {
16
18
  constructor(config, analyticsHelper) {
@@ -299,6 +301,40 @@ export class Channel extends Emitter {
299
301
  (_this$socket3 = this.socket) === null || _this$socket3 === void 0 ? void 0 : _this$socket3.connect();
300
302
  }
301
303
  });
304
+ /**
305
+ * Unbinds event listeners and timers used when handling connection auto-close when tab is hidden
306
+ */
307
+ _defineProperty(this, "cleanupAutoDisconnect", () => {
308
+ this.unbindVisibilityListener();
309
+ if (this.disconnectTimer) {
310
+ clearTimeout(this.disconnectTimer);
311
+ this.disconnectTimer = undefined;
312
+ }
313
+ });
314
+ /**
315
+ * Cleanup the visiblitychange listener upon Collab Provider destroy
316
+ * Value set when the listener is binded in addVisiblityListener
317
+ */
318
+ _defineProperty(this, "unbindVisibilityListener", () => {});
319
+ _defineProperty(this, "autoDisconnect", (disconnectTimer, disconnectDelay) => {
320
+ if (document.hidden) {
321
+ logger('visibilitychange: hidden');
322
+ return setTimeout(() => {
323
+ var _this$socket4;
324
+ logger('visibilitychange: closing connection');
325
+ (_this$socket4 = this.socket) === null || _this$socket4 === void 0 ? void 0 : _this$socket4.close();
326
+ }, disconnectDelay || disconnectDelay === 0 ? disconnectDelay * 1000 : 60 * 1000);
327
+ } else {
328
+ var _this$socket5;
329
+ logger('visibilitychange: visible');
330
+ if (disconnectTimer) {
331
+ clearTimeout(disconnectTimer);
332
+ }
333
+ logger('visibilitychange: re-connecting');
334
+ (_this$socket5 = this.socket) === null || _this$socket5 === void 0 ? void 0 : _this$socket5.connect();
335
+ return;
336
+ }
337
+ });
302
338
  this.config = config;
303
339
  this.analyticsHelper = analyticsHelper;
304
340
  this.initExperience = createDocInitExp(this.analyticsHelper);
@@ -480,6 +516,10 @@ export class Channel extends Emitter {
480
516
  this.reconnectHelper = new ReconnectHelper();
481
517
  // Fired upon a reconnection attempt error (from Socket.IO Manager)
482
518
  this.socket.io.on('reconnect_error', this.onReconnectError);
519
+
520
+ // Automatic disconnect on tab background with delay, to keep presence UX accurate
521
+ // and reduce wasted connections. Reconnects upon tab becoming active again
522
+ this.addVisiblityListener();
483
523
  }
484
524
 
485
525
  // Ignored via go/ees005
@@ -546,9 +586,26 @@ export class Channel extends Emitter {
546
586
  isLimitExceeded(stepLimit, stepSizeLimit, maxStepSizeLimit) {
547
587
  return stepLimit > 0 && this.stepCounter > stepLimit || stepSizeLimit > 0 && this.stepSizeCounter > stepSizeLimit || maxStepSizeLimit > 0 && this.maxStepSize > maxStepSizeLimit;
548
588
  }
589
+ /**
590
+ * Adds an event listener for visibilitychange events to handle auto-disconnection if tab is in background
591
+ */
592
+ addVisiblityListener() {
593
+ var _FeatureGates$getExpe;
594
+ const disconnectDelay = (_FeatureGates$getExpe = FeatureGates.getExperimentValue('platform_editor_connection_auto_disconnect_delay', 'delay', -1)) !== null && _FeatureGates$getExpe !== void 0 ? _FeatureGates$getExpe : -1;
595
+ const isAutoDisconnectEnabled = disconnectDelay >= 0;
596
+ if (isAutoDisconnectEnabled && this.config.isPresenceOnly) {
597
+ this.unbindVisibilityListener = bind(document, {
598
+ type: 'visibilitychange',
599
+ listener: () => {
600
+ this.disconnectTimer = this.autoDisconnect(this.disconnectTimer, disconnectDelay);
601
+ }
602
+ });
603
+ }
604
+ }
549
605
  disconnect() {
550
606
  var _this$network;
551
607
  this.unsubscribeAll();
608
+ this.cleanupAutoDisconnect();
552
609
  (_this$network = this.network) === null || _this$network === void 0 ? void 0 : _this$network.destroy();
553
610
  this.network = null;
554
611
  if (this.socket) {
@@ -1,5 +1,5 @@
1
1
  export const name = "@atlaskit/collab-provider";
2
- export const version = "10.15.0";
2
+ export const version = "10.16.0";
3
3
  export const nextMajorVersion = () => {
4
4
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
5
5
  };
@@ -25,6 +25,8 @@ import Network from './connectivity/network';
25
25
  import { INTERNAL_ERROR_CODE } from './errors/internal-errors';
26
26
  import { NCS_ERROR_CODE } from './errors/ncs-errors';
27
27
  import { NotConnectedError, NotInitializedError } from './errors/custom-errors';
28
+ import FeatureGates from '@atlaskit/feature-gate-js-client';
29
+ import { bind } from 'bind-event-listener';
28
30
  var logger = createLogger('Channel', 'green');
29
31
  export var Channel = /*#__PURE__*/function (_Emitter) {
30
32
  function Channel(config, analyticsHelper) {
@@ -411,6 +413,40 @@ export var Channel = /*#__PURE__*/function (_Emitter) {
411
413
  (_this$socket3 = _this.socket) === null || _this$socket3 === void 0 || _this$socket3.connect();
412
414
  }
413
415
  });
416
+ /**
417
+ * Unbinds event listeners and timers used when handling connection auto-close when tab is hidden
418
+ */
419
+ _defineProperty(_this, "cleanupAutoDisconnect", function () {
420
+ _this.unbindVisibilityListener();
421
+ if (_this.disconnectTimer) {
422
+ clearTimeout(_this.disconnectTimer);
423
+ _this.disconnectTimer = undefined;
424
+ }
425
+ });
426
+ /**
427
+ * Cleanup the visiblitychange listener upon Collab Provider destroy
428
+ * Value set when the listener is binded in addVisiblityListener
429
+ */
430
+ _defineProperty(_this, "unbindVisibilityListener", function () {});
431
+ _defineProperty(_this, "autoDisconnect", function (disconnectTimer, disconnectDelay) {
432
+ if (document.hidden) {
433
+ logger('visibilitychange: hidden');
434
+ return setTimeout(function () {
435
+ var _this$socket4;
436
+ logger('visibilitychange: closing connection');
437
+ (_this$socket4 = _this.socket) === null || _this$socket4 === void 0 || _this$socket4.close();
438
+ }, disconnectDelay || disconnectDelay === 0 ? disconnectDelay * 1000 : 60 * 1000);
439
+ } else {
440
+ var _this$socket5;
441
+ logger('visibilitychange: visible');
442
+ if (disconnectTimer) {
443
+ clearTimeout(disconnectTimer);
444
+ }
445
+ logger('visibilitychange: re-connecting');
446
+ (_this$socket5 = _this.socket) === null || _this$socket5 === void 0 || _this$socket5.connect();
447
+ return;
448
+ }
449
+ });
414
450
  _this.config = config;
415
451
  _this.analyticsHelper = analyticsHelper;
416
452
  _this.initExperience = createDocInitExp(_this.analyticsHelper);
@@ -644,6 +680,10 @@ export var Channel = /*#__PURE__*/function (_Emitter) {
644
680
  this.reconnectHelper = new ReconnectHelper();
645
681
  // Fired upon a reconnection attempt error (from Socket.IO Manager)
646
682
  this.socket.io.on('reconnect_error', this.onReconnectError);
683
+
684
+ // Automatic disconnect on tab background with delay, to keep presence UX accurate
685
+ // and reduce wasted connections. Reconnects upon tab becoming active again
686
+ this.addVisiblityListener();
647
687
  }
648
688
 
649
689
  // Ignored via go/ees005
@@ -768,11 +808,32 @@ export var Channel = /*#__PURE__*/function (_Emitter) {
768
808
  value: function isLimitExceeded(stepLimit, stepSizeLimit, maxStepSizeLimit) {
769
809
  return stepLimit > 0 && this.stepCounter > stepLimit || stepSizeLimit > 0 && this.stepSizeCounter > stepSizeLimit || maxStepSizeLimit > 0 && this.maxStepSize > maxStepSizeLimit;
770
810
  }
811
+ }, {
812
+ key: "addVisiblityListener",
813
+ value:
814
+ /**
815
+ * Adds an event listener for visibilitychange events to handle auto-disconnection if tab is in background
816
+ */
817
+ function addVisiblityListener() {
818
+ var _FeatureGates$getExpe,
819
+ _this3 = this;
820
+ var disconnectDelay = (_FeatureGates$getExpe = FeatureGates.getExperimentValue('platform_editor_connection_auto_disconnect_delay', 'delay', -1)) !== null && _FeatureGates$getExpe !== void 0 ? _FeatureGates$getExpe : -1;
821
+ var isAutoDisconnectEnabled = disconnectDelay >= 0;
822
+ if (isAutoDisconnectEnabled && this.config.isPresenceOnly) {
823
+ this.unbindVisibilityListener = bind(document, {
824
+ type: 'visibilitychange',
825
+ listener: function listener() {
826
+ _this3.disconnectTimer = _this3.autoDisconnect(_this3.disconnectTimer, disconnectDelay);
827
+ }
828
+ });
829
+ }
830
+ }
771
831
  }, {
772
832
  key: "disconnect",
773
833
  value: function disconnect() {
774
834
  var _this$network;
775
835
  this.unsubscribeAll();
836
+ this.cleanupAutoDisconnect();
776
837
  (_this$network = this.network) === null || _this$network === void 0 || _this$network.destroy();
777
838
  this.network = null;
778
839
  if (this.socket) {
@@ -1,5 +1,5 @@
1
1
  export var name = "@atlaskit/collab-provider";
2
- export var version = "10.15.0";
2
+ export var version = "10.16.0";
3
3
  export var nextMajorVersion = function nextMajorVersion() {
4
4
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
5
5
  };
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import { Emitter } from './emitter';
2
3
  import type { Config, ChannelEvent, Catchupv2Response, ReconcileResponse } from './types';
3
4
  import { type CatchupEventReason } from './helpers/const';
@@ -17,6 +18,7 @@ export declare class Channel extends Emitter<ChannelEvent> {
17
18
  private initExperience?;
18
19
  private token?;
19
20
  private network;
21
+ private disconnectTimer?;
20
22
  private readonly rateLimitWindowDurationMs;
21
23
  private rateLimitWindowStartMs;
22
24
  private stepCounter;
@@ -56,5 +58,19 @@ export declare class Channel extends Emitter<ChannelEvent> {
56
58
  sendMetadata: (metadata: Metadata) => void;
57
59
  sendPresenceJoined: () => void;
58
60
  onOnlineHandler: () => void;
61
+ /**
62
+ * Unbinds event listeners and timers used when handling connection auto-close when tab is hidden
63
+ */
64
+ private cleanupAutoDisconnect;
65
+ /**
66
+ * Cleanup the visiblitychange listener upon Collab Provider destroy
67
+ * Value set when the listener is binded in addVisiblityListener
68
+ */
69
+ private unbindVisibilityListener;
70
+ /**
71
+ * Adds an event listener for visibilitychange events to handle auto-disconnection if tab is in background
72
+ */
73
+ private addVisiblityListener;
74
+ autoDisconnect: (disconnectTimer: ReturnType<typeof setTimeout> | undefined, disconnectDelay: number) => NodeJS.Timeout | undefined;
59
75
  disconnect(): void;
60
76
  }
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import { Emitter } from './emitter';
2
3
  import type { Config, ChannelEvent, Catchupv2Response, ReconcileResponse } from './types';
3
4
  import { type CatchupEventReason } from './helpers/const';
@@ -17,6 +18,7 @@ export declare class Channel extends Emitter<ChannelEvent> {
17
18
  private initExperience?;
18
19
  private token?;
19
20
  private network;
21
+ private disconnectTimer?;
20
22
  private readonly rateLimitWindowDurationMs;
21
23
  private rateLimitWindowStartMs;
22
24
  private stepCounter;
@@ -56,5 +58,19 @@ export declare class Channel extends Emitter<ChannelEvent> {
56
58
  sendMetadata: (metadata: Metadata) => void;
57
59
  sendPresenceJoined: () => void;
58
60
  onOnlineHandler: () => void;
61
+ /**
62
+ * Unbinds event listeners and timers used when handling connection auto-close when tab is hidden
63
+ */
64
+ private cleanupAutoDisconnect;
65
+ /**
66
+ * Cleanup the visiblitychange listener upon Collab Provider destroy
67
+ * Value set when the listener is binded in addVisiblityListener
68
+ */
69
+ private unbindVisibilityListener;
70
+ /**
71
+ * Adds an event listener for visibilitychange events to handle auto-disconnection if tab is in background
72
+ */
73
+ private addVisiblityListener;
74
+ autoDisconnect: (disconnectTimer: ReturnType<typeof setTimeout> | undefined, disconnectDelay: number) => NodeJS.Timeout | undefined;
59
75
  disconnect(): void;
60
76
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/collab-provider",
3
- "version": "10.15.0",
3
+ "version": "10.16.0",
4
4
  "description": "A provider for collaborative editing.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -42,10 +42,11 @@
42
42
  "@atlaskit/platform-feature-flags": "^1.1.0",
43
43
  "@atlaskit/prosemirror-collab": "^0.16.0",
44
44
  "@atlaskit/react-ufo": "^3.13.0",
45
- "@atlaskit/tmp-editor-statsig": "^4.24.0",
45
+ "@atlaskit/tmp-editor-statsig": "^4.25.0",
46
46
  "@atlaskit/ufo": "^0.4.0",
47
47
  "@atlaskit/util-service-support": "^6.3.0",
48
48
  "@babel/runtime": "^7.0.0",
49
+ "bind-event-listener": "^3.0.0",
49
50
  "eventemitter2": "^4.1.0",
50
51
  "lodash": "^4.17.21",
51
52
  "prosemirror-changeset": "^2.2.1",