@webex/plugin-meetings 3.10.0-next.9 → 3.10.0-set-bitrate.2

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 (73) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +11 -3
  4. package/dist/constants.js.map +1 -1
  5. package/dist/hashTree/constants.js +20 -0
  6. package/dist/hashTree/constants.js.map +1 -0
  7. package/dist/hashTree/hashTree.js +515 -0
  8. package/dist/hashTree/hashTree.js.map +1 -0
  9. package/dist/hashTree/hashTreeParser.js +1266 -0
  10. package/dist/hashTree/hashTreeParser.js.map +1 -0
  11. package/dist/hashTree/types.js +22 -0
  12. package/dist/hashTree/types.js.map +1 -0
  13. package/dist/hashTree/utils.js +48 -0
  14. package/dist/hashTree/utils.js.map +1 -0
  15. package/dist/interpretation/index.js +1 -1
  16. package/dist/interpretation/siLanguage.js +1 -1
  17. package/dist/locus-info/index.js +550 -130
  18. package/dist/locus-info/index.js.map +1 -1
  19. package/dist/locus-info/types.js +7 -0
  20. package/dist/locus-info/types.js.map +1 -0
  21. package/dist/meeting/index.js +100 -50
  22. package/dist/meeting/index.js.map +1 -1
  23. package/dist/meeting/util.js +1 -0
  24. package/dist/meeting/util.js.map +1 -1
  25. package/dist/meetings/index.js +112 -70
  26. package/dist/meetings/index.js.map +1 -1
  27. package/dist/metrics/constants.js +3 -1
  28. package/dist/metrics/constants.js.map +1 -1
  29. package/dist/reachability/clusterReachability.js +44 -358
  30. package/dist/reachability/clusterReachability.js.map +1 -1
  31. package/dist/reachability/reachability.types.js +14 -1
  32. package/dist/reachability/reachability.types.js.map +1 -1
  33. package/dist/reachability/reachabilityPeerConnection.js +445 -0
  34. package/dist/reachability/reachabilityPeerConnection.js.map +1 -0
  35. package/dist/types/constants.d.ts +26 -21
  36. package/dist/types/hashTree/constants.d.ts +8 -0
  37. package/dist/types/hashTree/hashTree.d.ts +129 -0
  38. package/dist/types/hashTree/hashTreeParser.d.ts +260 -0
  39. package/dist/types/hashTree/types.d.ts +27 -0
  40. package/dist/types/hashTree/utils.d.ts +9 -0
  41. package/dist/types/locus-info/index.d.ts +97 -80
  42. package/dist/types/locus-info/types.d.ts +54 -0
  43. package/dist/types/meeting/index.d.ts +23 -9
  44. package/dist/types/meetings/index.d.ts +9 -2
  45. package/dist/types/metrics/constants.d.ts +2 -0
  46. package/dist/types/reachability/clusterReachability.d.ts +10 -88
  47. package/dist/types/reachability/reachability.types.d.ts +12 -1
  48. package/dist/types/reachability/reachabilityPeerConnection.d.ts +111 -0
  49. package/dist/webinar/index.js +1 -1
  50. package/package.json +23 -22
  51. package/src/constants.ts +13 -1
  52. package/src/hashTree/constants.ts +9 -0
  53. package/src/hashTree/hashTree.ts +463 -0
  54. package/src/hashTree/hashTreeParser.ts +1161 -0
  55. package/src/hashTree/types.ts +32 -0
  56. package/src/hashTree/utils.ts +42 -0
  57. package/src/locus-info/index.ts +597 -154
  58. package/src/locus-info/types.ts +53 -0
  59. package/src/meeting/index.ts +88 -28
  60. package/src/meeting/util.ts +1 -0
  61. package/src/meetings/index.ts +104 -51
  62. package/src/metrics/constants.ts +2 -0
  63. package/src/reachability/clusterReachability.ts +50 -347
  64. package/src/reachability/reachability.types.ts +15 -1
  65. package/src/reachability/reachabilityPeerConnection.ts +416 -0
  66. package/test/unit/spec/hashTree/hashTree.ts +655 -0
  67. package/test/unit/spec/hashTree/hashTreeParser.ts +1532 -0
  68. package/test/unit/spec/hashTree/utils.ts +103 -0
  69. package/test/unit/spec/locus-info/index.js +795 -16
  70. package/test/unit/spec/meeting/index.js +120 -20
  71. package/test/unit/spec/meeting/utils.js +77 -0
  72. package/test/unit/spec/meetings/index.js +71 -26
  73. package/test/unit/spec/reachability/clusterReachability.ts +281 -138
@@ -0,0 +1,53 @@
1
+ import {HtMeta} from '../hashTree/types';
2
+
3
+ export type LocusFullState = {
4
+ active: boolean;
5
+ count: number;
6
+ lastActive: string;
7
+ locked: boolean;
8
+ sessionId: string;
9
+ seessionIds: string[];
10
+ startTime: number;
11
+ state: string;
12
+ type: string;
13
+ };
14
+
15
+ export type Links = {
16
+ services: Record<'breakout' | 'record', {url: string}>; // there exist also other services, but these are the ones we currently use
17
+ resources: Record<'webcastInstance' | 'visibleDataSets', {url: string}>; // there exist also other resources, but these are the ones we currently use
18
+ };
19
+
20
+ export type LocusDTO = {
21
+ controls?: any;
22
+ fullState?: LocusFullState;
23
+ host?: {
24
+ id: string;
25
+ incomingCallProtocols: any[];
26
+ isExternal: boolean;
27
+ name: string;
28
+ orgId: string;
29
+ };
30
+ htMeta?: HtMeta;
31
+ info?: any;
32
+ jsSdkMeta?: {
33
+ removedParticipantIds: string[]; // list of ids of participants that are removed in the last update
34
+ };
35
+ links?: Links;
36
+ mediaShares?: any[];
37
+ meetings?: any[];
38
+ participants: any[];
39
+ replaces?: any[];
40
+ self?: any;
41
+ sequence?: {
42
+ dirtyParticipants: number;
43
+ entries: number[];
44
+ rangeEnd: number;
45
+ rangeStart: number;
46
+ sequenceHash: number;
47
+ sessionToken: string;
48
+ since: string;
49
+ totalParticipants: number;
50
+ };
51
+ syncUrl?: string;
52
+ url?: string;
53
+ };
@@ -1,5 +1,5 @@
1
1
  import uuid from 'uuid';
2
- import {cloneDeep, isEqual, isEmpty} from 'lodash';
2
+ import {cloneDeep, isEqual, isEmpty, merge} from 'lodash';
3
3
  import jwtDecode from 'jwt-decode';
4
4
  // @ts-ignore - Fix this
5
5
  import {StatelessWebexPlugin} from '@webex/webex-core';
@@ -50,6 +50,11 @@ import {
50
50
  type MeetingTranscriptPayload,
51
51
  } from '@webex/internal-plugin-voicea';
52
52
 
53
+ import {
54
+ getBrowserMediaErrorCode,
55
+ isBrowserMediaError,
56
+ isBrowserMediaErrorName,
57
+ } from '@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.util';
53
58
  import {processNewCaptions} from './voicea-meeting';
54
59
 
55
60
  import {
@@ -70,7 +75,7 @@ import Media, {type BundlePolicy} from '../media';
70
75
  import MediaProperties from '../media/properties';
71
76
  import MeetingStateMachine from './state';
72
77
  import {createMuteState} from './muteState';
73
- import LocusInfo from '../locus-info';
78
+ import LocusInfo, {LocusLLMEvent} from '../locus-info';
74
79
  import Metrics from '../metrics';
75
80
  import ReconnectionManager from '../reconnection-manager';
76
81
  import ReconnectionNotStartedError from '../common/errors/reconnection-not-started';
@@ -126,6 +131,8 @@ import {
126
131
  JOIN_BEFORE_HOST,
127
132
  REGISTRATION_ID_STATUS,
128
133
  STAGE_MANAGER_TYPE,
134
+ LOCUSEVENT,
135
+ LOCUS_LLM_EVENT,
129
136
  } from '../constants';
130
137
  import BEHAVIORAL_METRICS from '../metrics/constants';
131
138
  import ParameterError from '../common/errors/parameter';
@@ -171,6 +178,8 @@ import JoinForbiddenError from '../common/errors/join-forbidden-error';
171
178
  import {ReachabilityMetrics} from '../reachability/reachability.types';
172
179
  import {SetStageOptions, SetStageVideoLayout, UnsetStageVideoLayout} from './request.type';
173
180
  import {Invitee} from './type';
181
+ import {DataSet} from '../hashTree/hashTreeParser';
182
+ import {LocusDTO} from '../locus-info/types';
174
183
 
175
184
  // default callback so we don't call an undefined function, but in practice it should never be used
176
185
  const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
@@ -665,6 +674,7 @@ export default class Meeting extends StatelessWebexPlugin {
665
674
  localAudioStreamMuteStateHandler: () => void;
666
675
  localVideoStreamMuteStateHandler: () => void;
667
676
  localOutputTrackChangeHandler: () => void;
677
+ localConstraintsChangeHandler: () => void;
668
678
  roles: any[];
669
679
  environment: string;
670
680
  namespace = MEETINGS;
@@ -1530,6 +1540,12 @@ export default class Meeting extends StatelessWebexPlugin {
1530
1540
  }
1531
1541
  };
1532
1542
 
1543
+ this.localConstraintsChangeHandler = () => {
1544
+ if (!this.isMultistream) {
1545
+ this.mediaProperties.webrtcMediaConnection?.updatePreferredBitrateKbps();
1546
+ }
1547
+ };
1548
+
1533
1549
  /**
1534
1550
  * Promise that exists if SDP offer has been generated, and resolves once sdp answer is received.
1535
1551
  * @instance
@@ -4551,40 +4567,45 @@ export default class Meeting extends StatelessWebexPlugin {
4551
4567
  }
4552
4568
 
4553
4569
  /**
4554
- * Set the locus info the class instance
4555
- * @param {Object} locus
4556
- * @param {Array} locus.mediaConnections
4557
- * @param {String} locus.locusUrl
4558
- * @param {String} locus.locusId
4559
- * @param {String} locus.mediaId
4560
- * @param {Object} locus.host
4570
+ * Set the locus info the class instance. Should be called with the parsed locus
4571
+ * we got in the join response.
4572
+ *
4573
+ * @param {Object} data
4574
+ * @param {Array} data.mediaConnections
4575
+ * @param {String} data.locusUrl
4576
+ * @param {String} data.locusId
4577
+ * @param {String} data.mediaId
4578
+ * @param {Object} data.host
4561
4579
  * @todo change name to genertic parser
4562
4580
  * @returns {undefined}
4563
4581
  * @private
4564
4582
  * @memberof Meeting
4565
4583
  */
4566
- setLocus(
4567
- locus:
4568
- | {
4569
- mediaConnections: Array<any>;
4570
- locusUrl: string;
4571
- locusId: string;
4572
- mediaId: string;
4573
- host: object;
4574
- }
4575
- | any
4576
- ) {
4577
- const mtgLocus: any = locus.locus || locus;
4584
+ setLocus(data: {
4585
+ locus: LocusDTO;
4586
+ mediaConnections: Array<any>;
4587
+ locusUrl: string;
4588
+ locusId: string;
4589
+ mediaId: string;
4590
+ host: object;
4591
+ selfId: string;
4592
+ dataSets: DataSet[];
4593
+ }) {
4594
+ const mtgLocus: any = data.locus;
4578
4595
 
4579
4596
  // LocusInfo object saves the locus object
4580
4597
  // this.locus = mtgLocus;
4581
- this.mediaConnections = locus.mediaConnections;
4582
- this.locusUrl = locus.locusUrl || locus.url;
4583
- this.locusId = locus.locusId;
4584
- this.selfId = locus.selfId;
4585
- this.mediaId = locus.mediaId;
4598
+ this.mediaConnections = data.mediaConnections;
4599
+ this.locusUrl = data.locusUrl;
4600
+ this.locusId = data.locusId;
4601
+ this.selfId = data.selfId;
4602
+ this.mediaId = data.mediaId;
4586
4603
  this.hostId = mtgLocus.host ? mtgLocus.host.id : this.hostId;
4587
- this.locusInfo.initialSetup(mtgLocus);
4604
+ this.locusInfo.initialSetup({
4605
+ trigger: 'join-response',
4606
+ locus: mtgLocus,
4607
+ dataSets: data.dataSets,
4608
+ });
4588
4609
  }
4589
4610
 
4590
4611
  /**
@@ -4793,7 +4814,7 @@ export default class Meeting extends StatelessWebexPlugin {
4793
4814
  this.localVideoStreamMuteStateHandler
4794
4815
  );
4795
4816
  oldStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
4796
-
4817
+ oldStream?.off(LocalStreamEventNames.ConstraintsChange, this.localConstraintsChangeHandler);
4797
4818
  // we don't update this.mediaProperties.mediaDirection.sendVideo, because we always keep it as true to avoid extra SDP exchanges
4798
4819
  this.mediaProperties.setLocalVideoStream(localStream);
4799
4820
 
@@ -4808,6 +4829,7 @@ export default class Meeting extends StatelessWebexPlugin {
4808
4829
  this.localVideoStreamMuteStateHandler
4809
4830
  );
4810
4831
  localStream?.on(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
4832
+ localStream?.on(LocalStreamEventNames.ConstraintsChange, this.localConstraintsChangeHandler);
4811
4833
 
4812
4834
  if (!this.isMultistream || !localStream) {
4813
4835
  // for multistream WCME automatically un-publishes the old stream when we publish a new one
@@ -4942,6 +4964,7 @@ export default class Meeting extends StatelessWebexPlugin {
4942
4964
  this.localVideoStreamMuteStateHandler
4943
4965
  );
4944
4966
  videoStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
4967
+ videoStream?.off(LocalStreamEventNames.ConstraintsChange, this.localConstraintsChangeHandler);
4945
4968
 
4946
4969
  shareAudioStream?.off(StreamEventNames.Ended, this.handleShareAudioStreamEnded);
4947
4970
  shareAudioStream?.off(
@@ -5431,6 +5454,20 @@ export default class Meeting extends StatelessWebexPlugin {
5431
5454
  shouldRetry = false;
5432
5455
  }
5433
5456
 
5457
+ if (CallDiagnosticUtils.isBrowserMediaError(error)) {
5458
+ shouldRetry = false;
5459
+ // eslint-disable-next-line no-ex-assign
5460
+ error = merge({
5461
+ error: {
5462
+ body: {
5463
+ errorCode: CallDiagnosticUtils.getBrowserMediaErrorCode(error),
5464
+ message: error?.message,
5465
+ name: error?.name,
5466
+ },
5467
+ },
5468
+ });
5469
+ }
5470
+
5434
5471
  // we only want to call leave if join was successful and this was a retry or we won't be doing any more retries
5435
5472
  if (joined && (isRetry || !shouldRetry)) {
5436
5473
  try {
@@ -5697,6 +5734,21 @@ export default class Meeting extends StatelessWebexPlugin {
5697
5734
  }
5698
5735
  }
5699
5736
 
5737
+ /** Handles Locus LLM events
5738
+ *
5739
+ * @param {LocusLLMEvent} event - The Locus LLM event to process
5740
+ * @returns {void}
5741
+ */
5742
+ private processLocusLLMEvent = (event: LocusLLMEvent): void => {
5743
+ if (event.data.eventType === LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
5744
+ this.locusInfo.parse(this, event.data);
5745
+ } else {
5746
+ LoggerProxy.logger.warn(
5747
+ `Meeting:index#processLocusLLMEvent --> Unknown event type: ${event.data.eventType}`
5748
+ );
5749
+ }
5750
+ };
5751
+
5700
5752
  /**
5701
5753
  * Callback called when a relay event is received from meeting LLM Connection
5702
5754
  * @param {RelayEvent} e Event object coming from LLM Connection
@@ -6108,6 +6160,8 @@ export default class Meeting extends StatelessWebexPlugin {
6108
6160
  );
6109
6161
  // @ts-ignore - Fix type
6110
6162
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
6163
+ // @ts-ignore - Fix type
6164
+ this.webex.internal.llm.off(LOCUS_LLM_EVENT, this.processLocusLLMEvent);
6111
6165
  }
6112
6166
 
6113
6167
  if (!isJoined) {
@@ -6122,6 +6176,10 @@ export default class Meeting extends StatelessWebexPlugin {
6122
6176
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
6123
6177
  // @ts-ignore - Fix type
6124
6178
  this.webex.internal.llm.on('event:relay.event', this.processRelayEvent);
6179
+ // @ts-ignore - Fix type
6180
+ this.webex.internal.llm.off(LOCUS_LLM_EVENT, this.processLocusLLMEvent);
6181
+ // @ts-ignore - Fix type
6182
+ this.webex.internal.llm.on(LOCUS_LLM_EVENT, this.processLocusLLMEvent);
6125
6183
  LoggerProxy.logger.info(
6126
6184
  'Meeting:index#updateLLMConnection --> enabled to receive relay events!'
6127
6185
  );
@@ -9373,6 +9431,8 @@ export default class Meeting extends StatelessWebexPlugin {
9373
9431
 
9374
9432
  // @ts-ignore - fix types
9375
9433
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
9434
+ // @ts-ignore - Fix type
9435
+ this.webex.internal.llm.off(LOCUS_LLM_EVENT, this.processLocusLLMEvent);
9376
9436
  };
9377
9437
 
9378
9438
  /**
@@ -31,6 +31,7 @@ const MeetingUtil = {
31
31
 
32
32
  // First todo: add check for existance
33
33
  parsed.locus = response.body.locus;
34
+ parsed.dataSets = response.body.dataSets;
34
35
  parsed.mediaConnections = response.body.mediaConnections;
35
36
  parsed.locusUrl = parsed.locus.url;
36
37
  parsed.locusId = parsed.locus.url.split('/').pop();
@@ -1,5 +1,5 @@
1
1
  /* eslint no-shadow: ["error", { "allow": ["eventType"] }] */
2
- import {cloneDeep, clone} from 'lodash';
2
+ import {cloneDeep, clone, set} from 'lodash';
3
3
  import '@webex/internal-plugin-mercury';
4
4
  import '@webex/internal-plugin-conversation';
5
5
  import '@webex/internal-plugin-metrics';
@@ -66,6 +66,7 @@ import JoinWebinarError from '../common/errors/join-webinar-error';
66
66
  import {SpaceIDDeprecatedError} from '../common/errors/webex-errors';
67
67
  import NoMeetingInfoError from '../common/errors/no-meeting-info';
68
68
  import JoinForbiddenError from '../common/errors/join-forbidden-error';
69
+ import {HashTreeMessage} from '../hashTree/hashTreeParser';
69
70
 
70
71
  let mediaLogger;
71
72
 
@@ -94,6 +95,18 @@ class MediaLogger {
94
95
  LoggerProxy.logger.debug(...args);
95
96
  }
96
97
  }
98
+
99
+ export type LocusEvent = {
100
+ eventType: LOCUSEVENT;
101
+
102
+ // fields populated for "classic" locus events (eventType = 'locus.difference' and others, see LOCUSEVENT)
103
+ locusUrl?: string;
104
+ locus?: any;
105
+
106
+ // fields populated for "hash tree" locus events (eventType = 'locus.state_message' - see LOCUSEVENT.HASH_TREE_DATA_UPDATED)
107
+ stateElementsMessage?: HashTreeMessage;
108
+ };
109
+
97
110
  /**
98
111
  * Meetings Ready Event
99
112
  * Emitted when the meetings instance on webex is ready
@@ -406,7 +419,17 @@ export default class Meetings extends WebexPlugin {
406
419
  * @private
407
420
  * @memberof Meetings
408
421
  */
409
- getCorrespondingMeetingByLocus(data) {
422
+ getCorrespondingMeetingByLocus(data: LocusEvent) {
423
+ if (
424
+ data.eventType === LOCUSEVENT.HASH_TREE_DATA_UPDATED &&
425
+ data.stateElementsMessage?.locusUrl
426
+ ) {
427
+ return this.meetingCollection.getByKey(
428
+ MEETING_KEY.LOCUS_URL,
429
+ data.stateElementsMessage.locusUrl
430
+ );
431
+ }
432
+
410
433
  // getting meeting by correlationId. This will happen for the new event
411
434
  // Either the locus
412
435
  // TODO : Add check for the callBack Address
@@ -420,15 +443,13 @@ export default class Meetings extends WebexPlugin {
420
443
  ) ||
421
444
  this.meetingCollection.getByKey(
422
445
  MEETING_KEY.SIP_URI,
423
- data.locus.self &&
424
- data.locus.self.callbackInfo &&
425
- data.locus.self.callbackInfo.callbackAddress
446
+ data.locus?.self?.callbackInfo?.callbackAddress
426
447
  ) ||
427
- (data.locus.info?.isUnifiedSpaceMeeting
448
+ (data.locus?.info?.isUnifiedSpaceMeeting
428
449
  ? undefined
429
450
  : this.meetingCollection.getByKey(
430
451
  MEETING_KEY.CONVERSATION_URL,
431
- data.locus.conversationUrl
452
+ data.locus?.conversationUrl
432
453
  )) ||
433
454
  this.meetingCollection.getByKey(MEETING_KEY.MEETINGNUMBER, data.locus?.info?.webExMeetingId)
434
455
  );
@@ -445,30 +466,33 @@ export default class Meetings extends WebexPlugin {
445
466
  * @private
446
467
  * @memberof Meetings
447
468
  */
448
- private handleLocusEvent(data: {locusUrl: string; locus: any}, useRandomDelayForInfo = false) {
469
+ private handleLocusEvent(data: LocusEvent, useRandomDelayForInfo = false) {
449
470
  let meeting = this.getCorrespondingMeetingByLocus(data);
450
471
 
451
472
  // Special case when locus has got replaced, This only happend once if a replace locus exists
452
473
  // https://sqbu-github.cisco.com/WebExSquared/locus/wiki/Locus-changing-mid-call
453
474
 
454
- if (!meeting && data.locus?.replaces?.length > 0) {
455
- // Always the last element in the replace is the active one
456
- meeting = this.meetingCollection.getByKey(
457
- MEETING_KEY.LOCUS_URL,
458
- data.locus.replaces[data.locus.replaces.length - 1].locusUrl
459
- );
460
- }
475
+ if (data.eventType !== LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
476
+ if (!meeting && data.locus?.replaces?.length > 0) {
477
+ // Always the last element in the replace is the active one
478
+ meeting = this.meetingCollection.getByKey(
479
+ MEETING_KEY.LOCUS_URL,
480
+ data.locus.replaces[data.locus.replaces.length - 1].locusUrl
481
+ );
482
+ }
461
483
 
462
- if (meeting && !MeetingsUtil.isBreakoutLocusDTO(data.locus)) {
463
- meeting.locusInfo.updateMainSessionLocusCache(data.locus);
464
- }
465
- if (!this.isNeedHandleLocusDTO(meeting, data.locus)) {
466
- LoggerProxy.logger.log(
467
- `Meetings:index#handleLocusEvent --> doesn't need to process locus event`
468
- );
484
+ if (meeting && !MeetingsUtil.isBreakoutLocusDTO(data.locus)) {
485
+ meeting.locusInfo.updateMainSessionLocusCache(data.locus);
486
+ }
487
+ if (!this.isNeedHandleLocusDTO(meeting, data.locus)) {
488
+ LoggerProxy.logger.log(
489
+ `Meetings:index#handleLocusEvent --> doesn't need to process locus event`
490
+ );
469
491
 
470
- return;
492
+ return;
493
+ }
471
494
  }
495
+
472
496
  if (!meeting) {
473
497
  // TODO: create meeting when we get a meeting object
474
498
  // const checkForEnded = (locus) => {
@@ -489,42 +513,65 @@ export default class Meetings extends WebexPlugin {
489
513
  // };
490
514
  // rather then locus object change to locus url
491
515
 
492
- if (
493
- data.locus &&
494
- data.locus.fullState &&
495
- data.locus.fullState.state === LOCUS.STATE.INACTIVE
496
- ) {
497
- // just ignore the event as its already ended and not active
498
- LoggerProxy.logger.warn(
499
- 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
500
- );
516
+ if (data.eventType !== LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
517
+ if (
518
+ data.locus &&
519
+ data.locus.fullState &&
520
+ data.locus.fullState.state === LOCUS.STATE.INACTIVE
521
+ ) {
522
+ // just ignore the event as its already ended and not active
523
+ LoggerProxy.logger.warn(
524
+ 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
525
+ );
501
526
 
502
- return;
503
- }
527
+ return;
528
+ }
504
529
 
505
- // When its wireless share or guest and user leaves the meeting we dont have to keep the meeting object
506
- // Any future events will be neglected
530
+ // When its wireless share or guest and user leaves the meeting we dont have to keep the meeting object
531
+ // Any future events will be neglected
532
+
533
+ if (
534
+ data.locus &&
535
+ data.locus.self &&
536
+ data.locus.self.state === _LEFT_ &&
537
+ data.locus.self.removed === true
538
+ ) {
539
+ // just ignore the event as its already ended and not active
540
+ LoggerProxy.logger.warn(
541
+ 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
542
+ );
507
543
 
508
- if (
509
- data.locus &&
510
- data.locus.self &&
511
- data.locus.self.state === _LEFT_ &&
512
- data.locus.self.removed === true
513
- ) {
514
- // just ignore the event as its already ended and not active
515
- LoggerProxy.logger.warn(
516
- 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
517
- );
544
+ return;
545
+ }
546
+ }
518
547
 
519
- return;
548
+ if (data.eventType === LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
549
+ // in hash tree messages we don't ge the locus object, but the meeting constructor needs at least locus.url
550
+ set(data, 'locus.url', data.stateElementsMessage.locusUrl);
520
551
  }
521
552
 
522
553
  this.create(data.locus, DESTINATION_TYPE.LOCUS_ID, useRandomDelayForInfo)
523
- .then((newMeeting) => {
554
+ .then(async (newMeeting) => {
524
555
  meeting = newMeeting;
525
556
 
526
- // It's a new meeting so initialize the locus data
527
- meeting.locusInfo.initialSetup(data.locus);
557
+ try {
558
+ // It's a new meeting so initialize the locus data
559
+ await meeting.locusInfo.initialSetup({
560
+ trigger:
561
+ data.eventType === LOCUSEVENT.SDK_LOCUS_FROM_SYNC_MEETINGS
562
+ ? 'get-loci-response'
563
+ : 'locus-message',
564
+ locus: data.locus,
565
+ hashTreeMessage: data.stateElementsMessage,
566
+ });
567
+ } catch (error) {
568
+ LoggerProxy.logger.warn(
569
+ `Meetings:index#handleLocusEvent --> Error initializing locus data: ${error.message}`
570
+ );
571
+ // @ts-ignore
572
+ this.destroy(meeting, MEETING_REMOVED_REASON.LOCUS_DTO_SYNC_FAILED);
573
+ }
574
+
528
575
  this.checkHandleBreakoutLocus(data.locus);
529
576
  })
530
577
  .catch((e) => {
@@ -1739,6 +1786,7 @@ export default class Meetings extends WebexPlugin {
1739
1786
  lociToUpdate.forEach((locus) => {
1740
1787
  activeLocusUrl.push(locus.url);
1741
1788
  this.handleLocusEvent({
1789
+ eventType: LOCUSEVENT.SDK_LOCUS_FROM_SYNC_MEETINGS,
1742
1790
  locus,
1743
1791
  locusUrl: locus.url,
1744
1792
  });
@@ -1786,6 +1834,7 @@ export default class Meetings extends WebexPlugin {
1786
1834
  (mainLocus) => mainLocus.controls?.breakout?.url === breakoutLocus.controls?.breakout?.url
1787
1835
  );
1788
1836
  const existCorrespondingMeeting = this.getCorrespondingMeetingByLocus({
1837
+ eventType: LOCUSEVENT.SDK_NO_EVENT,
1789
1838
  locus: breakoutLocus,
1790
1839
  locusUrl: breakoutLocus.url,
1791
1840
  });
@@ -1831,7 +1880,11 @@ export default class Meetings extends WebexPlugin {
1831
1880
  }
1832
1881
 
1833
1882
  const associateBreakoutLocus = this.breakoutLocusForHandleLater[existIndex];
1834
- this.handleLocusEvent({locus: associateBreakoutLocus, locusUrl: associateBreakoutLocus.url});
1883
+ this.handleLocusEvent({
1884
+ eventType: LOCUSEVENT.SDK_NO_EVENT,
1885
+ locus: associateBreakoutLocus,
1886
+ locusUrl: associateBreakoutLocus.url,
1887
+ });
1835
1888
  this.breakoutLocusForHandleLater.splice(existIndex, 1);
1836
1889
  }
1837
1890
 
@@ -87,6 +87,8 @@ const BEHAVIORAL_METRICS = {
87
87
  VERIFY_REGISTRATION_ID_ERROR: 'js_sdk_verify_registrationId_error',
88
88
  JOIN_FORBIDDEN_ERROR: 'js_sdk_join_forbidden_error',
89
89
  MEDIA_ISSUE_DETECTED: 'js_sdk_media_issue_detected',
90
+ LOCUS_CLASSIC_VS_HASH_TREE_MISMATCH: 'js_sdk_locus_classic_vs_hash_tree_mismatch',
91
+ LOCUS_HASH_TREE_UNSUPPORTED_OPERATION: 'js_sdk_locus_hash_tree_unsupported_operation',
90
92
  };
91
93
 
92
94
  export {BEHAVIORAL_METRICS as default};