@webex/plugin-meetings 3.12.0-next.9 → 3.12.0-task-refactor.1

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 (201) hide show
  1. package/dist/annotation/index.js +5 -14
  2. package/dist/annotation/index.js.map +1 -1
  3. package/dist/breakouts/breakout.js +1 -1
  4. package/dist/breakouts/index.js +1 -1
  5. package/dist/config.js +2 -8
  6. package/dist/config.js.map +1 -1
  7. package/dist/constants.js +6 -29
  8. package/dist/constants.js.map +1 -1
  9. package/dist/hashTree/hashTreeParser.js +29 -1563
  10. package/dist/hashTree/hashTreeParser.js.map +1 -1
  11. package/dist/hashTree/types.js +3 -13
  12. package/dist/hashTree/types.js.map +1 -1
  13. package/dist/index.js +2 -11
  14. package/dist/index.js.map +1 -1
  15. package/dist/interceptors/index.js +0 -7
  16. package/dist/interceptors/index.js.map +1 -1
  17. package/dist/interceptors/locusRouteToken.js +5 -27
  18. package/dist/interceptors/locusRouteToken.js.map +1 -1
  19. package/dist/interpretation/index.js +2 -2
  20. package/dist/interpretation/index.js.map +1 -1
  21. package/dist/interpretation/siLanguage.js +1 -1
  22. package/dist/locus-info/controlsUtils.js +3 -7
  23. package/dist/locus-info/controlsUtils.js.map +1 -1
  24. package/dist/locus-info/index.js +247 -642
  25. package/dist/locus-info/index.js.map +1 -1
  26. package/dist/locus-info/selfUtils.js +0 -1
  27. package/dist/locus-info/selfUtils.js.map +1 -1
  28. package/dist/locus-info/types.js.map +1 -1
  29. package/dist/media/MediaConnectionAwaiter.js +1 -57
  30. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  31. package/dist/media/properties.js +2 -4
  32. package/dist/media/properties.js.map +1 -1
  33. package/dist/meeting/in-meeting-actions.js +1 -7
  34. package/dist/meeting/in-meeting-actions.js.map +1 -1
  35. package/dist/meeting/index.js +1036 -1481
  36. package/dist/meeting/index.js.map +1 -1
  37. package/dist/meeting/request.js +0 -50
  38. package/dist/meeting/request.js.map +1 -1
  39. package/dist/meeting/request.type.js.map +1 -1
  40. package/dist/meeting/util.js +3 -133
  41. package/dist/meeting/util.js.map +1 -1
  42. package/dist/meetings/index.js +59 -142
  43. package/dist/meetings/index.js.map +1 -1
  44. package/dist/meetings/util.js +7 -11
  45. package/dist/meetings/util.js.map +1 -1
  46. package/dist/member/index.js +0 -10
  47. package/dist/member/index.js.map +1 -1
  48. package/dist/member/util.js +0 -10
  49. package/dist/member/util.js.map +1 -1
  50. package/dist/metrics/constants.js +1 -7
  51. package/dist/metrics/constants.js.map +1 -1
  52. package/dist/multistream/mediaRequestManager.js +60 -9
  53. package/dist/multistream/mediaRequestManager.js.map +1 -1
  54. package/dist/multistream/remoteMediaManager.js +0 -11
  55. package/dist/multistream/remoteMediaManager.js.map +1 -1
  56. package/dist/multistream/sendSlotManager.js +2 -116
  57. package/dist/multistream/sendSlotManager.js.map +1 -1
  58. package/dist/reachability/clusterReachability.js +18 -171
  59. package/dist/reachability/clusterReachability.js.map +1 -1
  60. package/dist/reachability/index.js +11 -21
  61. package/dist/reachability/index.js.map +1 -1
  62. package/dist/reachability/reachabilityPeerConnection.js +1 -1
  63. package/dist/reachability/reachabilityPeerConnection.js.map +1 -1
  64. package/dist/reactions/reactions.type.js.map +1 -1
  65. package/dist/reconnection-manager/index.js +1 -0
  66. package/dist/reconnection-manager/index.js.map +1 -1
  67. package/dist/types/common/browser-detection.d.ts +0 -1
  68. package/dist/types/common/events/events-scope.d.ts +0 -1
  69. package/dist/types/common/events/events.d.ts +0 -1
  70. package/dist/types/config.d.ts +0 -5
  71. package/dist/types/constants.d.ts +1 -24
  72. package/dist/types/hashTree/hashTreeParser.d.ts +11 -260
  73. package/dist/types/hashTree/types.d.ts +0 -20
  74. package/dist/types/index.d.ts +0 -1
  75. package/dist/types/interceptors/index.d.ts +1 -2
  76. package/dist/types/interceptors/locusRouteToken.d.ts +0 -2
  77. package/dist/types/locus-info/index.d.ts +47 -68
  78. package/dist/types/locus-info/types.d.ts +12 -28
  79. package/dist/types/media/MediaConnectionAwaiter.d.ts +1 -10
  80. package/dist/types/media/properties.d.ts +1 -2
  81. package/dist/types/meeting/in-meeting-actions.d.ts +0 -6
  82. package/dist/types/meeting/index.d.ts +7 -86
  83. package/dist/types/meeting/request.d.ts +1 -16
  84. package/dist/types/meeting/request.type.d.ts +0 -5
  85. package/dist/types/meeting/util.d.ts +0 -31
  86. package/dist/types/meeting-info/util.d.ts +0 -1
  87. package/dist/types/meeting-info/utilv2.d.ts +0 -1
  88. package/dist/types/meetings/index.d.ts +2 -4
  89. package/dist/types/member/index.d.ts +0 -1
  90. package/dist/types/member/types.d.ts +4 -4
  91. package/dist/types/member/util.d.ts +0 -5
  92. package/dist/types/metrics/constants.d.ts +0 -6
  93. package/dist/types/multistream/mediaRequestManager.d.ts +23 -0
  94. package/dist/types/multistream/sendSlotManager.d.ts +1 -23
  95. package/dist/types/reachability/clusterReachability.d.ts +3 -30
  96. package/dist/types/reactions/reactions.type.d.ts +0 -1
  97. package/dist/types/recording-controller/util.d.ts +5 -5
  98. package/dist/types/roap/index.d.ts +1 -1
  99. package/dist/webinar/index.js +163 -438
  100. package/dist/webinar/index.js.map +1 -1
  101. package/package.json +24 -26
  102. package/src/annotation/index.ts +7 -27
  103. package/src/config.ts +0 -5
  104. package/src/constants.ts +1 -30
  105. package/src/hashTree/hashTreeParser.ts +25 -1523
  106. package/src/hashTree/types.ts +1 -24
  107. package/src/index.ts +1 -8
  108. package/src/interceptors/index.ts +1 -2
  109. package/src/interceptors/locusRouteToken.ts +5 -22
  110. package/src/interpretation/index.ts +2 -2
  111. package/src/locus-info/controlsUtils.ts +0 -17
  112. package/src/locus-info/index.ts +213 -707
  113. package/src/locus-info/selfUtils.ts +0 -1
  114. package/src/locus-info/types.ts +12 -27
  115. package/src/media/MediaConnectionAwaiter.ts +1 -41
  116. package/src/media/properties.ts +1 -3
  117. package/src/meeting/in-meeting-actions.ts +0 -12
  118. package/src/meeting/index.ts +84 -461
  119. package/src/meeting/request.ts +0 -42
  120. package/src/meeting/request.type.ts +0 -6
  121. package/src/meeting/util.ts +2 -160
  122. package/src/meetings/index.ts +60 -180
  123. package/src/meetings/util.ts +9 -10
  124. package/src/member/index.ts +0 -10
  125. package/src/member/util.ts +0 -12
  126. package/src/metrics/constants.ts +0 -7
  127. package/src/multistream/mediaRequestManager.ts +54 -4
  128. package/src/multistream/remoteMediaManager.ts +0 -13
  129. package/src/multistream/sendSlotManager.ts +3 -97
  130. package/src/reachability/clusterReachability.ts +27 -153
  131. package/src/reachability/index.ts +1 -15
  132. package/src/reachability/reachabilityPeerConnection.ts +1 -3
  133. package/src/reactions/reactions.type.ts +0 -1
  134. package/src/reconnection-manager/index.ts +1 -0
  135. package/src/webinar/index.ts +6 -265
  136. package/test/unit/spec/annotation/index.ts +7 -69
  137. package/test/unit/spec/interceptors/locusRouteToken.ts +0 -44
  138. package/test/unit/spec/locus-info/controlsUtils.js +1 -56
  139. package/test/unit/spec/locus-info/index.js +90 -1457
  140. package/test/unit/spec/media/MediaConnectionAwaiter.ts +1 -41
  141. package/test/unit/spec/media/properties.ts +3 -12
  142. package/test/unit/spec/meeting/in-meeting-actions.ts +2 -8
  143. package/test/unit/spec/meeting/index.js +128 -981
  144. package/test/unit/spec/meeting/request.js +0 -70
  145. package/test/unit/spec/meeting/utils.js +26 -438
  146. package/test/unit/spec/meetings/index.js +33 -845
  147. package/test/unit/spec/meetings/utils.js +1 -51
  148. package/test/unit/spec/member/index.js +4 -28
  149. package/test/unit/spec/member/util.js +27 -65
  150. package/test/unit/spec/multistream/mediaRequestManager.ts +85 -2
  151. package/test/unit/spec/multistream/remoteMediaManager.ts +0 -30
  152. package/test/unit/spec/multistream/sendSlotManager.ts +36 -135
  153. package/test/unit/spec/reachability/clusterReachability.ts +1 -125
  154. package/test/unit/spec/reachability/index.ts +3 -26
  155. package/test/unit/spec/reconnection-manager/index.js +8 -4
  156. package/test/unit/spec/webinar/index.ts +37 -534
  157. package/dist/aiEnableRequest/index.js +0 -184
  158. package/dist/aiEnableRequest/index.js.map +0 -1
  159. package/dist/aiEnableRequest/utils.js +0 -36
  160. package/dist/aiEnableRequest/utils.js.map +0 -1
  161. package/dist/hashTree/constants.js +0 -22
  162. package/dist/hashTree/constants.js.map +0 -1
  163. package/dist/hashTree/hashTree.js +0 -533
  164. package/dist/hashTree/hashTree.js.map +0 -1
  165. package/dist/hashTree/utils.js +0 -69
  166. package/dist/hashTree/utils.js.map +0 -1
  167. package/dist/interceptors/constant.js +0 -12
  168. package/dist/interceptors/constant.js.map +0 -1
  169. package/dist/interceptors/dataChannelAuthToken.js +0 -290
  170. package/dist/interceptors/dataChannelAuthToken.js.map +0 -1
  171. package/dist/interceptors/utils.js +0 -27
  172. package/dist/interceptors/utils.js.map +0 -1
  173. package/dist/types/aiEnableRequest/index.d.ts +0 -5
  174. package/dist/types/aiEnableRequest/utils.d.ts +0 -2
  175. package/dist/types/hashTree/constants.d.ts +0 -9
  176. package/dist/types/hashTree/hashTree.d.ts +0 -136
  177. package/dist/types/hashTree/utils.d.ts +0 -22
  178. package/dist/types/interceptors/constant.d.ts +0 -5
  179. package/dist/types/interceptors/dataChannelAuthToken.d.ts +0 -43
  180. package/dist/types/interceptors/utils.d.ts +0 -1
  181. package/dist/types/webinar/utils.d.ts +0 -6
  182. package/dist/webinar/utils.js +0 -25
  183. package/dist/webinar/utils.js.map +0 -1
  184. package/src/aiEnableRequest/README.md +0 -84
  185. package/src/aiEnableRequest/index.ts +0 -170
  186. package/src/aiEnableRequest/utils.ts +0 -25
  187. package/src/hashTree/constants.ts +0 -10
  188. package/src/hashTree/hashTree.ts +0 -480
  189. package/src/hashTree/utils.ts +0 -62
  190. package/src/interceptors/constant.ts +0 -6
  191. package/src/interceptors/dataChannelAuthToken.ts +0 -170
  192. package/src/interceptors/utils.ts +0 -16
  193. package/src/webinar/utils.ts +0 -16
  194. package/test/unit/spec/aiEnableRequest/index.ts +0 -981
  195. package/test/unit/spec/aiEnableRequest/utils.ts +0 -130
  196. package/test/unit/spec/hashTree/hashTree.ts +0 -721
  197. package/test/unit/spec/hashTree/hashTreeParser.ts +0 -3670
  198. package/test/unit/spec/hashTree/utils.ts +0 -140
  199. package/test/unit/spec/interceptors/dataChannelAuthToken.ts +0 -210
  200. package/test/unit/spec/interceptors/utils.ts +0 -75
  201. package/test/unit/spec/webinar/utils.ts +0 -39
@@ -18,7 +18,6 @@ import {
18
18
  CALL_REMOVED_REASON,
19
19
  RECORDING_STATE,
20
20
  Enum,
21
- SELF_ROLES,
22
21
  } from '../constants';
23
22
 
24
23
  import InfoUtils from './infoUtils';
@@ -34,15 +33,12 @@ import BEHAVIORAL_METRICS from '../metrics/constants';
34
33
  import HashTreeParser, {
35
34
  DataSet,
36
35
  HashTreeMessage,
36
+ HashTreeObject,
37
+ isSelf,
37
38
  LocusInfoUpdateType,
38
- Metadata,
39
39
  } from '../hashTree/hashTreeParser';
40
- import {HashTreeObject, ObjectType, ObjectTypeToLocusKeyMap} from '../hashTree/types';
41
- import {isMetadata, isSelf} from '../hashTree/utils';
42
- import {Links, LocusDTO, ReplacesInfo} from './types';
43
- import MeetingsUtil from '../meetings/util';
44
- import {MEETING_KEY} from '../meetings/meetings.types';
45
- import MeetingCollection from '../meetings/collection';
40
+ import {ObjectType} from '../hashTree/types';
41
+ import {LocusDTO} from './types';
46
42
 
47
43
  export type LocusLLMEvent = {
48
44
  data: {
@@ -51,12 +47,9 @@ export type LocusLLMEvent = {
51
47
  };
52
48
  };
53
49
 
54
- // list of top level keys in Locus DTO relevant for Hash Tree DTOs processing
55
- // it does not contain fields specific to classic Locus DTOs like sequence or baseSequence
56
50
  const LocusDtoTopLevelKeys = [
57
51
  'controls',
58
52
  'fullState',
59
- 'embeddedApps',
60
53
  'host',
61
54
  'info',
62
55
  'links',
@@ -71,13 +64,10 @@ const LocusDtoTopLevelKeys = [
71
64
  'htMeta', // only exists when hash trees are used
72
65
  ];
73
66
 
74
- export type LocusApiResponseBody =
75
- | {
76
- dataSets?: DataSet[];
77
- locus: LocusDTO; // this LocusDTO here might not be the full one (for example it won't have all the participants, but it should have self)
78
- metadata?: Metadata;
79
- }
80
- | LocusDTO; // when we invoke APIs on the whole Locus like "mute all" backend returns the whole Locus in the response like this
67
+ export type LocusApiResponseBody = {
68
+ dataSets?: DataSet[];
69
+ locus: LocusDTO; // this LocusDTO here might not be the full one (for example it won't have all the participants, but it should have self)
70
+ };
81
71
 
82
72
  const LocusObjectStateAfterUpdates = {
83
73
  unchanged: 'unchanged',
@@ -87,167 +77,6 @@ const LocusObjectStateAfterUpdates = {
87
77
 
88
78
  type LocusObjectStateAfterUpdates = Enum<typeof LocusObjectStateAfterUpdates>;
89
79
 
90
- export type HashTreeParserEntry = {
91
- parser: HashTreeParser;
92
- replacedAt?: string;
93
- initializedFromHashTree: boolean;
94
- };
95
-
96
- /**
97
- * Gets the replacement information
98
- *
99
- * @param {any} self - "self" object from Locus DTO
100
- * @param {string} deviceUrl - The URL of the user's device
101
- * @returns {any} The replace information if available, otherwise undefined
102
- */
103
- function getReplaceInfoFromSelf(self: any, deviceUrl: string): ReplacesInfo | undefined {
104
- if (self) {
105
- const device = MeetingsUtil.getThisDevice({self}, deviceUrl);
106
-
107
- if (device?.replaces?.length > 0) {
108
- return device.replaces[0];
109
- }
110
- }
111
-
112
- return undefined;
113
- }
114
-
115
- /**
116
- * Finds a meeting by its locus URL in meeting collection. It checks all HashTreeParsers of all meetings in the collection.
117
- *
118
- * @param {MeetingCollection} meetingCollection - The collection of meetings to search
119
- * @param {string} locusUrl - The locus URL to search for
120
- * @returns {any} The meeting if found, otherwise undefined
121
- */
122
- function findLocusUrlInAnyHashTreeParser(
123
- meetingCollection: MeetingCollection,
124
- locusUrl: string
125
- ): any {
126
- for (const meeting of Object.values(meetingCollection.getAll()) as any[]) {
127
- if (meeting?.locusInfo?.hashTreeParsers?.has(locusUrl)) {
128
- return meeting;
129
- }
130
- }
131
-
132
- return undefined;
133
- }
134
-
135
- /**
136
- * Finds a meeting for a given hash tree message.
137
- *
138
- * @param {HashTreeMessage} message - The hash tree message to find the meeting for
139
- * @param {MeetingCollection} meetingCollection - The collection of meetings to search
140
- * @param {string} deviceUrl - The URL of the user's device
141
- * @returns {any} The meeting if found, otherwise undefined
142
- */
143
- export function findMeetingForHashTreeMessage(
144
- message: HashTreeMessage,
145
- meetingCollection: MeetingCollection,
146
- deviceUrl: string
147
- ): any {
148
- let foundMeeting = findLocusUrlInAnyHashTreeParser(meetingCollection, message.locusUrl);
149
-
150
- if (foundMeeting) {
151
- return foundMeeting;
152
- }
153
-
154
- // if we haven't found anything, it may mean that message has a new locusUrl
155
- // check if it indicates that it replaces some existing current locusUrl (this is indicated in "self")
156
- const self = message.locusStateElements?.find((el) => isSelf(el))?.data;
157
- const replaces = getReplaceInfoFromSelf(self, deviceUrl);
158
-
159
- if (replaces?.locusUrl) {
160
- foundMeeting = findLocusUrlInAnyHashTreeParser(meetingCollection, replaces.locusUrl);
161
-
162
- return foundMeeting;
163
- }
164
-
165
- return undefined;
166
- }
167
-
168
- /**
169
- * Creates a locus object from the objects received in a hash tree message. It usually will be
170
- * incomplete, because hash tree messages only contain the parts of locus that have changed,
171
- * and some updates come separately over Mercury or LLM in separate messages.
172
- *
173
- * @param {HashTreeMessage} message hash tree message to created the locus from
174
- * @returns {Object} the created locus object and metadata if present
175
- */
176
- export function createLocusFromHashTreeMessage(message: HashTreeMessage): {
177
- locus: LocusDTO;
178
- metadata?: Metadata;
179
- } {
180
- const locus: LocusDTO = {
181
- participants: [],
182
- url: message.locusUrl,
183
- };
184
- let metadata: Metadata | undefined;
185
-
186
- if (!message.locusStateElements) {
187
- return {locus, metadata};
188
- }
189
-
190
- for (const element of message.locusStateElements) {
191
- if (!element.data) {
192
- // eslint-disable-next-line no-continue
193
- continue;
194
- }
195
-
196
- const type = element.htMeta.elementId.type.toLowerCase();
197
-
198
- switch (type) {
199
- case ObjectType.locus: {
200
- // spread locus object data onto the top level, but remove keys managed by other ObjectTypes
201
- const locusObjectData = {...element.data};
202
-
203
- Object.values(ObjectTypeToLocusKeyMap).forEach((locusDtoKey) => {
204
- delete locusObjectData[locusDtoKey];
205
- });
206
-
207
- Object.assign(locus, locusObjectData);
208
- break;
209
- }
210
- case ObjectType.participant:
211
- locus.participants.push(element.data);
212
- break;
213
- case ObjectType.mediaShare:
214
- if (!locus.mediaShares) {
215
- locus.mediaShares = [];
216
- }
217
- locus.mediaShares.push(element.data);
218
- break;
219
- case ObjectType.embeddedApp:
220
- if (!locus.embeddedApps) {
221
- locus.embeddedApps = [];
222
- }
223
- locus.embeddedApps.push(element.data);
224
- break;
225
- case ObjectType.control:
226
- if (!locus.controls) {
227
- locus.controls = {};
228
- }
229
- Object.assign(locus.controls, element.data);
230
- break;
231
- case ObjectType.links:
232
- case ObjectType.info:
233
- case ObjectType.fullState:
234
- case ObjectType.self: {
235
- const locusDtoKey = ObjectTypeToLocusKeyMap[type];
236
- locus[locusDtoKey] = element.data;
237
- break;
238
- }
239
- case ObjectType.metadata:
240
- // metadata is not part of Locus DTO
241
- metadata = {...element.data, htMeta: element.htMeta} as Metadata;
242
- break;
243
- default:
244
- break;
245
- }
246
- }
247
-
248
- return {locus, metadata};
249
- }
250
-
251
80
  /**
252
81
  * @description LocusInfo extends ChildEmitter to convert locusInfo info a private emitter to parent object
253
82
  * @export
@@ -265,7 +94,10 @@ export default class LocusInfo extends EventsScope {
265
94
  aclUrl: any;
266
95
  baseSequence: any;
267
96
  created: any;
97
+ identities: any;
98
+ membership: any;
268
99
  participants: any;
100
+ participantsUrl: any;
269
101
  replaces: any;
270
102
  scheduledMeeting: any;
271
103
  sequence: any;
@@ -277,11 +109,13 @@ export default class LocusInfo extends EventsScope {
277
109
  info: any;
278
110
  roles: any;
279
111
  mediaShares: any;
112
+ replace: any;
280
113
  url: any;
281
- links?: Links;
114
+ services: any;
115
+ resources: any;
282
116
  mainSessionLocusCache: any;
283
117
  self: any;
284
- hashTreeParsers: Map<string, HashTreeParserEntry>;
118
+ hashTreeParser?: HashTreeParser;
285
119
  hashTreeObjectId2ParticipantId: Map<number, string>; // mapping of hash tree object ids to participant ids
286
120
  classicVsHashTreeMismatchMetricCounter = 0;
287
121
 
@@ -303,7 +137,6 @@ export default class LocusInfo extends EventsScope {
303
137
  this.meetingId = meetingId;
304
138
  this.updateMeeting = updateMeeting;
305
139
  this.locusParser = new LocusDeltaParser();
306
- this.hashTreeParsers = new Map();
307
140
  this.hashTreeObjectId2ParticipantId = new Map();
308
141
  }
309
142
 
@@ -409,7 +242,7 @@ export default class LocusInfo extends EventsScope {
409
242
  'Locus-info:index#doLocusSync --> got full DTO when we asked for delta'
410
243
  );
411
244
  }
412
- meeting.locusInfo.onFullLocus('classic Locus sync', res.body);
245
+ meeting.locusInfo.onFullLocus(res.body);
413
246
  })
414
247
  .catch((e) => {
415
248
  LoggerProxy.logger.info(
@@ -493,10 +326,13 @@ export default class LocusInfo extends EventsScope {
493
326
  init(locus: any = {}) {
494
327
  this.created = locus.created || null;
495
328
  this.scheduledMeeting = locus.meeting || null;
329
+ this.participantsUrl = locus.participantsUrl || null;
496
330
  this.replaces = locus.replaces || null;
497
331
  this.aclUrl = locus.aclUrl || null;
498
332
  this.baseSequence = locus.baseSequence || null;
499
333
  this.sequence = locus.sequence || null;
334
+ this.membership = locus.membership || null;
335
+ this.identities = locus.identities || null;
500
336
  this.participants = locus.participants || null;
501
337
 
502
338
  /**
@@ -522,60 +358,29 @@ export default class LocusInfo extends EventsScope {
522
358
  this.updateSelf(locus.self);
523
359
  this.updateHostInfo(locus.host);
524
360
  this.updateMediaShares(locus.mediaShares);
525
- this.updateLinks(locus.links);
361
+ this.updateServices(locus.links?.services);
362
+ this.updateResources(locus.links?.resources);
526
363
  }
527
364
 
528
365
  /**
529
- * Creates a HashTreeParser instance for a given locusUrl and stores it in the map.
530
- * @param {Object} params
531
- * @param {string} params.locusUrl - the locus URL used as the map key
532
- * @param {Object} params.initialLocus - initial locus data
533
- * @param {Object} params.metadata - hash tree metadata
534
- * @param {string} params.replacedAt - timestamp from Locus indicating when the replacement happened
535
- * @returns {HashTreeParser} the newly created parser
366
+ * Creates the HashTreeParser instance.
367
+ * @param {Object} initial locus data
368
+ * @returns {void}
536
369
  */
537
370
  private createHashTreeParser({
538
- locusUrl,
539
371
  initialLocus,
540
- metadata,
541
- replacedAt,
542
372
  }: {
543
- locusUrl: string;
544
373
  initialLocus: {
545
374
  dataSets: Array<DataSet>;
546
375
  locus: any;
547
376
  };
548
- metadata: Metadata;
549
- replacedAt?: string;
550
- }): HashTreeParser {
551
- const parser = new HashTreeParser({
377
+ }) {
378
+ return new HashTreeParser({
552
379
  initialLocus,
553
- metadata,
554
380
  webexRequest: this.webex.request.bind(this.webex),
555
- locusInfoUpdateCallback: this.updateFromHashTree.bind(this, locusUrl),
556
- debugId: `HT-${locusUrl.split('/').pop().substring(0, 4)}`,
557
- excludedDataSets: this.webex.config.meetings.locus?.excludedDataSets,
381
+ locusInfoUpdateCallback: this.updateFromHashTree.bind(this),
382
+ debugId: `HT-${this.meetingId.substring(0, 4)}`,
558
383
  });
559
-
560
- // When a new HashTreeParser is created, previous one should be stopped.
561
- // Locus will only be sending us updates for the current one.
562
- for (const [existingLocusUrl, existingEntry] of this.hashTreeParsers) {
563
- if (existingEntry.parser.state !== 'stopped') {
564
- existingEntry.parser.stop();
565
- if (replacedAt) {
566
- existingEntry.replacedAt = replacedAt;
567
- } else {
568
- LoggerProxy.logger.warn(
569
- `Locus-info:index#createHashTreeParser --> no replacedAt timestamp provided for new HashTreeParser with locusUrl ${locusUrl}, replacing ${existingLocusUrl}`
570
- );
571
- }
572
- }
573
- }
574
-
575
- this.hashTreeParsers.set(locusUrl, {parser, initializedFromHashTree: false});
576
- this.hashTreeObjectId2ParticipantId.clear();
577
-
578
- return parser;
579
384
  }
580
385
 
581
386
  /**
@@ -589,7 +394,6 @@ export default class LocusInfo extends EventsScope {
589
394
  trigger: 'join-response';
590
395
  locus: LocusDTO;
591
396
  dataSets?: DataSet[];
592
- metadata?: Metadata;
593
397
  }
594
398
  | {
595
399
  trigger: 'locus-message';
@@ -604,50 +408,41 @@ export default class LocusInfo extends EventsScope {
604
408
  switch (data.trigger) {
605
409
  case 'locus-message':
606
410
  if (data.hashTreeMessage) {
607
- // we need the Metadata object to be in the received message, because it contains visibleDataSets
411
+ // we need the SELF object to be in the received message, because it contains visibleDataSets
608
412
  // and these are needed to initialize all the hash trees
609
- const metadataObject = data.hashTreeMessage.locusStateElements?.find((el) =>
610
- isMetadata(el)
611
- );
413
+ const selfObject = data.hashTreeMessage.locusStateElements?.find((el) => isSelf(el));
612
414
 
613
- if (!metadataObject?.data?.visibleDataSets) {
614
- // this is a common case (not an error)
615
- // it happens for example after we leave the meeting and still get some heartbeats or delayed messages
616
- LoggerProxy.logger.info(
617
- `Locus-info:index#initialSetup --> cannot initialize HashTreeParser, Metadata object with visibleDataSets is missing in the message`
415
+ if (!selfObject?.data?.visibleDataSets) {
416
+ LoggerProxy.logger.warn(
417
+ `Locus-info:index#initialSetup --> cannot initialize HashTreeParser, SELF object with visibleDataSets is missing in the message`
618
418
  );
619
419
 
620
- // throw so that handleLocusEvent() catches it and destroys the partially created meeting object
621
- throw new Error('Metadata object with visibleDataSets is missing in the message');
420
+ throw new Error('SELF object with visibleDataSets is missing in the message');
622
421
  }
623
422
 
624
423
  LoggerProxy.logger.info(
625
424
  'Locus-info:index#initialSetup --> creating HashTreeParser from message'
626
425
  );
627
426
  // first create the HashTreeParser, but don't initialize it with any data yet
628
- const hashTreeParser = this.createHashTreeParser({
629
- locusUrl: data.hashTreeMessage.locusUrl,
427
+ // pass just a fake locus that contains only the visibleDataSets
428
+ this.hashTreeParser = this.createHashTreeParser({
630
429
  initialLocus: {
631
- locus: null,
632
- dataSets: data.hashTreeMessage.dataSets,
633
- },
634
- metadata: {
635
- htMeta: metadataObject.htMeta,
636
- visibleDataSets: metadataObject.data.visibleDataSets,
430
+ locus: {self: {visibleDataSets: selfObject.data.visibleDataSets}},
431
+ dataSets: [], // empty, because they will be populated in initializeFromMessage() call // dataSets: data.hashTreeMessage.dataSets,
637
432
  },
638
433
  });
639
434
 
640
435
  // now handle the message - that should populate all the visible datasets
641
- await hashTreeParser.initializeFromMessage(data.hashTreeMessage);
436
+ await this.hashTreeParser.initializeFromMessage(data.hashTreeMessage);
642
437
  } else {
643
438
  // "classic" Locus case, no hash trees involved
644
439
  this.updateLocusCache(data.locus);
645
- this.onFullLocus('classic locus message', data.locus, undefined);
440
+ this.onFullLocus(data.locus, undefined);
646
441
  }
647
442
  break;
648
443
  case 'join-response':
649
444
  this.updateLocusCache(data.locus);
650
- this.onFullLocus('join response', data.locus, undefined, data.dataSets, data.metadata);
445
+ this.onFullLocus(data.locus, undefined, data.dataSets);
651
446
  break;
652
447
  case 'get-loci-response':
653
448
  if (data.locus?.links?.resources?.visibleDataSets?.url) {
@@ -655,21 +450,20 @@ export default class LocusInfo extends EventsScope {
655
450
  'Locus-info:index#initialSetup --> creating HashTreeParser from get-loci-response'
656
451
  );
657
452
  // first create the HashTreeParser, but don't initialize it with any data yet
658
- const hashTreeParser = this.createHashTreeParser({
659
- locusUrl: data.locus.url,
453
+ // pass just a fake locus that contains only the visibleDataSets
454
+ this.hashTreeParser = this.createHashTreeParser({
660
455
  initialLocus: {
661
- locus: null,
456
+ locus: {self: {visibleDataSets: data.locus?.self?.visibleDataSets}},
662
457
  dataSets: [], // empty, because we don't have them yet
663
458
  },
664
- metadata: null, // get-loci-response doesn't contain Metadata object
665
459
  });
666
460
 
667
461
  // now initialize all the data
668
- await hashTreeParser.initializeFromGetLociResponse(data.locus);
462
+ await this.hashTreeParser.initializeFromGetLociResponse(data.locus);
669
463
  } else {
670
464
  // "classic" Locus case, no hash trees involved
671
465
  this.updateLocusCache(data.locus);
672
- this.onFullLocus('classic get-loci-response', data.locus, undefined);
466
+ this.onFullLocus(data.locus, undefined);
673
467
  }
674
468
  }
675
469
  // Change it to true after it receives it first locus object
@@ -683,42 +477,27 @@ export default class LocusInfo extends EventsScope {
683
477
  * @returns {void}
684
478
  */
685
479
  handleLocusAPIResponse(meeting, responseBody: LocusApiResponseBody): void {
686
- const isWrapped = 'locus' in responseBody;
687
- const locusUrl = isWrapped ? responseBody.locus?.url : responseBody.url;
688
- const hashTreeParserEntry = locusUrl && this.hashTreeParsers.get(locusUrl);
689
- if (hashTreeParserEntry) {
690
- if (isWrapped) {
691
- if (!responseBody.dataSets) {
692
- this.sendClassicVsHashTreeMismatchMetric(
693
- meeting,
694
- `expected hash tree dataSets in API response but they are missing`
695
- );
696
- // continuing as we can still manage without responseBody.dataSets, but this is very suspicious
697
- }
698
- LoggerProxy.logger.info(
699
- 'Locus-info:index#handleLocusAPIResponse --> passing Locus API response to HashTreeParser: ',
700
- responseBody
701
- );
702
- // update the data in our hash trees
703
- hashTreeParserEntry.parser.handleLocusUpdate(responseBody);
704
- } else {
705
- // LocusDTO without wrapper - pass it through as if it had no dataSets
706
- hashTreeParserEntry.parser.handleLocusUpdate({locus: responseBody});
707
- }
480
+ if (this.hashTreeParser) {
481
+ // API responses with hash tree are a bit problematic and not fully confirmed how they will look like
482
+ // we don't really need them, because all updates are guaranteed to come via Mercury or LLM messages anyway
483
+ // so it's OK to skip them for now
484
+ LoggerProxy.logger.info(
485
+ 'Locus-info:index#handleLocusAPIResponse: skipping handling of API http response with hashTreeParser'
486
+ );
708
487
  } else {
709
- if (isWrapped && responseBody.dataSets) {
488
+ if (responseBody.dataSets) {
710
489
  this.sendClassicVsHashTreeMismatchMetric(
711
490
  meeting,
712
491
  `unexpected hash tree dataSets in API response`
713
492
  );
714
493
  }
715
494
  // classic Locus delta
716
- const locus = isWrapped ? responseBody.locus : responseBody;
717
- this.handleLocusDelta(locus, meeting);
495
+ this.handleLocusDelta(responseBody.locus, meeting);
718
496
  }
719
497
  }
720
498
 
721
499
  /**
500
+ *
722
501
  * @param {HashTreeObject} object data set object
723
502
  * @param {any} locus
724
503
  * @returns {void}
@@ -726,39 +505,29 @@ export default class LocusInfo extends EventsScope {
726
505
  updateLocusFromHashTreeObject(object: HashTreeObject, locus: LocusDTO): LocusDTO {
727
506
  const type = object.htMeta.elementId.type.toLowerCase();
728
507
 
729
- const addParticipantObject = (obj: HashTreeObject) => {
730
- if (!locus.participants) {
731
- locus.participants = [];
732
- }
733
- locus.participants.push(obj.data);
734
- this.hashTreeObjectId2ParticipantId.set(obj.htMeta.elementId.id, obj.data.id);
735
- };
736
-
737
508
  switch (type) {
738
509
  case ObjectType.locus: {
739
510
  if (!object.data) {
740
511
  // not doing anything here, as we need Locus to always be there (at least some fields)
741
512
  // and that's already taken care of in updateFromHashTree()
742
513
  LoggerProxy.logger.info(
743
- `Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object removed, version=${object.htMeta.elementId.version}`
514
+ `Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object removed`
744
515
  );
745
516
 
746
517
  return locus;
747
518
  }
748
519
  // replace the main locus
749
520
 
750
- // The Locus object we receive from backend has empty participants array,
751
- // and may have (although it shouldn't) other fields that are managed by other ObjectTypes
752
- // like "fullState" or "info", so we're making sure to delete them here
521
+ // The Locus object we receive from backend has empty participants, so removing them to avoid it overriding the ones in our current locus object
522
+ // Also, other fields like mediaShares are managed by other ObjectType updates, so removing them too
523
+ // BTW, it also doesn't have "self". That's OK as it won't override existing locus.self and also existing SDK code can handle that missing self in Locus updates
753
524
  const locusObjectFromData = object.data;
754
-
755
- Object.values(ObjectTypeToLocusKeyMap).forEach((locusDtoKey) => {
756
- delete locusObjectFromData[locusDtoKey];
757
- });
525
+ delete locusObjectFromData.participants;
526
+ delete locusObjectFromData.mediaShares;
758
527
 
759
528
  locus = {...locus, ...locusObjectFromData};
760
529
  LoggerProxy.logger.info(
761
- `Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object updated to version=${object.htMeta.elementId.version}`
530
+ `Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object updated`
762
531
  );
763
532
  break;
764
533
  }
@@ -771,7 +540,7 @@ export default class LocusInfo extends EventsScope {
771
540
  object.data.name === 'content'
772
541
  ? `floor=${object.data.floor?.disposition}, ${object.data.floor?.beneficiary?.id}`
773
542
  : ''
774
- } version=${object.htMeta.elementId.version}`
543
+ }`
775
544
  );
776
545
  const existingMediaShare = locus.mediaShares?.find(
777
546
  (ms) => ms.htMeta.elementId.id === object.htMeta.elementId.id
@@ -785,46 +554,27 @@ export default class LocusInfo extends EventsScope {
785
554
  }
786
555
  } else {
787
556
  LoggerProxy.logger.info(
788
- `Locus-info:index#updateLocusFromHashTreeObject --> mediaShare id=${object.htMeta.elementId.id} removed, version=${object.htMeta.elementId.version}`
557
+ `Locus-info:index#updateLocusFromHashTreeObject --> mediaShare id=${object.htMeta.elementId.id} removed`
789
558
  );
790
559
  locus.mediaShares = locus.mediaShares?.filter(
791
560
  (ms) => ms.htMeta.elementId.id !== object.htMeta.elementId.id
792
561
  );
793
562
  }
794
563
  break;
795
- case ObjectType.embeddedApp:
796
- if (object.data) {
797
- LoggerProxy.logger.info(
798
- `Locus-info:index#updateLocusFromHashTreeObject --> embeddedApp id=${object.htMeta.elementId.id} url='${object.data.url}' updated version=${object.htMeta.elementId.version}:`,
799
- object.data
800
- );
801
- const existingEmbeddedApp = locus.embeddedApps?.find(
802
- (ms) => ms.htMeta.elementId.id === object.htMeta.elementId.id
803
- );
804
-
805
- if (existingEmbeddedApp) {
806
- Object.assign(existingEmbeddedApp, object.data);
807
- } else {
808
- locus.embeddedApps = locus.embeddedApps || [];
809
- locus.embeddedApps.push(object.data);
810
- }
811
- } else {
812
- LoggerProxy.logger.info(
813
- `Locus-info:index#updateLocusFromHashTreeObject --> embeddedApp id=${object.htMeta.elementId.id} removed, version=${object.htMeta.elementId.version}`
814
- );
815
- locus.embeddedApps = locus.embeddedApps?.filter(
816
- (ms) => ms.htMeta.elementId.id !== object.htMeta.elementId.id
817
- );
818
- }
819
- break;
820
564
  case ObjectType.participant:
821
565
  LoggerProxy.logger.info(
822
566
  `Locus-info:index#updateLocusFromHashTreeObject --> participant id=${
823
567
  object.htMeta.elementId.id
824
- } ${object.data ? 'updated' : 'removed'} version=${object.htMeta.elementId.version}`
568
+ } ${object.data ? 'updated' : 'removed'}`
825
569
  );
826
570
  if (object.data) {
827
- addParticipantObject(object);
571
+ if (!locus.participants) {
572
+ locus.participants = [];
573
+ }
574
+ const participantObject = object.data;
575
+ participantObject.htMeta = object.htMeta;
576
+ locus.participants.push(participantObject);
577
+ this.hashTreeObjectId2ParticipantId.set(object.htMeta.elementId.id, participantObject.id);
828
578
  } else {
829
579
  const participantId = this.hashTreeObjectId2ParticipantId.get(object.htMeta.elementId.id);
830
580
 
@@ -834,83 +584,20 @@ export default class LocusInfo extends EventsScope {
834
584
  locus.jsSdkMeta.removedParticipantIds.push(participantId);
835
585
  this.hashTreeObjectId2ParticipantId.delete(object.htMeta.elementId.id);
836
586
  }
837
- // Create self from the participant if it matches self identity and is being moved.
838
- // We need this, because participant update comes in LLM message often before the self update from Mercury.
839
- // Other parts of the code detect move only by looking at self, while some other parts of the SDK/webapp code
840
- // look at participant for roles etc, so if participant is updated but not self, then it looks like we our lost roles temporarily
841
- // (until self is updated)
842
- // This will be fixed properly in SPARK-790239
843
- if (
844
- object.data &&
845
- object.data.identity === locus.self?.identity &&
846
- object.data.state === 'LEFT' &&
847
- object.data.reason === 'MOVED'
848
- ) {
849
- LoggerProxy.logger.info(
850
- `Locus-info:index#updateLocusFromHashTreeObject --> FOUND a match for MOVED self in participant object ${object.htMeta.elementId.id}`
851
- );
852
- Object.assign(locus[ObjectTypeToLocusKeyMap[ObjectType.self]], object.data);
853
- }
854
587
  break;
855
- case ObjectType.control:
856
- if (object.data) {
857
- Object.keys(object.data).forEach((controlKey) => {
858
- LoggerProxy.logger.info(
859
- `Locus-info:index#updateLocusFromHashTreeObject --> control ${controlKey} updated:`,
860
- object.data[controlKey]
861
- );
862
- if (!locus.controls) {
863
- locus.controls = {};
864
- }
865
- locus.controls[controlKey] = object.data[controlKey];
866
- });
867
- } else {
868
- LoggerProxy.logger.warn(
869
- `Locus-info:index#updateLocusFromHashTreeObject --> control object update without data - this is not expected!`
870
- );
871
- }
872
- break;
873
- case ObjectType.links:
874
- case ObjectType.info:
875
- case ObjectType.fullState:
876
588
  case ObjectType.self:
877
589
  if (!object.data) {
878
590
  // self without data is handled inside HashTreeParser and results in LocusInfoUpdateType.MEETING_ENDED, so we should never get here
879
- // all other types info, fullstate, etc - Locus should never send them without data
880
591
  LoggerProxy.logger.warn(
881
- `Locus-info:index#updateLocusFromHashTreeObject --> received ${type} object without data, this is not expected! version=${object.htMeta.elementId.version}`
592
+ `Locus-info:index#updateLocusFromHashTreeObject --> received SELF object without data, this is not expected!`
882
593
  );
883
- } else {
884
- LoggerProxy.logger.info(
885
- `Locus-info:index#updateLocusFromHashTreeObject --> ${type} object updated to version ${object.htMeta.elementId.version}`
886
- );
887
- const locusDtoKey = ObjectTypeToLocusKeyMap[type];
888
- locus[locusDtoKey] = object.data;
889
-
890
- /* Hash tree based webinar attendees don't receive a Participant object for themselves from Locus,
891
- but a lot of existing code in SDK and web app expects a member object for self to exist,
892
- so whenever SELF changes for a webinar attendee, we copy it into a participant object.
893
- We can do it, because SELF has always all the same properties as a participant object.
894
- */
895
- if (
896
- type === ObjectType.self &&
897
- locus.info?.isWebinar &&
898
- object.data.controls?.role?.roles?.find(
899
- (r) => r.type === SELF_ROLES.ATTENDEE && r.hasRole
900
- )
901
- ) {
902
- LoggerProxy.logger.info(
903
- `Locus-info:index#updateLocusFromHashTreeObject --> webinar attendee: creating participant object from self`
904
- );
905
- addParticipantObject(object);
906
- }
594
+
595
+ return locus;
907
596
  }
908
- break;
909
- case ObjectType.metadata:
910
597
  LoggerProxy.logger.info(
911
- `Locus-info:index#updateLocusFromHashTreeObject --> metadata object updated to version ${object.htMeta.elementId.version}`
598
+ `Locus-info:index#updateLocusFromHashTreeObject --> SELF object updated`
912
599
  );
913
- // we don't use hash tree metadata right now for anything, it's mainly used internally by HashTreeParser
600
+ locus.self = object.data;
914
601
  break;
915
602
  default:
916
603
  LoggerProxy.logger.warn(
@@ -944,92 +631,6 @@ export default class LocusInfo extends EventsScope {
944
631
  }
945
632
  }
946
633
 
947
- /**
948
- * Checks if the hash tree message should trigger a switch to a different HashTreeParser
949
- *
950
- * @param {HashTreeMessage} message incoming hash tree message
951
- * @returns {boolean} true if the message was handled as a parser switch, false otherwise
952
- */
953
- private handleHashTreeParserSwitch(message: HashTreeMessage): boolean {
954
- const entry = this.hashTreeParsers.get(message.locusUrl);
955
-
956
- const self = message.locusStateElements?.find((el) => isSelf(el))?.data;
957
- const replaces = getReplaceInfoFromSelf(
958
- self,
959
- // @ts-ignore
960
- this.webex.internal.device.url
961
- );
962
-
963
- if (!entry) {
964
- // Metadata object that contains information about visible datasets is needed to initialize the HashTreeParser,
965
- // but it's buried inside the message, we need to find it and pass it to HashTreeParser constructor
966
- const metadata = message.locusStateElements?.find((el) => isMetadata(el));
967
-
968
- if (metadata?.data?.visibleDataSets?.length > 0) {
969
- LoggerProxy.logger.info(
970
- `Locus-info:index#handleHashTreeParserSwitch --> no hash tree parser found for locusUrl ${message.locusUrl}, creating a new one`
971
- );
972
-
973
- const parser = this.createHashTreeParser({
974
- locusUrl: message.locusUrl,
975
- initialLocus: {
976
- locus: null,
977
- dataSets: message.dataSets,
978
- },
979
- metadata: {
980
- htMeta: metadata.htMeta,
981
- visibleDataSets: metadata.data.visibleDataSets,
982
- },
983
- replacedAt: replaces?.replacedAt,
984
- });
985
-
986
- // handle the message with the new parser
987
- parser.handleMessage(message);
988
- }
989
-
990
- return true;
991
- }
992
- if (entry.parser.state === 'stopped') {
993
- // the message matches a stopped parser, we need to check if maybe this is a new "replacement" and we need to re-activate the parser
994
- // this happens when you move from breakout A -> breakout B -> back to breakout A
995
- if (replaces) {
996
- if (replaces.replacedAt > (entry.replacedAt || '')) {
997
- LoggerProxy.logger.info(
998
- `Locus-info:index#handleHashTreeParserSwitch --> resuming a HashTreeParser for locusUrl=${message.locusUrl}, which replaces ${replaces.locusUrl}`
999
- );
1000
- const replacedEntry = this.hashTreeParsers.get(replaces.locusUrl);
1001
-
1002
- if (replacedEntry) {
1003
- replacedEntry.replacedAt = replaces.replacedAt;
1004
- entry.initializedFromHashTree = false;
1005
- this.hashTreeObjectId2ParticipantId.clear();
1006
-
1007
- replacedEntry.parser.stop();
1008
- entry.parser.resume(message);
1009
- } else {
1010
- LoggerProxy.logger.warn(
1011
- `Locus-info:index#handleHashTreeParserSwitch --> the parser that is supposed to be replaced with the currently resumed parser is not found, locusUrl=${replaces.locusUrl}`
1012
- );
1013
- }
1014
- } else {
1015
- LoggerProxy.logger.info(
1016
- `Locus-info:index#handleHashTreeParserSwitch --> received message for stopped HashTreeParser with locusUrl ${message.locusUrl}, but replaces info provided is not newer, so not re-activating the parser`
1017
- );
1018
- }
1019
-
1020
- return true;
1021
- }
1022
-
1023
- LoggerProxy.logger.info(
1024
- `Locus-info:index#handleHashTreeParserSwitch --> received message for stopped HashTreeParser with locusUrl ${message.locusUrl}, but no replaces info provided, so not re-activating the parser`
1025
- );
1026
-
1027
- return true;
1028
- }
1029
-
1030
- return false;
1031
- }
1032
-
1033
634
  /**
1034
635
  * Handles a hash tree message received from Locus.
1035
636
  *
@@ -1048,28 +649,18 @@ export default class LocusInfo extends EventsScope {
1048
649
  return;
1049
650
  }
1050
651
 
1051
- const parserSwitched = this.handleHashTreeParserSwitch(message);
1052
-
1053
- if (parserSwitched) {
1054
- return;
1055
- }
1056
-
1057
- const entry = this.hashTreeParsers.get(message.locusUrl);
1058
-
1059
- entry.parser.handleMessage(message);
652
+ this.hashTreeParser.handleMessage(message);
1060
653
  }
1061
654
 
1062
655
  /**
1063
656
  * Callback registered with HashTreeParser to receive locus info updates.
1064
657
  * Updates our locus info based on the data parsed by the hash tree parser.
1065
658
  *
1066
- * @param {string} locusUrl - the locus URL for which the update is received
1067
659
  * @param {LocusInfoUpdateType} updateType - The type of update received.
1068
660
  * @param {Object} [data] - Additional data for the update, if applicable.
1069
661
  * @returns {void}
1070
662
  */
1071
663
  private updateFromHashTree(
1072
- locusUrl: string,
1073
664
  updateType: LocusInfoUpdateType,
1074
665
  data?: {updatedObjects: HashTreeObject[]}
1075
666
  ) {
@@ -1078,10 +669,7 @@ export default class LocusInfo extends EventsScope {
1078
669
  // initialize our new locus
1079
670
  let locus: LocusDTO = {
1080
671
  participants: [],
1081
- jsSdkMeta: {
1082
- removedParticipantIds: [],
1083
- forceReplaceMembers: false,
1084
- },
672
+ jsSdkMeta: {removedParticipantIds: []},
1085
673
  };
1086
674
 
1087
675
  // first go over all the updates and check what happens with the main locus object
@@ -1114,40 +702,21 @@ export default class LocusInfo extends EventsScope {
1114
702
  }
1115
703
  });
1116
704
 
1117
- const hashTreeParserEntry = this.hashTreeParsers.get(locusUrl);
1118
-
1119
- if (!hashTreeParserEntry.initializedFromHashTree) {
1120
- // this is the first time we're getting an update for this locusUrl,
1121
- // so it's probably a move to/from breakout. We need to start from a clean state,
1122
- // so empty locus and we rely on Locus giving us sufficient data in the updates to populate it.
1123
- LoggerProxy.logger.info(
1124
- `Locus-info:index#updateFromHashTree --> first INITIAL update for locusUrl ${locusUrl}, starting from empty state`
1125
- );
1126
- hashTreeParserEntry.initializedFromHashTree = true;
1127
- locus.jsSdkMeta.forceReplaceMembers = true;
1128
- } else if (
1129
- // if Locus object is unchanged or removed, we need to keep using the existing locus
1130
- // because the rest of the locusInfo code expects locus to always be present (with at least some of the fields)
1131
- // if it gets updated, we only need to have the fields that are not part of "locus" object (like "info" or "mediaShares")
1132
- // so that when Locus object gets updated, if the new one is missing some field, that field will
1133
- // be removed from our locusInfo
705
+ // if Locus object is unchanged or removed, we need to keep using the existing locus
706
+ // because the rest of the locusInfo code expects locus to always be present (with at least some of the fields)
707
+ // if it gets updated, we don't need to do anything and we start with an empty one
708
+ // so that when it gets updated, if the new one is missing some field, that field will
709
+ // be removed from our locusInfo
710
+ if (
1134
711
  locusObjectStateAfterUpdates === LocusObjectStateAfterUpdates.unchanged ||
1135
712
  locusObjectStateAfterUpdates === LocusObjectStateAfterUpdates.removed
1136
713
  ) {
1137
- // copy over all of existing locus except participants
714
+ // copy over existing locus
1138
715
  LocusDtoTopLevelKeys.forEach((key) => {
1139
716
  if (key !== 'participants') {
1140
717
  locus[key] = cloneDeep(this[key]);
1141
718
  }
1142
719
  });
1143
- } else {
1144
- // initialize only the fields that are not part of main "Locus" object
1145
- // (except participants, which need to stay empty - that means "no participant changes")
1146
- Object.values(ObjectTypeToLocusKeyMap).forEach((locusDtoKey) => {
1147
- if (locusDtoKey !== 'participants') {
1148
- locus[locusDtoKey] = cloneDeep(this[locusDtoKey]);
1149
- }
1150
- });
1151
720
  }
1152
721
 
1153
722
  LoggerProxy.logger.info(
@@ -1155,7 +724,7 @@ export default class LocusInfo extends EventsScope {
1155
724
  data.updatedObjects.map((o) => ({
1156
725
  type: o.htMeta.elementId.type,
1157
726
  id: o.htMeta.elementId.id,
1158
- hasData: !!o.data,
727
+ hasData: o.data !== undefined,
1159
728
  }))
1160
729
  )}`
1161
730
  );
@@ -1171,14 +740,11 @@ export default class LocusInfo extends EventsScope {
1171
740
  }
1172
741
 
1173
742
  case LocusInfoUpdateType.MEETING_ENDED: {
743
+ LoggerProxy.logger.info(
744
+ `Locus-info:index#updateFromHashTree --> received signal that meeting ended, destroying meeting ${this.meetingId}`
745
+ );
1174
746
  const meeting = this.webex.meetings.meetingCollection.get(this.meetingId);
1175
-
1176
- if (meeting) {
1177
- LoggerProxy.logger.info(
1178
- `Locus-info:index#updateFromHashTree --> received signal that meeting ended, destroying meeting ${this.meetingId}`
1179
- );
1180
- this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.SELF_REMOVED);
1181
- }
747
+ this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.SELF_REMOVED);
1182
748
  }
1183
749
  }
1184
750
  }
@@ -1190,25 +756,15 @@ export default class LocusInfo extends EventsScope {
1190
756
  * @memberof LocusInfo
1191
757
  */
1192
758
  parse(meeting: any, data: any) {
1193
- if (this.hashTreeParsers.size > 0) {
759
+ if (this.hashTreeParser) {
1194
760
  this.handleHashTreeMessage(
1195
761
  meeting,
1196
762
  data.eventType,
1197
763
  data.stateElementsMessage as HashTreeMessage
1198
764
  );
1199
765
  } else {
766
+ // eslint-disable-next-line @typescript-eslint/no-shadow
1200
767
  const {eventType} = data;
1201
-
1202
- if (eventType === LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
1203
- // this can happen when we get an event before join http response
1204
- // it's OK to just ignore it
1205
- LoggerProxy.logger.info(
1206
- `Locus-info:index#parse --> received locus hash tree event before hashTreeParser is created`
1207
- );
1208
-
1209
- return;
1210
- }
1211
-
1212
768
  const locus = this.getTheLocusToUpdate(data.locus);
1213
769
  LoggerProxy.logger.info(`Locus-info:index#parse --> received locus data: ${eventType}`);
1214
770
 
@@ -1229,11 +785,17 @@ export default class LocusInfo extends EventsScope {
1229
785
  case LOCUSEVENT.PARTICIPANT_DECLINED:
1230
786
  case LOCUSEVENT.FLOOR_GRANTED:
1231
787
  case LOCUSEVENT.FLOOR_RELEASED:
1232
- this.onFullLocus(`classic locus event ${eventType}`, locus, eventType);
788
+ this.onFullLocus(locus, eventType);
1233
789
  break;
1234
790
  case LOCUSEVENT.DIFFERENCE:
1235
791
  this.handleLocusDelta(locus, meeting);
1236
792
  break;
793
+ case LOCUSEVENT.HASH_TREE_DATA_UPDATED:
794
+ this.sendClassicVsHashTreeMismatchMetric(
795
+ meeting,
796
+ `got ${eventType}, expected classic events`
797
+ );
798
+ break;
1237
799
 
1238
800
  default:
1239
801
  // Why will there be a event with no eventType ????
@@ -1257,63 +819,46 @@ export default class LocusInfo extends EventsScope {
1257
819
  /**
1258
820
  * Function for handling full locus when it's using hash trees (so not the "classic" one).
1259
821
  *
1260
- * @param {string} debugText string explaining the trigger for this call, added to logs for debugging purposes
1261
822
  * @param {object} locus locus object
1262
- * @param {object} metadata locus hash trees metadata
1263
823
  * @param {string} eventType locus event
1264
824
  * @param {DataSet[]} dataSets
1265
825
  * @returns {void}
1266
826
  */
1267
- private onFullLocusWithHashTrees(
1268
- debugText: string,
1269
- locus: any,
1270
- metadata: Metadata,
1271
- eventType?: string,
1272
- dataSets?: Array<DataSet>
1273
- ) {
1274
- if (!this.hashTreeParsers.has(locus.url)) {
827
+ private onFullLocusWithHashTrees(locus: any, eventType?: string, dataSets?: Array<DataSet>) {
828
+ if (!this.hashTreeParser) {
829
+ LoggerProxy.logger.info(`Locus-info:index#onFullLocus --> creating hash tree parser`);
1275
830
  LoggerProxy.logger.info(
1276
- `Locus-info:index#onFullLocus (${debugText}) --> creating hash tree parser for locusUrl=${locus.url}`
1277
- );
1278
- LoggerProxy.logger.info(
1279
- `Locus-info:index#onFullLocus (${debugText}) --> dataSets:`,
831
+ 'Locus-info:index#onFullLocus --> dataSets:',
1280
832
  dataSets,
1281
833
  ' and locus:',
1282
- locus,
1283
- ' and metadata:',
1284
- metadata
834
+ locus
1285
835
  );
1286
- this.createHashTreeParser({
1287
- locusUrl: locus.url,
836
+ this.hashTreeParser = this.createHashTreeParser({
1288
837
  initialLocus: {locus, dataSets},
1289
- metadata,
1290
838
  });
1291
- // we have a full locus to start with, so we consider Locus info to be "initialized"
1292
- this.hashTreeParsers.get(locus.url).initializedFromHashTree = true;
1293
839
  this.onFullLocusCommon(locus, eventType);
1294
840
  } else {
1295
841
  // in this case the Locus we're getting is not necessarily the full one
1296
842
  // so treat it like if we just got it in any api response
1297
843
 
1298
844
  LoggerProxy.logger.info(
1299
- `Locus-info:index#onFullLocus (${debugText}) --> hash tree parser already exists, handling it like a normal API response`
845
+ 'Locus-info:index#onFullLocus --> hash tree parser already exists, handling it like a normal API response'
1300
846
  );
1301
- this.handleLocusAPIResponse(undefined, {dataSets, locus, metadata});
847
+ this.handleLocusAPIResponse(undefined, {dataSets, locus});
1302
848
  }
1303
849
  }
1304
850
 
1305
851
  /**
1306
852
  * Function for handling full locus when it's the "classic" one (not hash trees)
1307
853
  *
1308
- * @param {string} debugText string explaining the trigger for this call, added to logs for debugging purposes
1309
854
  * @param {object} locus locus object
1310
855
  * @param {string} eventType locus event
1311
856
  * @returns {void}
1312
857
  */
1313
- private onFullLocusClassic(debugText: string, locus: any, eventType?: string) {
858
+ private onFullLocusClassic(locus: any, eventType?: string) {
1314
859
  if (!this.locusParser.isNewFullLocus(locus)) {
1315
860
  LoggerProxy.logger.info(
1316
- `Locus-info:index#onFullLocus (${debugText}) --> ignoring old full locus DTO, eventType=${eventType}`
861
+ `Locus-info:index#onFullLocus --> ignoring old full locus DTO, eventType=${eventType}`
1317
862
  );
1318
863
 
1319
864
  return;
@@ -1323,37 +868,24 @@ export default class LocusInfo extends EventsScope {
1323
868
 
1324
869
  /**
1325
870
  * updates the locus with full locus object
1326
- * @param {string} debugText string explaining the trigger for this call, added to logs for debugging purposes
1327
871
  * @param {object} locus locus object
1328
872
  * @param {string} eventType locus event
1329
873
  * @param {DataSet[]} dataSets
1330
- * @param {object} metadata locus hash trees metadata
1331
874
  * @returns {object} null
1332
875
  * @memberof LocusInfo
1333
876
  */
1334
- onFullLocus(
1335
- debugText: string,
1336
- locus: any,
1337
- eventType?: string,
1338
- dataSets?: Array<DataSet>,
1339
- metadata?: Metadata
1340
- ) {
877
+ onFullLocus(locus: any, eventType?: string, dataSets?: Array<DataSet>) {
1341
878
  if (!locus) {
1342
879
  LoggerProxy.logger.error(
1343
- `Locus-info:index#onFullLocus (${debugText}) --> object passed as argument was invalid, continuing.`
880
+ 'Locus-info:index#onFullLocus --> object passed as argument was invalid, continuing.'
1344
881
  );
1345
882
  }
1346
883
 
1347
884
  if (dataSets) {
1348
- if (!metadata) {
1349
- throw new Error(
1350
- `Locus-info:index#onFullLocus (${debugText}) --> hash tree metadata is missing with full Locus`
1351
- );
1352
- }
1353
885
  // this is the new hashmap Locus DTO format (only applicable to webinars for now)
1354
- this.onFullLocusWithHashTrees(debugText, locus, metadata, eventType, dataSets);
886
+ this.onFullLocusWithHashTrees(locus, eventType, dataSets);
1355
887
  } else {
1356
- this.onFullLocusClassic(debugText, locus, eventType);
888
+ this.onFullLocusClassic(locus, eventType);
1357
889
  }
1358
890
  }
1359
891
 
@@ -1368,7 +900,7 @@ export default class LocusInfo extends EventsScope {
1368
900
  this.participants = locus.participants;
1369
901
  this.participants?.forEach((participant) => {
1370
902
  // participant.htMeta is set only for hash tree based locus
1371
- if (typeof participant.htMeta?.elementId.id === 'number') {
903
+ if (participant.htMeta?.elementId.id) {
1372
904
  this.hashTreeObjectId2ParticipantId.set(participant.htMeta.elementId.id, participant.id);
1373
905
  }
1374
906
  });
@@ -1395,8 +927,8 @@ export default class LocusInfo extends EventsScope {
1395
927
  // eslint-disable-next-line @typescript-eslint/no-shadow
1396
928
  handleOneOnOneEvent(eventType: string) {
1397
929
  if (
1398
- this.parsedLocus.fullState?.type === _CALL_ ||
1399
- this.parsedLocus.fullState?.type === _SIP_BRIDGE_
930
+ this.parsedLocus.fullState.type === _CALL_ ||
931
+ this.parsedLocus.fullState.type === _SIP_BRIDGE_
1400
932
  ) {
1401
933
  // for 1:1 bob calls alice and alice declines, notify the meeting state
1402
934
  if (eventType === LOCUSEVENT.PARTICIPANT_DECLINED) {
@@ -1431,61 +963,20 @@ export default class LocusInfo extends EventsScope {
1431
963
  }
1432
964
  }
1433
965
 
1434
- /**
1435
- * Makes sure that passed in locus object has a participant object for self.
1436
- *
1437
- * @param {LocusDTO} locus The locus object to check and modify if needed
1438
- * @returns {void}
1439
- */
1440
- ensureSelfParticipantExists(locus: any) {
1441
- const {self} = locus;
1442
-
1443
- // sanity check, this should never fail
1444
- if (!self?.identity || !Array.isArray(locus.participants)) {
1445
- LoggerProxy.logger.warn(
1446
- `Locus-info:index#ensureSelfParticipantExists --> locus object is missing required fields, cannot ensure self participant exists. self?.identity="${self?.identity}"`
1447
- );
1448
-
1449
- return;
1450
- }
1451
-
1452
- const selfExists = locus.participants.some(
1453
- (participant) => participant.identity === self.identity
1454
- );
1455
-
1456
- if (!selfExists) {
1457
- locus.participants.push({...self});
1458
- }
1459
- }
1460
-
1461
966
  /**
1462
967
  * @param {Object} locus
1463
968
  * @returns {undefined}
1464
969
  * @memberof LocusInfo
1465
970
  */
1466
971
  onDeltaLocus(locus: any) {
1467
- const isReplaceMembers =
1468
- locus.jsSdkMeta?.forceReplaceMembers !== undefined
1469
- ? locus.jsSdkMeta.forceReplaceMembers
1470
- : ControlsUtils.isNeedReplaceMembers(this.controls, locus.controls);
1471
-
1472
- if (isReplaceMembers) {
1473
- // when we're moving between breakouts, Locus sometimes doesn't send us
1474
- // any participants at all for a few seconds
1475
- // Web app relies on having at least the self participant always there
1476
- // so we copy self into participants if it's not there.
1477
- this.ensureSelfParticipantExists(locus);
1478
- }
972
+ const isReplaceMembers = ControlsUtils.isNeedReplaceMembers(this.controls, locus.controls);
1479
973
  this.mergeParticipants(this.participants, locus.participants);
1480
- const updatesApplied = this.updateLocusInfo(locus);
1481
-
1482
- if (updatesApplied) {
1483
- this.updateParticipants(
1484
- locus.participants,
1485
- locus.jsSdkMeta?.removedParticipantIds,
1486
- isReplaceMembers
1487
- );
1488
- }
974
+ this.updateLocusInfo(locus);
975
+ this.updateParticipants(
976
+ locus.participants,
977
+ locus.jsSdkMeta?.removedParticipantIds,
978
+ isReplaceMembers
979
+ );
1489
980
  this.isMeetingActive();
1490
981
  }
1491
982
 
@@ -1499,7 +990,7 @@ export default class LocusInfo extends EventsScope {
1499
990
  // When moved to a breakout session locus sends a message for the previous locus
1500
991
  // indicating that we have been moved. It isn't helpful to continue parsing this
1501
992
  // as it gets interpreted as if we have left the call
1502
- return false;
993
+ return;
1503
994
  }
1504
995
 
1505
996
  this.updateControls(locus.controls, locus.self);
@@ -1510,17 +1001,19 @@ export default class LocusInfo extends EventsScope {
1510
1001
  this.updateLocusUrl(locus.url, ControlsUtils.isMainSessionDTO(locus));
1511
1002
  this.updateMeetingInfo(locus.info, locus.self);
1512
1003
  this.updateMediaShares(locus.mediaShares);
1513
- this.updateReplaces(locus.replaces);
1004
+ this.updateParticipantsUrl(locus.participantsUrl);
1005
+ this.updateReplace(locus.replace);
1514
1006
  this.updateSelf(locus.self);
1515
1007
  this.updateAclUrl(locus.aclUrl);
1516
1008
  this.updateBasequence(locus.baseSequence);
1517
1009
  this.updateSequence(locus.sequence);
1010
+ this.updateMemberShip(locus.membership);
1011
+ this.updateIdentifiers(locus.identities);
1518
1012
  this.updateEmbeddedApps(locus.embeddedApps);
1519
- this.updateLinks(locus.links);
1013
+ this.updateServices(locus.links?.services);
1014
+ this.updateResources(locus.links?.resources);
1520
1015
  this.compareAndUpdate();
1521
1016
  // update which required to compare different objects from locus
1522
-
1523
- return true;
1524
1017
  }
1525
1018
 
1526
1019
  /**
@@ -1552,9 +1045,9 @@ export default class LocusInfo extends EventsScope {
1552
1045
  */
1553
1046
  isMeetingActive() {
1554
1047
  if (
1555
- this.parsedLocus.fullState?.type === _CALL_ ||
1556
- this.parsedLocus.fullState?.type === _SIP_BRIDGE_ ||
1557
- this.parsedLocus.fullState?.type === _SPACE_SHARE_
1048
+ this.parsedLocus.fullState.type === _CALL_ ||
1049
+ this.parsedLocus.fullState.type === _SIP_BRIDGE_ ||
1050
+ this.parsedLocus.fullState.type === _SPACE_SHARE_
1558
1051
  ) {
1559
1052
  // @ts-ignore
1560
1053
  const partner = this.getLocusPartner(this.participants, this.self);
@@ -1647,7 +1140,7 @@ export default class LocusInfo extends EventsScope {
1647
1140
  }
1648
1141
  );
1649
1142
  }
1650
- } else if (this.parsedLocus.fullState?.type === _MEETING_) {
1143
+ } else if (this.parsedLocus.fullState.type === _MEETING_) {
1651
1144
  if (
1652
1145
  this.fullState &&
1653
1146
  (this.fullState.state === LOCUS.STATE.INACTIVE ||
@@ -1676,6 +1169,27 @@ export default class LocusInfo extends EventsScope {
1676
1169
  shouldLeave: false,
1677
1170
  }
1678
1171
  );
1172
+ } else if (this.fullState && this.fullState.removed) {
1173
+ // user has been dropped from a meeting
1174
+
1175
+ // @ts-ignore
1176
+ this.webex.internal.newMetrics.submitClientEvent({
1177
+ name: 'client.call.remote-ended',
1178
+ options: {
1179
+ meetingId: this.meetingId,
1180
+ },
1181
+ });
1182
+ this.emitScoped(
1183
+ {
1184
+ file: 'locus-info',
1185
+ function: 'isMeetingActive',
1186
+ },
1187
+ EVENTS.DESTROY_MEETING,
1188
+ {
1189
+ reason: MEETING_REMOVED_REASON.FULLSTATE_REMOVED,
1190
+ shouldLeave: false,
1191
+ }
1192
+ );
1679
1193
  }
1680
1194
  // If you are guest and you are removed from the meeting
1681
1195
  // You wont get any further events
@@ -1723,7 +1237,6 @@ export default class LocusInfo extends EventsScope {
1723
1237
  compareSelfAndHost() {
1724
1238
  // In some cases the host info is not present but the moderator values changes from null to false so it triggers an update
1725
1239
  if (
1726
- this.parsedLocus.self &&
1727
1240
  this.parsedLocus.self.selfIdentity === this.parsedLocus.host?.hostId &&
1728
1241
  this.parsedLocus.self.moderator
1729
1242
  ) {
@@ -1810,8 +1323,6 @@ export default class LocusInfo extends EventsScope {
1810
1323
  hasRecordingPausedChanged,
1811
1324
  hasMeetingContainerChanged,
1812
1325
  hasTranscribeChanged,
1813
- hasHesiodLLMIdChanged,
1814
- hasAiSummaryNotificationChanged,
1815
1326
  hasTranscribeSpokenLanguageChanged,
1816
1327
  hasManualCaptionChanged,
1817
1328
  hasEntryExitToneChanged,
@@ -1953,34 +1464,6 @@ export default class LocusInfo extends EventsScope {
1953
1464
  );
1954
1465
  }
1955
1466
 
1956
- if (hasHesiodLLMIdChanged) {
1957
- const {hesiodLlmId} = current.transcribe;
1958
-
1959
- this.emitScoped(
1960
- {
1961
- file: 'locus-info',
1962
- function: 'updateControls',
1963
- },
1964
- LOCUSINFO.EVENTS.CONTROLS_MEETING_HESIOD_LLM_ID_UPDATED,
1965
- {
1966
- hesiodLlmId,
1967
- }
1968
- );
1969
- }
1970
-
1971
- if (hasAiSummaryNotificationChanged) {
1972
- this.emitScoped(
1973
- {
1974
- file: 'locus-info',
1975
- function: 'updateControls',
1976
- },
1977
- LOCUSINFO.EVENTS.CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED,
1978
- {
1979
- aiSummaryNotification: current.transcribe.aiSummaryNotification,
1980
- }
1981
- );
1982
- }
1983
-
1984
1467
  if (hasTranscribeSpokenLanguageChanged) {
1985
1468
  const {spokenLanguage} = current.transcribe;
1986
1469
 
@@ -2013,7 +1496,6 @@ export default class LocusInfo extends EventsScope {
2013
1496
 
2014
1497
  if (hasBreakoutChanged) {
2015
1498
  const {breakout} = current;
2016
-
2017
1499
  breakout.breakoutMoveId = SelfUtils.getReplacedBreakoutMoveId(
2018
1500
  self,
2019
1501
  this.webex.internal.device.url
@@ -2181,19 +1663,17 @@ export default class LocusInfo extends EventsScope {
2181
1663
  }
2182
1664
 
2183
1665
  /**
2184
- * Updates links and emits appropriate events if services or resources have changed
2185
- * @param {Object} links
1666
+ * @param {Object} services
2186
1667
  * @returns {undefined}
2187
1668
  * @memberof LocusInfo
2188
1669
  */
2189
- updateLinks(links?: Links) {
2190
- const {services, resources} = links || {};
2191
-
2192
- if (services && !isEqual(this.links?.services, services)) {
1670
+ updateServices(services: Record<'breakout' | 'record', {url: string}>) {
1671
+ if (services && !isEqual(this.services, services)) {
1672
+ this.services = services;
2193
1673
  this.emitScoped(
2194
1674
  {
2195
1675
  file: 'locus-info',
2196
- function: 'updateLinks',
1676
+ function: 'updateServices',
2197
1677
  },
2198
1678
  LOCUSINFO.EVENTS.LINKS_SERVICES,
2199
1679
  {
@@ -2201,12 +1681,20 @@ export default class LocusInfo extends EventsScope {
2201
1681
  }
2202
1682
  );
2203
1683
  }
1684
+ }
2204
1685
 
2205
- if (resources && !isEqual(this.links?.resources, resources)) {
1686
+ /**
1687
+ * @param {Object} resources
1688
+ * @returns {undefined}
1689
+ * @memberof LocusInfo
1690
+ */
1691
+ updateResources(resources: Record<'webcastInstance', {url: string}>) {
1692
+ if (resources && !isEqual(this.resources, resources)) {
1693
+ this.resources = resources;
2206
1694
  this.emitScoped(
2207
1695
  {
2208
1696
  file: 'locus-info',
2209
- function: 'updateLinks',
1697
+ function: 'updateResources',
2210
1698
  },
2211
1699
  LOCUSINFO.EVENTS.LINKS_RESOURCES,
2212
1700
  {
@@ -2214,8 +1702,6 @@ export default class LocusInfo extends EventsScope {
2214
1702
  }
2215
1703
  );
2216
1704
  }
2217
-
2218
- this.links = links;
2219
1705
  }
2220
1706
 
2221
1707
  /**
@@ -2402,13 +1888,24 @@ export default class LocusInfo extends EventsScope {
2402
1888
  }
2403
1889
 
2404
1890
  /**
2405
- * @param {Object} replaces
1891
+ * @param {String} participantsUrl
1892
+ * @returns {undefined}
1893
+ * @memberof LocusInfo
1894
+ */
1895
+ updateParticipantsUrl(participantsUrl: string) {
1896
+ if (participantsUrl && !isEqual(this.participantsUrl, participantsUrl)) {
1897
+ this.participantsUrl = participantsUrl;
1898
+ }
1899
+ }
1900
+
1901
+ /**
1902
+ * @param {Object} replace
2406
1903
  * @returns {undefined}
2407
1904
  * @memberof LocusInfo
2408
1905
  */
2409
- updateReplaces(replaces: object) {
2410
- if (replaces && !isEqual(this.replaces, replaces)) {
2411
- this.replaces = replaces;
1906
+ updateReplace(replace: object) {
1907
+ if (replace && !isEqual(this.replace, replace)) {
1908
+ this.replace = replace;
2412
1909
  }
2413
1910
  }
2414
1911
 
@@ -2439,14 +1936,14 @@ export default class LocusInfo extends EventsScope {
2439
1936
  }
2440
1937
 
2441
1938
  // TODO: check if we need to save the sipUri here as well
2442
- // this.emit(LOCUSINFO.EVENTS.MEETING_UPDATE, SelfUtils.getSipUrl(this.getLocusPartner(participants, self), this.parsedLocus.fullState?.type, this.parsedLocus.info?.sipUri));
1939
+ // this.emit(LOCUSINFO.EVENTS.MEETING_UPDATE, SelfUtils.getSipUrl(this.getLocusPartner(participants, self), this.parsedLocus.fullState.type, this.parsedLocus.info.sipUri));
2443
1940
  const result = SelfUtils.getSipUrl(
2444
1941
  this.getLocusPartner(this.participants, self),
2445
- this.parsedLocus.fullState?.type,
2446
- this.parsedLocus.info?.sipUri
1942
+ this.parsedLocus.fullState.type,
1943
+ this.parsedLocus.info.sipUri
2447
1944
  );
2448
1945
 
2449
- if (result?.sipUri) {
1946
+ if (result.sipUri) {
2450
1947
  this.updateMeeting(result);
2451
1948
  }
2452
1949
 
@@ -2491,19 +1988,6 @@ export default class LocusInfo extends EventsScope {
2491
1988
  );
2492
1989
  }
2493
1990
 
2494
- if (parsedSelves.updates.selfIdChanged) {
2495
- this.emitScoped(
2496
- {
2497
- file: 'locus-info',
2498
- function: 'updateSelf',
2499
- },
2500
- LOCUSINFO.EVENTS.SELF_ID_CHANGED,
2501
- {
2502
- selfId: parsedSelves.current.selfId,
2503
- }
2504
- );
2505
- }
2506
-
2507
1991
  if (parsedSelves.updates.interpretationChanged) {
2508
1992
  this.emitScoped(
2509
1993
  {
@@ -2758,6 +2242,28 @@ export default class LocusInfo extends EventsScope {
2758
2242
  }
2759
2243
  }
2760
2244
 
2245
+ /**
2246
+ * @param {Object} membership
2247
+ * @returns {undefined}
2248
+ * @memberof LocusInfo
2249
+ */
2250
+ updateMemberShip(membership: object) {
2251
+ if (membership && !isEqual(this.membership, membership)) {
2252
+ this.membership = membership;
2253
+ }
2254
+ }
2255
+
2256
+ /**
2257
+ * @param {Array} identities
2258
+ * @returns {undefined}
2259
+ * @memberof LocusInfo
2260
+ */
2261
+ updateIdentifiers(identities: Array<any>) {
2262
+ if (identities && !isEqual(this.identities, identities)) {
2263
+ this.identities = identities;
2264
+ }
2265
+ }
2266
+
2761
2267
  /**
2762
2268
  * check the locus is main session's one or not, if is main session's, update main session cache
2763
2269
  * @param {Object} locus