@atlaskit/collab-provider 8.8.2 → 8.9.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/analytics/analytics-helper.js +21 -12
  3. package/dist/cjs/channel.js +5 -3
  4. package/dist/cjs/document/document-service.js +1 -1
  5. package/dist/cjs/errors/error-code-mapper.js +9 -2
  6. package/dist/cjs/namespace/namespace-service.js +77 -0
  7. package/dist/cjs/participants/participants-helper.js +2 -0
  8. package/dist/cjs/participants/participants-service.js +94 -30
  9. package/dist/cjs/provider/index.js +91 -173
  10. package/dist/cjs/version-wrapper.js +1 -1
  11. package/dist/cjs/version.json +1 -1
  12. package/dist/es2019/analytics/analytics-helper.js +21 -12
  13. package/dist/es2019/channel.js +5 -3
  14. package/dist/es2019/document/document-service.js +1 -1
  15. package/dist/es2019/errors/error-code-mapper.js +9 -2
  16. package/dist/es2019/namespace/namespace-service.js +48 -0
  17. package/dist/es2019/participants/participants-helper.js +2 -0
  18. package/dist/es2019/participants/participants-service.js +88 -30
  19. package/dist/es2019/provider/index.js +82 -145
  20. package/dist/es2019/version-wrapper.js +1 -1
  21. package/dist/es2019/version.json +1 -1
  22. package/dist/esm/analytics/analytics-helper.js +21 -12
  23. package/dist/esm/channel.js +5 -3
  24. package/dist/esm/document/document-service.js +1 -1
  25. package/dist/esm/errors/error-code-mapper.js +9 -2
  26. package/dist/esm/namespace/namespace-service.js +69 -0
  27. package/dist/esm/participants/participants-helper.js +2 -0
  28. package/dist/esm/participants/participants-service.js +94 -30
  29. package/dist/esm/provider/index.js +91 -173
  30. package/dist/esm/version-wrapper.js +1 -1
  31. package/dist/esm/version.json +1 -1
  32. package/dist/types/namespace/namespace-service.d.ts +14 -0
  33. package/dist/types/participants/participants-helper.d.ts +2 -4
  34. package/dist/types/participants/participants-service.d.ts +40 -16
  35. package/dist/types/provider/index.d.ts +20 -36
  36. package/dist/types/types.d.ts +1 -1
  37. package/dist/types-ts4.5/analytics/analytics-helper.d.ts +11 -0
  38. package/dist/types-ts4.5/analytics/performance.d.ts +15 -0
  39. package/dist/types-ts4.5/analytics/ufo.d.ts +3 -0
  40. package/dist/types-ts4.5/channel.d.ts +47 -0
  41. package/dist/types-ts4.5/config.d.ts +5 -0
  42. package/dist/types-ts4.5/connectivity/network.d.ts +17 -0
  43. package/dist/types-ts4.5/connectivity/reconnect-helper.d.ts +8 -0
  44. package/dist/types-ts4.5/connectivity/singleton.d.ts +3 -0
  45. package/dist/types-ts4.5/disconnected-reason-mapper.d.ts +16 -0
  46. package/dist/types-ts4.5/document/catchup.d.ts +9 -0
  47. package/dist/types-ts4.5/document/document-service.d.ts +86 -0
  48. package/dist/types-ts4.5/document/step-queue-state.d.ts +16 -0
  49. package/dist/types-ts4.5/emitter.d.ts +19 -0
  50. package/dist/types-ts4.5/errors/error-code-mapper.d.ts +2 -0
  51. package/dist/types-ts4.5/errors/error-types.d.ts +443 -0
  52. package/dist/types-ts4.5/feature-flags/__test__/index.unit.d.ts +1 -0
  53. package/dist/types-ts4.5/feature-flags/index.d.ts +9 -0
  54. package/dist/types-ts4.5/feature-flags/types.d.ts +13 -0
  55. package/dist/types-ts4.5/helpers/const.d.ts +183 -0
  56. package/dist/types-ts4.5/helpers/utils.d.ts +5 -0
  57. package/dist/types-ts4.5/index.d.ts +4 -0
  58. package/dist/types-ts4.5/metadata/metadata-service.d.ts +25 -0
  59. package/dist/types-ts4.5/namespace/namespace-service.d.ts +14 -0
  60. package/dist/types-ts4.5/participants/participants-helper.d.ts +12 -0
  61. package/dist/types-ts4.5/participants/participants-service.d.ts +94 -0
  62. package/dist/types-ts4.5/participants/participants-state.d.ts +13 -0
  63. package/dist/types-ts4.5/participants/telepointers-helper.d.ts +4 -0
  64. package/dist/types-ts4.5/provider/commit-step.d.ts +25 -0
  65. package/dist/types-ts4.5/provider/index.d.ts +146 -0
  66. package/dist/types-ts4.5/socket-io-provider.d.ts +5 -0
  67. package/dist/types-ts4.5/types.d.ts +265 -0
  68. package/dist/types-ts4.5/version-wrapper.d.ts +3 -0
  69. package/package.json +3 -3
  70. package/report.api.md +4 -2
@@ -4,16 +4,27 @@ import { EVENT_ACTION, EVENT_STATUS } from '../helpers/const';
4
4
  import { telepointerFromStep } from './telepointers-helper';
5
5
  import { createParticipantFromPayload as enrichParticipant, PARTICIPANT_UPDATE_INTERVAL } from './participants-helper';
6
6
  import { ParticipantsState } from './participants-state';
7
+ import { createLogger } from '../helpers/utils';
8
+ const logger = createLogger('PresenceService', 'pink');
9
+ const SEND_PRESENCE_INTERVAL = 150 * 1000; // 150 seconds
10
+
11
+ /**
12
+ * This service is responsible for handling presence and participant events, as well as sending them on to the editor or NCS.
13
+ * @param analyticsHelper Analytics helper instance
14
+ * @param participantsState Starts with no participants, only add this when testing
15
+ * @param emit Emit from the Provider class (to the editor)
16
+ * @param getUser Callback to get user data from the editor
17
+ * @param channelBroadcast Broadcast from the Channel class (to NCS)
18
+ * @param sendPresenceJoined Callback to Channel class
19
+ */
7
20
  export class ParticipantsService {
8
- constructor(analyticsHelper, participantsState = new ParticipantsState()) {
21
+ constructor(analyticsHelper, participantsState = new ParticipantsState(), emit, getUser, channelBroadcast, sendPresenceJoined) {
9
22
  /**
10
23
  * Carries out 3 things: 1) enriches the participant with user data, 2) updates the participantsState, 3) emits the presence event
11
24
  * @param payload Payload from incoming socket event
12
- * @param getUser Function to get user data from confluence
13
- * @param emit Function to execute emit from provider socket
14
25
  * @returns Awaitable Promise, due to getUser
15
26
  */
16
- _defineProperty(this, "updateParticipant", async (payload, getUser, emit) => {
27
+ _defineProperty(this, "onParticipantUpdated", async payload => {
17
28
  const {
18
29
  userId
19
30
  } = payload;
@@ -30,11 +41,11 @@ export class ParticipantsService {
30
41
  {
31
42
  ...payload,
32
43
  userId
33
- }, getUser);
44
+ }, this.getUser);
34
45
  } catch (error) {
35
46
  var _this$analyticsHelper;
36
47
  // We don't want to throw errors for Presence features as they tend to self-restore
37
- (_this$analyticsHelper = this.analyticsHelper) === null || _this$analyticsHelper === void 0 ? void 0 : _this$analyticsHelper.sendErrorEvent(error, 'enriching participant');
48
+ (_this$analyticsHelper = this.analyticsHelper) === null || _this$analyticsHelper === void 0 ? void 0 : _this$analyticsHelper.sendErrorEvent(error, 'Error while enriching participant');
38
49
  }
39
50
  if (!participant) {
40
51
  return;
@@ -46,28 +57,27 @@ export class ParticipantsService {
46
57
  }
47
58
  this.emitPresence({
48
59
  joined: [participant]
49
- }, emit, 'handling participant updated event');
60
+ }, 'handling participant updated event');
50
61
  });
51
62
  /**
52
63
  * Called when a participant leaves the session.
53
- *
54
64
  * We emit the `presence` event to update the active avatars in the editor.
55
65
  */
56
- _defineProperty(this, "participantLeft", ({
66
+ _defineProperty(this, "onParticipantLeft", ({
57
67
  sessionId
58
- }, emit) => {
68
+ }) => {
59
69
  this.participantsState.removeBySessionId(sessionId);
60
70
  this.emitPresence({
61
71
  left: [{
62
72
  sessionId
63
73
  }]
64
- }, emit, 'participant leaving');
74
+ }, 'participant leaving');
65
75
  });
66
- _defineProperty(this, "disconnect", (reason, sessionId, emit) => {
76
+ _defineProperty(this, "disconnect", (reason, sessionId) => {
67
77
  const left = this.participantsState.getParticipants();
68
78
  this.participantsState.clear();
69
79
  try {
70
- emit('disconnected', {
80
+ this.emit('disconnected', {
71
81
  reason: disconnectedReasonMapper(reason),
72
82
  sid: sessionId
73
83
  });
@@ -79,7 +89,7 @@ export class ParticipantsService {
79
89
  if (left.length) {
80
90
  this.emitPresence({
81
91
  left
82
- }, emit, 'emitting presence update on disconnect');
92
+ }, 'emitting presence update on disconnect');
83
93
  }
84
94
  });
85
95
  /**
@@ -91,7 +101,7 @@ export class ParticipantsService {
91
101
  * Called when we receive a telepointer update from another
92
102
  * participant.
93
103
  */
94
- _defineProperty(this, "participantTelepointer", (payload, thisSessionId, getUser, emit) => {
104
+ _defineProperty(this, "onParticipantTelepointer", (payload, thisSessionId) => {
95
105
  const {
96
106
  sessionId,
97
107
  selection,
@@ -111,44 +121,43 @@ export class ParticipantsService {
111
121
  type: 'telepointer',
112
122
  selection,
113
123
  sessionId
114
- }, emit, 'handling participant telepointer event');
124
+ }, 'handling participant telepointer event');
115
125
  });
116
126
  /**
117
127
  * Every 5 minutes (PARTICIPANT_UPDATE_INTERVAL), removes inactive participants and emits the update to other participants.
118
128
  * Needs to be kicked off in the Provider.
119
129
  * @param sessionId SessionId from provider's connection
120
- * @param emit Function to execute emit from provider socket
121
130
  */
122
- _defineProperty(this, "removeInactiveParticipants", (sessionId, emit) => {
131
+ _defineProperty(this, "startInactiveRemover", sessionId => {
123
132
  clearTimeout(this.participantUpdateTimeout);
124
133
  try {
125
- this.filterInactive(sessionId, emit);
134
+ this.filterInactive(sessionId);
126
135
  } catch (err) {
127
136
  var _this$analyticsHelper3;
128
137
  (_this$analyticsHelper3 = this.analyticsHelper) === null || _this$analyticsHelper3 === void 0 ? void 0 : _this$analyticsHelper3.sendErrorEvent(err, 'Failed filtering inactive participants');
129
138
  }
130
- this.participantUpdateTimeout = window.setTimeout(() => this.removeInactiveParticipants(sessionId, emit), PARTICIPANT_UPDATE_INTERVAL);
139
+ this.participantUpdateTimeout = window.setTimeout(() => this.startInactiveRemover(sessionId), PARTICIPANT_UPDATE_INTERVAL);
131
140
  });
132
141
  /**
133
142
  * Keep list of participants up to date. Filter out inactive users etc.
134
143
  */
135
- _defineProperty(this, "filterInactive", (sessionId, emit) => {
144
+ _defineProperty(this, "filterInactive", sessionId => {
136
145
  const now = Date.now();
137
146
  const left = this.participantsState.getParticipants().filter(p => p.sessionId !== sessionId && now - p.lastActive > PARTICIPANT_UPDATE_INTERVAL);
138
147
  left.forEach(p => this.participantsState.removeBySessionId(p.sessionId));
139
148
  left.length && this.emitPresence({
140
149
  left
141
- }, emit, 'filtering inactive participants');
150
+ }, 'filtering inactive participants');
142
151
  });
143
152
  /**
144
153
  * Wrapper function to emit with error handling and analytics
145
154
  * @param data Data to emit
146
155
  * @param emit Emit function from Provider
147
156
  */
148
- _defineProperty(this, "emitPresence", (data, emit, errorMessage) => {
157
+ _defineProperty(this, "emitPresence", (data, errorMessage) => {
149
158
  try {
150
159
  var _this$analyticsHelper4;
151
- emit('presence', data);
160
+ this.emit('presence', data);
152
161
  (_this$analyticsHelper4 = this.analyticsHelper) === null || _this$analyticsHelper4 === void 0 ? void 0 : _this$analyticsHelper4.sendActionEvent(EVENT_ACTION.UPDATE_PARTICIPANTS, EVENT_STATUS.SUCCESS, {
153
162
  participants: this.participantsState.size()
154
163
  });
@@ -163,9 +172,9 @@ export class ParticipantsService {
163
172
  * @param data Data to emit
164
173
  * @param emit Emit function from Provider
165
174
  */
166
- _defineProperty(this, "emitTelepointer", (data, emit, errorMessage) => {
175
+ _defineProperty(this, "emitTelepointer", (data, errorMessage) => {
167
176
  try {
168
- emit('telepointer', data);
177
+ this.emit('telepointer', data);
169
178
  } catch (error) {
170
179
  var _this$analyticsHelper6;
171
180
  // We don't want to throw errors for Presence features as they tend to self-restore
@@ -178,19 +187,68 @@ export class ParticipantsService {
178
187
  _defineProperty(this, "clearTimers", () => {
179
188
  clearTimeout(this.participantUpdateTimeout);
180
189
  });
181
- this.participantsState = participantsState;
190
+ _defineProperty(this, "sendPresence", payload => {
191
+ try {
192
+ clearTimeout(this.presenceUpdateTimeout);
193
+ this.channelBroadcast('participant:updated', payload);
194
+ this.presenceUpdateTimeout = window.setTimeout(() => this.sendPresence(payload), SEND_PRESENCE_INTERVAL);
195
+ } catch (error) {
196
+ var _this$analyticsHelper7;
197
+ // We don't want to throw errors for Presence features as they tend to self-restore
198
+ (_this$analyticsHelper7 = this.analyticsHelper) === null || _this$analyticsHelper7 === void 0 ? void 0 : _this$analyticsHelper7.sendErrorEvent(error, 'Error while sending presence');
199
+ }
200
+ });
201
+ /**
202
+ * Called when a participant joins the session.
203
+ *
204
+ * We keep track of participants internally, and emit the `presence` event to update
205
+ * the active avatars in the editor.
206
+ * This method will be triggered from backend to notify all participants to exchange presence
207
+ */
208
+ _defineProperty(this, "onPresenceJoined", payload => {
209
+ try {
210
+ logger('Participant joined with session: ', payload.sessionId);
211
+ // This expose existing users to the newly joined user
212
+ this.sendPresence(payload);
213
+ } catch (error) {
214
+ var _this$analyticsHelper8;
215
+ // We don't want to throw errors for Presence features as they tend to self-restore
216
+ (_this$analyticsHelper8 = this.analyticsHelper) === null || _this$analyticsHelper8 === void 0 ? void 0 : _this$analyticsHelper8.sendErrorEvent(error, 'Error while joining presence');
217
+ }
218
+ });
219
+ /**
220
+ * Called when the current user joins the session.
221
+ *
222
+ * This will send both a 'presence' event and a 'participant:updated' event.
223
+ * This updates both the avatars and the participants list.
224
+ */
225
+ _defineProperty(this, "onPresence", payload => {
226
+ try {
227
+ logger('onPresence userId: ', payload.userId);
228
+ this.sendPresence(payload);
229
+ this.sendPresenceJoined();
230
+ } catch (error) {
231
+ var _this$analyticsHelper9;
232
+ // We don't want to throw errors for Presence features as they tend to self-restore
233
+ (_this$analyticsHelper9 = this.analyticsHelper) === null || _this$analyticsHelper9 === void 0 ? void 0 : _this$analyticsHelper9.sendErrorEvent(error, 'Error while receiving presence');
234
+ }
235
+ });
182
236
  this.analyticsHelper = analyticsHelper;
237
+ this.participantsState = participantsState;
238
+ this.emit = emit;
239
+ this.getUser = getUser;
240
+ this.channelBroadcast = channelBroadcast;
241
+ this.sendPresenceJoined = sendPresenceJoined;
183
242
  }
184
243
  /**
185
244
  * Called on receiving steps, emits each step's telepointer
186
245
  * @param steps Steps to extract telepointers from
187
- * @param emit Provider emit function
188
246
  */
189
- emitTelepointersFromSteps(steps, emit) {
247
+ emitTelepointersFromSteps(steps) {
190
248
  steps.forEach(step => {
191
249
  const event = telepointerFromStep(this.participantsState.getParticipants(), step);
192
250
  if (event) {
193
- this.emitTelepointer(event, emit, 'emitting telepointers from steps');
251
+ this.emitTelepointer(event, 'emitting telepointers from steps');
194
252
  }
195
253
  });
196
254
  }
@@ -4,14 +4,13 @@ import { Channel } from '../channel';
4
4
  import { createLogger } from '../helpers/utils';
5
5
  import AnalyticsHelper from '../analytics/analytics-helper';
6
6
  import { telepointerCallback } from '../participants/telepointers-helper';
7
- import { ParticipantsService } from '../participants/participants-service';
8
7
  import { DestroyError, GetCurrentStateError, GetFinalAcknowledgedStateError, ProviderInitialisationError, SendTransactionError, SetEditorWidthError, SetMetadataError, SetTitleError, NCS_ERROR_CODE } from '../errors/error-types';
9
8
  import { MetadataService } from '../metadata/metadata-service';
10
9
  import { DocumentService } from '../document/document-service';
10
+ import { NamespaceService } from '../namespace/namespace-service';
11
+ import { ParticipantsService } from '../participants/participants-service';
11
12
  import { errorCodeMapper } from '../errors/error-code-mapper';
12
13
  const logger = createLogger('Provider', 'black');
13
- const SEND_PRESENCE_INTERVAL = 150 * 1000; // 150 seconds
14
-
15
14
  const OUT_OF_SYNC_PERIOD = 3 * 1000; // 3 seconds
16
15
 
17
16
  export const MAX_STEP_REJECTED_ERROR = 15;
@@ -20,9 +19,24 @@ export class Provider extends Emitter {
20
19
  super();
21
20
  _defineProperty(this, "isChannelInitialized", false);
22
21
  _defineProperty(this, "isProviderInitialized", false);
23
- // To keep track of the namespace event changes from the server.
24
- _defineProperty(this, "isNamespaceLocked", false);
25
- _defineProperty(this, "emitCallback", (evt, data) => this.emit(evt, data));
22
+ // isPreinitializating acts as a feature flag to determine when the provider has been initialized early
23
+ // and also contains the initial draft
24
+ _defineProperty(this, "isPreinitializing", false);
25
+ /**
26
+ * Wrapper for this.emit, it binds scope for callbacks and waits for initialising of the editor before emitting events.
27
+ * Waiting for the collab provider to become connected to the editor ensures the editor doesn't miss any events.
28
+ * @param evt - Event name to emit to subscribers
29
+ * @param data - Event data to emit to subscribers
30
+ */
31
+ _defineProperty(this, "emitCallback", (evt, data) => {
32
+ // When the provider is initialized early, we want the editor state promise to resolve before emitting events
33
+ // to ensure that it is ready to listen to the events fired by NCS
34
+ if (this.isPreinitializing) {
35
+ this.getStatePromise.then(() => this.emit(evt, data));
36
+ } else {
37
+ this.emit(evt, data);
38
+ }
39
+ });
26
40
  _defineProperty(this, "initializeChannel", () => {
27
41
  this.emit('connecting', {
28
42
  initial: true
@@ -33,7 +47,7 @@ export class Provider extends Emitter {
33
47
  initialized
34
48
  }) => {
35
49
  this.sessionId = sid;
36
- this.emit('connected', {
50
+ this.emitCallback('connected', {
37
51
  sid,
38
52
  initial: !initialized
39
53
  });
@@ -60,7 +74,7 @@ export class Provider extends Emitter {
60
74
  Date.now() - this.disconnectedAt >= OUT_OF_SYNC_PERIOD) {
61
75
  this.documentService.throttledCatchup();
62
76
  }
63
- this.startInactiveRemover();
77
+ this.participantsService.startInactiveRemover(this.sessionId);
64
78
  this.disconnectedAt = undefined;
65
79
  }).on('init', ({
66
80
  doc,
@@ -74,7 +88,8 @@ export class Provider extends Emitter {
74
88
  metadata
75
89
  });
76
90
  this.metadataService.updateMetadata(metadata);
77
- }).on('restore', this.documentService.onRestore).on('steps:added', this.documentService.onStepsAdded).on('metadata:changed', this.metadataService.onMetadataChanged).on('participant:telepointer', this.onParticipantTelepointer.bind(this)).on('presence:joined', this.onPresenceJoined.bind(this)).on('presence', this.onPresence.bind(this)).on('participant:left', this.onParticipantLeft.bind(this)).on('participant:updated', this.onParticipantUpdated.bind(this)).on('disconnect', this.onDisconnected.bind(this)).on('error', this.onErrorHandled).on('status', this.onNamespaceStatusChanged.bind(this)).connect(shouldInitialize);
91
+ this.isProviderInitialized = true;
92
+ }).on('restore', this.documentService.onRestore).on('steps:added', this.documentService.onStepsAdded).on('metadata:changed', this.metadataService.onMetadataChanged).on('participant:telepointer', payload => this.participantsService.onParticipantTelepointer(payload, this.sessionId)).on('presence:joined', this.participantsService.onPresenceJoined).on('presence', this.participantsService.onPresence).on('participant:left', this.participantsService.onParticipantLeft).on('participant:updated', this.participantsService.onParticipantUpdated).on('disconnect', this.onDisconnected.bind(this)).on('error', this.onErrorHandled).on('status', this.namespaceService.onNamespaceStatusChanged).connect(shouldInitialize);
78
93
  });
79
94
  /**
80
95
  * @param {InternalError} error The error to handle
@@ -94,83 +109,16 @@ export class Provider extends Emitter {
94
109
  if (mappedError) {
95
110
  var _this$analyticsHelper2;
96
111
  (_this$analyticsHelper2 = this.analyticsHelper) === null || _this$analyticsHelper2 === void 0 ? void 0 : _this$analyticsHelper2.sendErrorEvent(mappedError, 'Error emitted');
97
- this.emit('error', mappedError);
112
+ this.emitCallback('error', mappedError);
98
113
  }
99
114
  }
100
115
  });
101
- _defineProperty(this, "sendPresence", () => {
102
- try {
103
- if (this.presenceUpdateTimeout) {
104
- clearTimeout(this.presenceUpdateTimeout);
105
- }
106
- this.channel.broadcast('participant:updated', {
107
- sessionId: this.sessionId,
108
- userId: this.userId,
109
- clientId: this.clientId
110
- });
111
- this.presenceUpdateTimeout = window.setTimeout(() => this.sendPresence(), SEND_PRESENCE_INTERVAL);
112
- } catch (error) {
113
- var _this$analyticsHelper3;
114
- // We don't want to throw errors for Presence features as they tend to self-restore
115
- (_this$analyticsHelper3 = this.analyticsHelper) === null || _this$analyticsHelper3 === void 0 ? void 0 : _this$analyticsHelper3.sendErrorEvent(error, 'Error while sending presence');
116
- }
117
- });
118
- /**
119
- * Called when a participant joins the session.
120
- *
121
- * We keep track of participants internally in this class, and emit the `presence` event to update
122
- * the active avatars in the editor.
123
- * This method will be triggered from backend to notify all participants to exchange presence
124
- */
125
- _defineProperty(this, "onPresenceJoined", ({
126
- sessionId
127
- }) => {
128
- try {
129
- logger('Participant joined with session: ', sessionId);
130
- // This expose existing users to the newly joined user
131
- this.sendPresence();
132
- } catch (error) {
133
- var _this$analyticsHelper4;
134
- // We don't want to throw errors for Presence features as they tend to self-restore
135
- (_this$analyticsHelper4 = this.analyticsHelper) === null || _this$analyticsHelper4 === void 0 ? void 0 : _this$analyticsHelper4.sendErrorEvent(error, 'Error while joining presence');
136
- }
137
- });
138
- _defineProperty(this, "onPresence", ({
139
- userId
140
- }) => {
141
- try {
142
- logger('onPresence userId: ', userId);
143
- this.userId = userId;
144
- this.sendPresence();
145
- this.channel.sendPresenceJoined();
146
- } catch (error) {
147
- var _this$analyticsHelper5;
148
- // We don't want to throw errors for Presence features as they tend to self-restore
149
- (_this$analyticsHelper5 = this.analyticsHelper) === null || _this$analyticsHelper5 === void 0 ? void 0 : _this$analyticsHelper5.sendErrorEvent(error, 'Error while receiving presence');
150
- }
151
- });
152
- /**
153
- * Called when a participant leaves the session.
154
- *
155
- * We emit the `presence` event to update the active avatars in the editor.
156
- */
157
- _defineProperty(this, "onParticipantLeft", data => this.participantsService.participantLeft(data, this.emitCallback));
158
- _defineProperty(this, "startInactiveRemover", () => this.participantsService.removeInactiveParticipants(this.sessionId, this.emitCallback));
159
- /**
160
- * Called when we receive an update event from another participant.
161
- */
162
- _defineProperty(this, "onParticipantUpdated", data => this.participantsService.updateParticipant(data, this.config.getUser, this.emitCallback));
163
- /**
164
- * Called when we receive a telepointer update from another
165
- * participant.
166
- */
167
- _defineProperty(this, "onParticipantTelepointer", data => this.participantsService.participantTelepointer(data, this.sessionId, this.config.getUser, this.emitCallback));
168
116
  // Note: this gets triggered on page reload for Firefox (not other browsers) because of closeOnBeforeunload: false
169
117
  _defineProperty(this, "onDisconnected", ({
170
118
  reason
171
119
  }) => {
172
120
  this.disconnectedAt = Date.now();
173
- this.participantsService.disconnect(reason, this.sessionId, this.emitCallback);
121
+ this.participantsService.disconnect(reason, this.sessionId);
174
122
  });
175
123
  /**
176
124
  * Returns the documents metadata
@@ -187,8 +135,8 @@ export class Provider extends Emitter {
187
135
  try {
188
136
  return this.documentService.getCurrentState();
189
137
  } catch (error) {
190
- var _this$analyticsHelper6;
191
- (_this$analyticsHelper6 = this.analyticsHelper) === null || _this$analyticsHelper6 === void 0 ? void 0 : _this$analyticsHelper6.sendErrorEvent(error, 'Error while returning ADF version of current draft document');
138
+ var _this$analyticsHelper3;
139
+ (_this$analyticsHelper3 = this.analyticsHelper) === null || _this$analyticsHelper3 === void 0 ? void 0 : _this$analyticsHelper3.sendErrorEvent(error, 'Error while returning ADF version of current draft document');
192
140
  throw new GetCurrentStateError('Error while returning the current state of the draft document', error); // Reject the promise so the consumer can react to it failing
193
141
  }
194
142
  });
@@ -201,46 +149,14 @@ export class Provider extends Emitter {
201
149
  try {
202
150
  return this.documentService.getFinalAcknowledgedState();
203
151
  } catch (error) {
204
- var _this$analyticsHelper7;
205
- (_this$analyticsHelper7 = this.analyticsHelper) === null || _this$analyticsHelper7 === void 0 ? void 0 : _this$analyticsHelper7.sendErrorEvent(error, 'Error while returning ADF version of the final draft document');
152
+ var _this$analyticsHelper4;
153
+ (_this$analyticsHelper4 = this.analyticsHelper) === null || _this$analyticsHelper4 === void 0 ? void 0 : _this$analyticsHelper4.sendErrorEvent(error, 'Error while returning ADF version of the final draft document');
206
154
  throw new GetFinalAcknowledgedStateError('Error while returning the final acknowledged state of the draft document', error); // Reject the promise so the consumer can react to it failing
207
155
  }
208
156
  });
209
157
  _defineProperty(this, "getUnconfirmedSteps", () => {
210
158
  return this.documentService.getUnconfirmedSteps();
211
159
  });
212
- /**
213
- * ESS-2916 namespace status event- lock/unlock
214
- */
215
- _defineProperty(this, "onNamespaceStatusChanged", async data => {
216
- const {
217
- isLocked,
218
- waitTimeInMs,
219
- timestamp
220
- } = data;
221
- const start = Date.now();
222
- logger(`Received a namespace status changed event `, {
223
- data
224
- });
225
- if (isLocked && waitTimeInMs) {
226
- this.isNamespaceLocked = true;
227
- logger(`Received a namespace status change event `, {
228
- isLocked
229
- });
230
-
231
- // To protect the collab editing process from locked out due to BE
232
- setTimeout(() => {
233
- logger(`The namespace lock has expired`, {
234
- waitTime: Date.now() - start,
235
- timestamp
236
- });
237
- this.isNamespaceLocked = false;
238
- }, waitTimeInMs);
239
- return;
240
- }
241
- this.isNamespaceLocked = false;
242
- logger(`The page lock has expired`);
243
- });
244
160
  /**
245
161
  * Used when the provider is disconnected or destroyed to prevent perpetual timers from continuously running
246
162
  */
@@ -254,9 +170,14 @@ export class Provider extends Emitter {
254
170
  this.isChannelInitialized = false;
255
171
  this.initialDraft = this.config.initialDraft;
256
172
  this.isProviderInitialized = false;
257
- this.participantsService = new ParticipantsService(this.analyticsHelper);
173
+ this.isPreinitializing = false;
174
+ this.participantsService = new ParticipantsService(this.analyticsHelper, undefined, this.emitCallback, this.config.getUser, this.channel.broadcast, this.channel.sendPresenceJoined);
258
175
  this.metadataService = new MetadataService(this.emitCallback, this.channel.sendMetadata);
259
176
  this.documentService = new DocumentService(this.participantsService, this.analyticsHelper, this.channel.fetchCatchup, this.emitCallback, this.channel.broadcast, () => this.userId, this.onErrorHandled, this.metadataService);
177
+ this.getStatePromise = new Promise(resolve => {
178
+ this.getStatePromiseResolve = resolve;
179
+ });
180
+ this.namespaceService = new NamespaceService();
260
181
  }
261
182
  /**
262
183
  * Initialisation logic, called by Jira with a dummy getState function, deprecated in favour of the setup method which allows more configuration
@@ -271,10 +192,15 @@ export class Provider extends Emitter {
271
192
  }
272
193
 
273
194
  /**
274
- * Initialisation logic, called by the editor in the collab-edit plugin
275
- * @param {Object} parameters ...
276
- * @param {Function} parameters.getState Function that returns the editor state, used to retrieve collab-edit properties and to interact with prosemirror-collab
277
- * @param {SyncUpErrorFunction} parameters.onSyncUpError (Optional) Function that gets called when the sync of steps fails after retrying 30 times, used by Editor to log to analytics
195
+ * Initialisation logic, called by the editor in the collab-edit plugin.
196
+ *
197
+ * When getState is nullish and a initialDraft is provided the collab provider is in a state of pre-initialization,
198
+ * the provider starts to enable the connection to NCS, but the provider will not emit any events until this function
199
+ * is called again with a getState function, indicating that the editor is loaded and ready to receive the emits.
200
+ *
201
+ * @param {Object} options ...
202
+ * @param {Function} options.getState Function that returns the editor state, used to retrieve collab-edit properties and to interact with prosemirror-collab
203
+ * @param {SyncUpErrorFunction} options.onSyncUpError (Optional) Function that gets called when the sync of steps fails after retrying 30 times, used by Editor to log to analytics
278
204
  * @throws {ProviderInitialisationError} Something went wrong during provider initialisation
279
205
  */
280
206
  setup({
@@ -283,23 +209,34 @@ export class Provider extends Emitter {
283
209
  }) {
284
210
  this.checkForCookies();
285
211
  try {
286
- const collabPlugin = getState().plugins.find(p => p.key === 'collab$');
287
- if (collabPlugin === undefined) {
288
- throw new ProviderInitialisationError('Collab provider attempted to initialise, but Editor state is missing collab plugin');
212
+ // if setup is called with no state and the initial draft is already provided
213
+ // set a flag to mark early provider setup
214
+ if (!getState && this.initialDraft) {
215
+ this.isPreinitializing = true;
216
+ }
217
+ if (getState) {
218
+ // if provider has already been initialized earlier, resolve the state once it is available
219
+ if (this.isPreinitializing) {
220
+ this.getStatePromiseResolve();
221
+ }
222
+ const collabPlugin = getState().plugins.find(p => p.key === 'collab$');
223
+ if (collabPlugin === undefined) {
224
+ throw new ProviderInitialisationError('Collab provider attempted to initialise, but Editor state is missing collab plugin');
225
+ }
226
+ this.clientId = collabPlugin.spec.config.clientID;
227
+ this.documentService.setup({
228
+ getState,
229
+ onSyncUpError,
230
+ clientId: this.clientId
231
+ });
289
232
  }
290
- this.clientId = collabPlugin.spec.config.clientID;
291
- this.documentService.setup({
292
- getState,
293
- onSyncUpError,
294
- clientId: this.clientId
295
- });
296
233
  if (!this.isChannelInitialized) {
297
234
  this.initializeChannel();
298
235
  this.isChannelInitialized = true;
299
236
  }
300
237
  } catch (initError) {
301
- var _this$analyticsHelper8;
302
- (_this$analyticsHelper8 = this.analyticsHelper) === null || _this$analyticsHelper8 === void 0 ? void 0 : _this$analyticsHelper8.sendErrorEvent(initError, 'Error while initialising the provider');
238
+ var _this$analyticsHelper5;
239
+ (_this$analyticsHelper5 = this.analyticsHelper) === null || _this$analyticsHelper5 === void 0 ? void 0 : _this$analyticsHelper5.sendErrorEvent(initError, 'Error while initialising the provider');
303
240
  // Throw error so consumers are aware the initialisation failed when initialising themselves
304
241
  throw new ProviderInitialisationError('Provider initialisation error', initError);
305
242
  }
@@ -307,9 +244,9 @@ export class Provider extends Emitter {
307
244
  }
308
245
  checkForCookies() {
309
246
  if (!global.navigator.cookieEnabled) {
310
- var _this$analyticsHelper9;
247
+ var _this$analyticsHelper6;
311
248
  const initError = new ProviderInitialisationError('Cookies are not enabled. Please enable cookies to use collaborative editing.');
312
- (_this$analyticsHelper9 = this.analyticsHelper) === null || _this$analyticsHelper9 === void 0 ? void 0 : _this$analyticsHelper9.sendErrorEvent(initError, 'Error while initialising the provider - cookies disabled');
249
+ (_this$analyticsHelper6 = this.analyticsHelper) === null || _this$analyticsHelper6 === void 0 ? void 0 : _this$analyticsHelper6.sendErrorEvent(initError, 'Error while initialising the provider - cookies disabled');
313
250
  throw new ProviderInitialisationError('Provider initialisation error - cookies disabled', initError);
314
251
  }
315
252
  }
@@ -324,14 +261,14 @@ export class Provider extends Emitter {
324
261
  send(_tr, _oldState, newState) {
325
262
  try {
326
263
  // Don't send steps while the document is locked (eg. when restoring the document)
327
- if (this.isNamespaceLocked) {
264
+ if (this.namespaceService.getIsNamespaceLocked()) {
328
265
  logger('The document is temporary locked');
329
266
  return;
330
267
  }
331
268
  this.documentService.send(_tr, _oldState, newState);
332
269
  } catch (error) {
333
- var _this$analyticsHelper10;
334
- (_this$analyticsHelper10 = this.analyticsHelper) === null || _this$analyticsHelper10 === void 0 ? void 0 : _this$analyticsHelper10.sendErrorEvent(error, 'Error while sending steps for a transaction');
270
+ var _this$analyticsHelper7;
271
+ (_this$analyticsHelper7 = this.analyticsHelper) === null || _this$analyticsHelper7 === void 0 ? void 0 : _this$analyticsHelper7.sendErrorEvent(error, 'Error while sending steps for a transaction');
335
272
  throw new SendTransactionError('Error while sending steps for a transaction', error);
336
273
  }
337
274
  }
@@ -356,9 +293,9 @@ export class Provider extends Emitter {
356
293
  this.channel.broadcast('participant:telepointer', payload, callback);
357
294
  }
358
295
  } catch (error) {
359
- var _this$analyticsHelper11;
296
+ var _this$analyticsHelper8;
360
297
  // We don't want to throw errors for Presence features as they tend to self-restore
361
- (_this$analyticsHelper11 = this.analyticsHelper) === null || _this$analyticsHelper11 === void 0 ? void 0 : _this$analyticsHelper11.sendErrorEvent(error, 'Error while sending message - telepointer');
298
+ (_this$analyticsHelper8 = this.analyticsHelper) === null || _this$analyticsHelper8 === void 0 ? void 0 : _this$analyticsHelper8.sendErrorEvent(error, 'Error while sending message - telepointer');
362
299
  }
363
300
  }
364
301
  /**
@@ -390,8 +327,8 @@ export class Provider extends Emitter {
390
327
  super.unsubscribeAll();
391
328
  this.channel.disconnect();
392
329
  } catch (error) {
393
- var _this$analyticsHelper12;
394
- (_this$analyticsHelper12 = this.analyticsHelper) === null || _this$analyticsHelper12 === void 0 ? void 0 : _this$analyticsHelper12.sendErrorEvent(error, 'Error while shutting down the collab provider');
330
+ var _this$analyticsHelper9;
331
+ (_this$analyticsHelper9 = this.analyticsHelper) === null || _this$analyticsHelper9 === void 0 ? void 0 : _this$analyticsHelper9.sendErrorEvent(error, 'Error while shutting down the collab provider');
395
332
  throw new DestroyError('Error while shutting down the collab provider', error);
396
333
  }
397
334
  this.clearTimers();
@@ -409,8 +346,8 @@ export class Provider extends Emitter {
409
346
  try {
410
347
  this.metadataService.setTitle(title, broadcast);
411
348
  } catch (error) {
412
- var _this$analyticsHelper13;
413
- (_this$analyticsHelper13 = this.analyticsHelper) === null || _this$analyticsHelper13 === void 0 ? void 0 : _this$analyticsHelper13.sendErrorEvent(error, 'Error while setting title');
349
+ var _this$analyticsHelper10;
350
+ (_this$analyticsHelper10 = this.analyticsHelper) === null || _this$analyticsHelper10 === void 0 ? void 0 : _this$analyticsHelper10.sendErrorEvent(error, 'Error while setting title');
414
351
  throw new SetTitleError('Error while setting title', error);
415
352
  }
416
353
  }
@@ -426,8 +363,8 @@ export class Provider extends Emitter {
426
363
  try {
427
364
  this.metadataService.setEditorWidth(editorWidth, broadcast);
428
365
  } catch (error) {
429
- var _this$analyticsHelper14;
430
- (_this$analyticsHelper14 = this.analyticsHelper) === null || _this$analyticsHelper14 === void 0 ? void 0 : _this$analyticsHelper14.sendErrorEvent(error, 'Error while setting editor width');
366
+ var _this$analyticsHelper11;
367
+ (_this$analyticsHelper11 = this.analyticsHelper) === null || _this$analyticsHelper11 === void 0 ? void 0 : _this$analyticsHelper11.sendErrorEvent(error, 'Error while setting editor width');
431
368
  throw new SetEditorWidthError('Error while setting editor width', error);
432
369
  }
433
370
  }
@@ -441,8 +378,8 @@ export class Provider extends Emitter {
441
378
  try {
442
379
  this.metadataService.setMetadata(metadata);
443
380
  } catch (error) {
444
- var _this$analyticsHelper15;
445
- (_this$analyticsHelper15 = this.analyticsHelper) === null || _this$analyticsHelper15 === void 0 ? void 0 : _this$analyticsHelper15.sendErrorEvent(error, 'Error while setting metadata');
381
+ var _this$analyticsHelper12;
382
+ (_this$analyticsHelper12 = this.analyticsHelper) === null || _this$analyticsHelper12 === void 0 ? void 0 : _this$analyticsHelper12.sendErrorEvent(error, 'Error while setting metadata');
446
383
  throw new SetMetadataError('Error while setting metadata', error);
447
384
  }
448
385
  }
@@ -1,5 +1,5 @@
1
1
  export const name = "@atlaskit/collab-provider";
2
- export const version = "8.8.2";
2
+ export const version = "8.9.0";
3
3
  export const nextMajorVersion = () => {
4
4
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
5
5
  };
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/collab-provider",
3
- "version": "8.8.2",
3
+ "version": "8.9.0",
4
4
  "sideEffects": false
5
5
  }