@webex/plugin-meetings 3.9.0-webinar5k.1 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +16 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/interpretation/index.js +1 -1
  6. package/dist/interpretation/siLanguage.js +1 -1
  7. package/dist/locus-info/index.js +40 -328
  8. package/dist/locus-info/index.js.map +1 -1
  9. package/dist/meeting/in-meeting-actions.js +6 -0
  10. package/dist/meeting/in-meeting-actions.js.map +1 -1
  11. package/dist/meeting/index.js +196 -160
  12. package/dist/meeting/index.js.map +1 -1
  13. package/dist/meeting/muteState.js +5 -2
  14. package/dist/meeting/muteState.js.map +1 -1
  15. package/dist/meeting/type.js +7 -0
  16. package/dist/meeting/type.js.map +1 -0
  17. package/dist/meeting/util.js +79 -10
  18. package/dist/meeting/util.js.map +1 -1
  19. package/dist/meetings/index.js +37 -39
  20. package/dist/meetings/index.js.map +1 -1
  21. package/dist/member/types.js.map +1 -1
  22. package/dist/members/collection.js +0 -13
  23. package/dist/members/collection.js.map +1 -1
  24. package/dist/members/index.js +21 -40
  25. package/dist/members/index.js.map +1 -1
  26. package/dist/members/util.js.map +1 -1
  27. package/dist/multistream/mediaRequestManager.js +1 -1
  28. package/dist/multistream/mediaRequestManager.js.map +1 -1
  29. package/dist/multistream/remoteMedia.js +34 -5
  30. package/dist/multistream/remoteMedia.js.map +1 -1
  31. package/dist/multistream/remoteMediaGroup.js +42 -2
  32. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  33. package/dist/types/constants.d.ts +16 -0
  34. package/dist/types/locus-info/index.d.ts +3 -102
  35. package/dist/types/meeting/in-meeting-actions.d.ts +6 -0
  36. package/dist/types/meeting/index.d.ts +23 -28
  37. package/dist/types/meeting/type.d.ts +9 -0
  38. package/dist/types/meeting/util.d.ts +6 -3
  39. package/dist/types/member/types.d.ts +0 -1
  40. package/dist/types/members/collection.d.ts +0 -6
  41. package/dist/types/members/index.d.ts +7 -16
  42. package/dist/types/members/util.d.ts +2 -1
  43. package/dist/types/multistream/remoteMedia.d.ts +20 -1
  44. package/dist/types/multistream/remoteMediaGroup.d.ts +11 -0
  45. package/dist/webinar/index.js +1 -1
  46. package/package.json +22 -24
  47. package/src/constants.ts +16 -2
  48. package/src/locus-info/index.ts +39 -409
  49. package/src/meeting/in-meeting-actions.ts +13 -0
  50. package/src/meeting/index.ts +92 -63
  51. package/src/meeting/muteState.ts +6 -2
  52. package/src/meeting/type.ts +9 -0
  53. package/src/meeting/util.ts +93 -19
  54. package/src/meetings/index.ts +6 -19
  55. package/src/member/types.ts +0 -1
  56. package/src/members/collection.ts +0 -11
  57. package/src/members/index.ts +10 -33
  58. package/src/members/util.ts +2 -1
  59. package/src/multistream/mediaRequestManager.ts +7 -7
  60. package/src/multistream/remoteMedia.ts +34 -4
  61. package/src/multistream/remoteMediaGroup.ts +37 -2
  62. package/test/unit/spec/locus-info/index.js +8 -365
  63. package/test/unit/spec/meeting/in-meeting-actions.ts +6 -0
  64. package/test/unit/spec/meeting/index.js +254 -38
  65. package/test/unit/spec/meeting/utils.js +122 -1
  66. package/test/unit/spec/meetings/index.js +2 -0
  67. package/test/unit/spec/members/index.js +37 -1
  68. package/test/unit/spec/multistream/mediaRequestManager.ts +19 -6
  69. package/test/unit/spec/multistream/remoteMedia.ts +66 -2
  70. package/dist/hashTree/constants.js +0 -23
  71. package/dist/hashTree/constants.js.map +0 -1
  72. package/dist/hashTree/hashTree.js +0 -516
  73. package/dist/hashTree/hashTree.js.map +0 -1
  74. package/dist/hashTree/hashTreeParser.js +0 -521
  75. package/dist/hashTree/hashTreeParser.js.map +0 -1
  76. package/dist/types/hashTree/constants.d.ts +0 -8
  77. package/dist/types/hashTree/hashTree.d.ts +0 -128
  78. package/dist/types/hashTree/hashTreeParser.d.ts +0 -152
  79. package/src/hashTree/constants.ts +0 -12
  80. package/src/hashTree/hashTree.ts +0 -460
  81. package/src/hashTree/hashTreeParser.ts +0 -556
  82. package/test/unit/spec/hashTree/hashTree.ts +0 -394
  83. package/test/unit/spec/hashTree/hashTreeParser.ts +0 -156
@@ -66,7 +66,7 @@ import Media, {type BundlePolicy} from '../media';
66
66
  import MediaProperties from '../media/properties';
67
67
  import MeetingStateMachine from './state';
68
68
  import {createMuteState} from './muteState';
69
- import LocusInfo, {LocusDTO, LocusLLMEvent} from '../locus-info';
69
+ import LocusInfo from '../locus-info';
70
70
  import Metrics from '../metrics';
71
71
  import ReconnectionManager from '../reconnection-manager';
72
72
  import ReconnectionNotStartedError from '../common/errors/reconnection-not-started';
@@ -166,7 +166,7 @@ import MultistreamNotSupportedError from '../common/errors/multistream-not-suppo
166
166
  import JoinForbiddenError from '../common/errors/join-forbidden-error';
167
167
  import {ReachabilityMetrics} from '../reachability/reachability.types';
168
168
  import {SetStageOptions, SetStageVideoLayout, UnsetStageVideoLayout} from './request.type';
169
- import {DataSet} from '../hashTree/hashTreeParser';
169
+ import {Invitee} from './type';
170
170
 
171
171
  // default callback so we don't call an undefined function, but in practice it should never be used
172
172
  const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
@@ -251,6 +251,7 @@ export type CallStateForMetrics = {
251
251
  loginType?: string;
252
252
  userNameInput?: string;
253
253
  emailInput?: string;
254
+ pstnCorrelationId?: string;
254
255
  };
255
256
 
256
257
  export const MEDIA_UPDATE_TYPE = {
@@ -744,10 +745,11 @@ export default class Meeting extends StatelessWebexPlugin {
744
745
  /**
745
746
  * @param {Object} attrs
746
747
  * @param {Object} options
748
+ * @param {Function} callback - if provided, it will be called with the newly created meeting object as soon as the meeting.id is set
747
749
  * @constructor
748
750
  * @memberof Meeting
749
751
  */
750
- constructor(attrs: any, options: object) {
752
+ constructor(attrs: any, options: object, callback: (meeting: Meeting) => void) {
751
753
  super({}, options);
752
754
  /**
753
755
  * @instance
@@ -773,6 +775,11 @@ export default class Meeting extends StatelessWebexPlugin {
773
775
  * @memberof Meeting
774
776
  */
775
777
  this.id = uuid.v4();
778
+
779
+ if (callback) {
780
+ callback(this);
781
+ }
782
+
776
783
  /**
777
784
  * Call state used for metrics
778
785
  * @instance
@@ -1677,6 +1684,22 @@ export default class Meeting extends StatelessWebexPlugin {
1677
1684
  this.callStateForMetrics.correlationId = correlationId;
1678
1685
  }
1679
1686
 
1687
+ /**
1688
+ * Getter - Returns callStateForMetrics.pstnCorrelationId
1689
+ * @returns {string | undefined}
1690
+ */
1691
+ get pstnCorrelationId(): string | undefined {
1692
+ return this.callStateForMetrics.pstnCorrelationId;
1693
+ }
1694
+
1695
+ /**
1696
+ * Setter - sets callStateForMetrics.pstnCorrelationId
1697
+ * @param {string | undefined} correlationId
1698
+ */
1699
+ set pstnCorrelationId(correlationId: string | undefined) {
1700
+ this.callStateForMetrics.pstnCorrelationId = correlationId;
1701
+ }
1702
+
1680
1703
  /**
1681
1704
  * Getter - Returns callStateForMetrics.userNameInput
1682
1705
  * @returns {string}
@@ -3059,12 +3082,16 @@ export default class Meeting extends StatelessWebexPlugin {
3059
3082
  // There is no concept of local/remote share for whiteboard
3060
3083
  // It does not matter who requested to share the whiteboard, everyone gets the same view
3061
3084
  else if (whiteboardShare.disposition === FLOOR_ACTION.GRANTED) {
3062
- // WHITEBOARD - sharing whiteboard
3063
- // Webinar attendee should receive whiteboard as remote share
3064
- newShareStatus =
3065
- this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee
3066
- ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
3067
- : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
3085
+ if (this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee) {
3086
+ // WHITEBOARD - sharing whiteboard
3087
+ // Webinar attendee should receive whiteboard as remote share
3088
+ newShareStatus = SHARE_STATUS.REMOTE_SHARE_ACTIVE;
3089
+ } else if (this.guest) {
3090
+ // If user is a guest to a meeting, they should receive whiteboard as remote share
3091
+ newShareStatus = SHARE_STATUS.REMOTE_SHARE_ACTIVE;
3092
+ } else {
3093
+ newShareStatus = SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
3094
+ }
3068
3095
  }
3069
3096
  // or if content share is either released or null and whiteboard share is either released or null, no one is sharing
3070
3097
  else if (
@@ -3830,49 +3857,43 @@ export default class Meeting extends StatelessWebexPlugin {
3830
3857
 
3831
3858
  /**
3832
3859
  * Invite a guest to the call that isn't normally part of this call
3833
- * @param {Object} invitee
3860
+ * @param {Invitee} invitee
3834
3861
  * @param {String} invitee.emailAddress
3835
3862
  * @param {String} invitee.email
3836
3863
  * @param {String} invitee.phoneNumber
3837
3864
  * @param {Boolean} [alertIfActive]
3865
+ * @param {Boolean} [invitee.skipEmailValidation]
3866
+ * @param {Boolean} [invitee.isInternalNumber]
3838
3867
  * @returns {Promise} see #members.addMember
3839
3868
  * @public
3840
3869
  * @memberof Meeting
3841
3870
  */
3842
- public invite(
3843
- invitee: {
3844
- emailAddress: string;
3845
- email: string;
3846
- phoneNumber: string;
3847
- roles: Array<string>;
3848
- },
3849
- alertIfActive = true
3850
- ) {
3871
+ public invite(invitee: Invitee, alertIfActive = true) {
3851
3872
  return this.members.addMember(invitee, alertIfActive);
3852
3873
  }
3853
3874
 
3854
3875
  /**
3855
3876
  * Cancel an outgoing phone call invitation made during a meeting
3856
- * @param {Object} invitee
3877
+ * @param {Invitee} invitee
3857
3878
  * @param {String} invitee.phoneNumber
3858
3879
  * @returns {Promise} see #members.cancelPhoneInvite
3859
3880
  * @public
3860
3881
  * @memberof Meeting
3861
3882
  */
3862
- public cancelPhoneInvite(invitee: {phoneNumber: string}) {
3883
+ public cancelPhoneInvite(invitee: Invitee) {
3863
3884
  return this.members.cancelPhoneInvite(invitee);
3864
3885
  }
3865
3886
 
3866
3887
  /**
3867
3888
  * Cancel an SIP/phone call invitation made during a meeting
3868
- * @param {Object} invitee
3889
+ * @param {Invitee} invitee
3869
3890
  * @param {String} invitee.memberId
3870
3891
  * @param {Boolean} [invitee.isInternalNumber] - When cancel phone invitation, if the number is internal
3871
3892
  * @returns {Promise} see #members.cancelInviteByMemberId
3872
3893
  * @public
3873
3894
  * @memberof Meeting
3874
3895
  */
3875
- public cancelInviteByMemberId(invitee: {memberId: string; isInternalNumber?: boolean}) {
3896
+ public cancelInviteByMemberId(invitee: Invitee) {
3876
3897
  return this.members.cancelInviteByMemberId(invitee);
3877
3898
  }
3878
3899
 
@@ -4171,6 +4192,9 @@ export default class Meeting extends StatelessWebexPlugin {
4171
4192
  isClosedCaptionActive: MeetingUtil.isClosedCaptionActive(this.userDisplayHints),
4172
4193
  canStartManualCaption: MeetingUtil.canStartManualCaption(this.userDisplayHints),
4173
4194
  canStopManualCaption: MeetingUtil.canStopManualCaption(this.userDisplayHints),
4195
+ isLocalRecordingStarted: MeetingUtil.isLocalRecordingStarted(this.userDisplayHints),
4196
+ isLocalRecordingStopped: MeetingUtil.isLocalRecordingStopped(this.userDisplayHints),
4197
+ isLocalRecordingPaused: MeetingUtil.isLocalRecordingPaused(this.userDisplayHints),
4174
4198
  isManualCaptionActive: MeetingUtil.isManualCaptionActive(this.userDisplayHints),
4175
4199
  isSaveTranscriptsEnabled: MeetingUtil.isSaveTranscriptsEnabled(this.userDisplayHints),
4176
4200
  isWebexAssistantActive: MeetingUtil.isWebexAssistantActive(this.userDisplayHints),
@@ -4479,13 +4503,11 @@ export default class Meeting extends StatelessWebexPlugin {
4479
4503
  setLocus(
4480
4504
  locus:
4481
4505
  | {
4482
- locus: LocusDTO;
4483
4506
  mediaConnections: Array<any>;
4484
4507
  locusUrl: string;
4485
4508
  locusId: string;
4486
4509
  mediaId: string;
4487
4510
  host: object;
4488
- dataSets: DataSet[];
4489
4511
  }
4490
4512
  | any
4491
4513
  ) {
@@ -4499,7 +4521,7 @@ export default class Meeting extends StatelessWebexPlugin {
4499
4521
  this.selfId = locus.selfId;
4500
4522
  this.mediaId = locus.mediaId;
4501
4523
  this.hostId = mtgLocus.host ? mtgLocus.host.id : this.hostId;
4502
- this.locusInfo.initialSetup(mtgLocus, locus.dataSets);
4524
+ this.locusInfo.initialSetup(mtgLocus);
4503
4525
  }
4504
4526
 
4505
4527
  /**
@@ -5612,21 +5634,6 @@ export default class Meeting extends StatelessWebexPlugin {
5612
5634
  }
5613
5635
  }
5614
5636
 
5615
- /** Handles Locus LLM events
5616
- *
5617
- * @param {LocusLLMEvent} event - The Locus LLM event to process
5618
- * @returns {void}
5619
- */
5620
- private processLocusLLMEvent = (event: LocusLLMEvent): void => {
5621
- if (event.data.eventType === 'locus.state_message') {
5622
- this.locusInfo.parse(this, event.data);
5623
- } else {
5624
- LoggerProxy.logger.warn(
5625
- `Meeting:index#processLocusLLMEvent --> Unknown event type: ${event.data.eventType}`
5626
- );
5627
- }
5628
- };
5629
-
5630
5637
  /**
5631
5638
  * Callback called when a relay event is received from meeting LLM Connection
5632
5639
  * @param {RelayEvent} e Event object coming from LLM Connection
@@ -5945,15 +5952,6 @@ export default class Meeting extends StatelessWebexPlugin {
5945
5952
  this.meetingFiniteStateMachine.fail(error);
5946
5953
  LoggerProxy.logger.error('Meeting:index#join --> Failed', error);
5947
5954
 
5948
- // @ts-ignore
5949
- this.webex.internal.newMetrics.submitClientEvent({
5950
- name: 'client.locus.join.response',
5951
- payload: {
5952
- identifiers: {meetingLookupUrl: this.meetingInfo?.meetingLookupUrl},
5953
- },
5954
- options: {meetingId: this.id, rawError: error},
5955
- });
5956
-
5957
5955
  // TODO: change this to error codes and pre defined dictionary
5958
5956
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.JOIN_FAILURE, {
5959
5957
  correlation_id: this.correlationId,
@@ -6047,8 +6045,6 @@ export default class Meeting extends StatelessWebexPlugin {
6047
6045
  );
6048
6046
  // @ts-ignore - Fix type
6049
6047
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
6050
- // @ts-ignore - Fix type
6051
- this.webex.internal.llm.off('event:locus.state_message', this.processLocusLLMEvent);
6052
6048
  }
6053
6049
 
6054
6050
  if (!isJoined) {
@@ -6063,10 +6059,6 @@ export default class Meeting extends StatelessWebexPlugin {
6063
6059
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
6064
6060
  // @ts-ignore - Fix type
6065
6061
  this.webex.internal.llm.on('event:relay.event', this.processRelayEvent);
6066
- // @ts-ignore - Fix type
6067
- this.webex.internal.llm.off('event:locus.state_message', this.processLocusLLMEvent);
6068
- // @ts-ignore - Fix type
6069
- this.webex.internal.llm.on('event:locus.state_message', this.processLocusLLMEvent);
6070
6062
  LoggerProxy.logger.info(
6071
6063
  'Meeting:index#updateLLMConnection --> enabled to receive relay events!'
6072
6064
  );
@@ -6109,8 +6101,9 @@ export default class Meeting extends StatelessWebexPlugin {
6109
6101
  */
6110
6102
  private dialInPstn() {
6111
6103
  if (this.isPhoneProvisioned(this.dialInDeviceStatus)) return Promise.resolve(); // prevent multiple dial in devices from being provisioned
6104
+ this.pstnCorrelationId = uuid.v4();
6112
6105
 
6113
- const {correlationId, locusUrl} = this;
6106
+ const {pstnCorrelationId, locusUrl} = this;
6114
6107
 
6115
6108
  if (!this.dialInUrl) this.dialInUrl = `dialin:///${uuid.v4()}`;
6116
6109
 
@@ -6118,7 +6111,7 @@ export default class Meeting extends StatelessWebexPlugin {
6118
6111
  this.meetingRequest
6119
6112
  // @ts-ignore
6120
6113
  .dialIn({
6121
- correlationId,
6114
+ correlationId: pstnCorrelationId,
6122
6115
  dialInUrl: this.dialInUrl,
6123
6116
  locusUrl,
6124
6117
  clientUrl: this.deviceUrl,
@@ -6127,12 +6120,17 @@ export default class Meeting extends StatelessWebexPlugin {
6127
6120
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_DIAL_IN_FAILURE, {
6128
6121
  correlation_id: this.correlationId,
6129
6122
  dial_in_url: this.dialInUrl,
6123
+ dial_in_correlation_id: pstnCorrelationId,
6130
6124
  locus_id: locusUrl.split('/').pop(),
6131
6125
  client_url: this.deviceUrl,
6132
6126
  reason: error.error?.message,
6133
6127
  stack: error.stack,
6134
6128
  });
6135
6129
 
6130
+ if (this.pstnCorrelationId === pstnCorrelationId) {
6131
+ this.pstnCorrelationId = undefined;
6132
+ }
6133
+
6136
6134
  return Promise.reject(error);
6137
6135
  })
6138
6136
  );
@@ -6147,8 +6145,9 @@ export default class Meeting extends StatelessWebexPlugin {
6147
6145
  */
6148
6146
  private dialOutPstn(phoneNumber: string) {
6149
6147
  if (this.isPhoneProvisioned(this.dialOutDeviceStatus)) return Promise.resolve(); // prevent multiple dial out devices from being provisioned
6148
+ this.pstnCorrelationId = uuid.v4();
6150
6149
 
6151
- const {correlationId, locusUrl} = this;
6150
+ const {locusUrl, pstnCorrelationId} = this;
6152
6151
 
6153
6152
  if (!this.dialOutUrl) this.dialOutUrl = `dialout:///${uuid.v4()}`;
6154
6153
 
@@ -6156,7 +6155,7 @@ export default class Meeting extends StatelessWebexPlugin {
6156
6155
  this.meetingRequest
6157
6156
  // @ts-ignore
6158
6157
  .dialOut({
6159
- correlationId,
6158
+ correlationId: pstnCorrelationId,
6160
6159
  dialOutUrl: this.dialOutUrl,
6161
6160
  phoneNumber,
6162
6161
  locusUrl,
@@ -6166,12 +6165,17 @@ export default class Meeting extends StatelessWebexPlugin {
6166
6165
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_DIAL_OUT_FAILURE, {
6167
6166
  correlation_id: this.correlationId,
6168
6167
  dial_out_url: this.dialOutUrl,
6168
+ dial_out_correlation_id: pstnCorrelationId,
6169
6169
  locus_id: locusUrl.split('/').pop(),
6170
6170
  client_url: this.deviceUrl,
6171
6171
  reason: error.error?.message,
6172
6172
  stack: error.stack,
6173
6173
  });
6174
6174
 
6175
+ if (this.pstnCorrelationId === pstnCorrelationId) {
6176
+ this.pstnCorrelationId = undefined;
6177
+ }
6178
+
6175
6179
  return Promise.reject(error);
6176
6180
  })
6177
6181
  );
@@ -6185,6 +6189,8 @@ export default class Meeting extends StatelessWebexPlugin {
6185
6189
  * @returns {Promise}
6186
6190
  */
6187
6191
  public disconnectPhoneAudio() {
6192
+ const correlationToClear = this.pstnCorrelationId;
6193
+
6188
6194
  return Promise.all([
6189
6195
  this.isPhoneProvisioned(this.dialInDeviceStatus)
6190
6196
  ? MeetingUtil.disconnectPhoneAudio(this, this.dialInUrl)
@@ -6192,7 +6198,11 @@ export default class Meeting extends StatelessWebexPlugin {
6192
6198
  this.isPhoneProvisioned(this.dialOutDeviceStatus)
6193
6199
  ? MeetingUtil.disconnectPhoneAudio(this, this.dialOutUrl)
6194
6200
  : Promise.resolve(),
6195
- ]);
6201
+ ]).then(() => {
6202
+ if (this.pstnCorrelationId === correlationToClear) {
6203
+ this.pstnCorrelationId = undefined;
6204
+ }
6205
+ });
6196
6206
  }
6197
6207
 
6198
6208
  /**
@@ -8414,6 +8424,10 @@ export default class Meeting extends StatelessWebexPlugin {
8414
8424
  }
8415
8425
 
8416
8426
  if (whiteboard) {
8427
+ // @ts-ignore
8428
+ this.webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp({
8429
+ key: 'internal.client.share.initiated',
8430
+ });
8417
8431
  // @ts-ignore
8418
8432
  this.webex.internal.newMetrics.submitClientEvent({
8419
8433
  name: 'client.share.initiated',
@@ -8473,11 +8487,17 @@ export default class Meeting extends StatelessWebexPlugin {
8473
8487
  const whiteboard = this.locusInfo.mediaShares.find((element) => element.name === 'whiteboard');
8474
8488
 
8475
8489
  if (whiteboard) {
8490
+ // @ts-ignore
8491
+ this.webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp({
8492
+ key: 'internal.client.share.stopped',
8493
+ });
8476
8494
  // @ts-ignore
8477
8495
  this.webex.internal.newMetrics.submitClientEvent({
8478
8496
  name: 'client.share.stopped',
8479
8497
  payload: {
8480
8498
  mediaType: 'whiteboard',
8499
+ // @ts-ignore
8500
+ shareDuration: this.webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration(),
8481
8501
  },
8482
8502
  options: {
8483
8503
  meetingId: this.id,
@@ -8635,12 +8655,18 @@ export default class Meeting extends StatelessWebexPlugin {
8635
8655
  }
8636
8656
  this.screenShareFloorState = ScreenShareFloorStatus.RELEASED;
8637
8657
  if (content) {
8658
+ // @ts-ignore
8659
+ this.webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp({
8660
+ key: 'internal.client.share.stopped',
8661
+ });
8638
8662
  // @ts-ignore
8639
8663
  this.webex.internal.newMetrics.submitClientEvent({
8640
8664
  name: 'client.share.stopped',
8641
8665
  payload: {
8642
8666
  mediaType: 'share',
8643
8667
  shareInstanceId: this.localShareInstanceId,
8668
+ // @ts-ignore
8669
+ shareDuration: this.webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration(),
8644
8670
  },
8645
8671
  options: {meetingId: this.id},
8646
8672
  });
@@ -9238,8 +9264,6 @@ export default class Meeting extends StatelessWebexPlugin {
9238
9264
 
9239
9265
  // @ts-ignore - fix types
9240
9266
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
9241
- // @ts-ignore - Fix type
9242
- this.webex.internal.llm.off('event:locus.state_message', this.processLocusLLMEvent);
9243
9267
  };
9244
9268
 
9245
9269
  /**
@@ -9619,6 +9643,11 @@ export default class Meeting extends StatelessWebexPlugin {
9619
9643
  this.shareCAEventSentStatus.transmitStart = false;
9620
9644
  this.shareCAEventSentStatus.transmitStop = false;
9621
9645
 
9646
+ // @ts-ignore
9647
+ this.webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp({
9648
+ key: 'internal.client.share.initiated',
9649
+ });
9650
+
9622
9651
  // @ts-ignore
9623
9652
  this.webex.internal.newMetrics.submitClientEvent({
9624
9653
  name: 'client.share.initiated',
@@ -291,14 +291,18 @@ export class MuteState {
291
291
  );
292
292
 
293
293
  return MeetingUtil.remoteUpdateAudioVideo(meeting, audioMuted, videoMuted)
294
- .then((response) => {
294
+ .then((locus) => {
295
295
  LoggerProxy.logger.info(
296
296
  `Meeting:muteState#sendLocalMuteRequestToServer --> ${this.type}: local mute (audio=${audioMuted}, video=${videoMuted}) applied to server`
297
297
  );
298
298
 
299
299
  this.state.server.localMute = this.type === AUDIO ? audioMuted : videoMuted;
300
300
 
301
- return MeetingUtil.updateLocusFromApiResponse(meeting, response);
301
+ if (locus) {
302
+ meeting.locusInfo.handleLocusDelta(locus, meeting);
303
+ }
304
+
305
+ return locus;
302
306
  })
303
307
  .catch((remoteUpdateError) => {
304
308
  LoggerProxy.logger.warn(
@@ -0,0 +1,9 @@
1
+ export type Invitee = {
2
+ memberId: string;
3
+ emailAddress: string;
4
+ email: string;
5
+ phoneNumber: string;
6
+ roles: Array<string>;
7
+ skipEmailValidation?: boolean;
8
+ isInternalNumber?: boolean;
9
+ };
@@ -31,7 +31,6 @@ 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;
35
34
  parsed.mediaConnections = response.body.mediaConnections;
36
35
  parsed.locusUrl = parsed.locus.url;
37
36
  parsed.locusId = parsed.locus.url.split('/').pop();
@@ -60,16 +59,18 @@ const MeetingUtil = {
60
59
  );
61
60
  }
62
61
 
63
- return meeting.locusMediaRequest.send({
64
- type: 'LocalMute',
65
- selfUrl: meeting.selfUrl,
66
- mediaId: meeting.mediaId,
67
- sequence: meeting.locusInfo.sequence,
68
- muteOptions: {
69
- audioMuted,
70
- videoMuted,
71
- },
72
- });
62
+ return meeting.locusMediaRequest
63
+ .send({
64
+ type: 'LocalMute',
65
+ selfUrl: meeting.selfUrl,
66
+ mediaId: meeting.mediaId,
67
+ sequence: meeting.locusInfo.sequence,
68
+ muteOptions: {
69
+ audioMuted,
70
+ videoMuted,
71
+ },
72
+ })
73
+ .then((response) => response?.body?.locus);
73
74
  },
74
75
 
75
76
  hasOwner: (info) => info && info.owner,
@@ -196,6 +197,17 @@ const MeetingUtil = {
196
197
  });
197
198
 
198
199
  return parsed;
200
+ })
201
+ .catch((err) => {
202
+ webex.internal.newMetrics.submitClientEvent({
203
+ name: 'client.locus.join.response',
204
+ payload: {
205
+ identifiers: {meetingLookupUrl: meeting.meetingInfo?.meetingLookupUrl},
206
+ },
207
+ options: {meetingId: meeting.id, rawError: err},
208
+ });
209
+
210
+ throw err;
199
211
  });
200
212
  },
201
213
 
@@ -207,6 +219,10 @@ const MeetingUtil = {
207
219
  meeting.simultaneousInterpretation.cleanUp();
208
220
  meeting.locusMediaRequest = undefined;
209
221
 
222
+ meeting.webex?.internal?.newMetrics?.callDiagnosticMetrics?.clearEventLimitsForCorrelationId(
223
+ meeting.correlationId
224
+ );
225
+
210
226
  // make sure we send last metrics before we close the peerconnection
211
227
  const stopStatsAnalyzer = meeting.statsAnalyzer
212
228
  ? meeting.statsAnalyzer.stopAnalyzer()
@@ -327,10 +343,57 @@ const MeetingUtil = {
327
343
  meeting.resourceId = meeting.resourceId || options.resourceId;
328
344
 
329
345
  if (meeting.requiredCaptcha) {
330
- return Promise.reject(new CaptchaError());
346
+ const errorToThrow = new CaptchaError();
347
+
348
+ // @ts-ignore
349
+ webex.internal.newMetrics.submitClientEvent({
350
+ name: 'client.meetinginfo.response',
351
+ options: {
352
+ meetingId: meeting.id,
353
+ },
354
+ payload: {
355
+ errors: [
356
+ {
357
+ fatal: false,
358
+ category: 'expected',
359
+ name: 'other',
360
+ shownToUser: false,
361
+ errorCode: errorToThrow.code,
362
+ errorDescription: errorToThrow.name,
363
+ rawErrorMessage: errorToThrow.sdkMessage,
364
+ },
365
+ ],
366
+ },
367
+ });
368
+
369
+ return Promise.reject(errorToThrow);
331
370
  }
371
+
332
372
  if (meeting.passwordStatus === PASSWORD_STATUS.REQUIRED) {
333
- return Promise.reject(new PasswordError());
373
+ const errorToThrow = new PasswordError();
374
+
375
+ // @ts-ignore
376
+ webex.internal.newMetrics.submitClientEvent({
377
+ name: 'client.meetinginfo.response',
378
+ options: {
379
+ meetingId: meeting.id,
380
+ },
381
+ payload: {
382
+ errors: [
383
+ {
384
+ fatal: false,
385
+ category: 'expected',
386
+ name: 'other',
387
+ shownToUser: false,
388
+ errorCode: errorToThrow.code,
389
+ errorDescription: errorToThrow.name,
390
+ rawErrorMessage: errorToThrow.sdkMessage,
391
+ },
392
+ ],
393
+ },
394
+ });
395
+
396
+ return Promise.reject(errorToThrow);
334
397
  }
335
398
 
336
399
  if (options.pin) {
@@ -541,6 +604,15 @@ const MeetingUtil = {
541
604
  canStartManualCaption: (displayHints) =>
542
605
  displayHints.includes(DISPLAY_HINTS.MANUAL_CAPTION_START),
543
606
 
607
+ isLocalRecordingStarted: (displayHints) =>
608
+ displayHints.includes(DISPLAY_HINTS.LOCAL_RECORDING_STATUS_STARTED),
609
+
610
+ isLocalRecordingStopped: (displayHints) =>
611
+ displayHints.includes(DISPLAY_HINTS.LOCAL_RECORDING_STATUS_STOPPED),
612
+
613
+ isLocalRecordingPaused: (displayHints) =>
614
+ displayHints.includes(DISPLAY_HINTS.LOCAL_RECORDING_STATUS_PAUSED),
615
+
544
616
  canStopManualCaption: (displayHints) => displayHints.includes(DISPLAY_HINTS.MANUAL_CAPTION_STOP),
545
617
 
546
618
  isManualCaptionActive: (displayHints) =>
@@ -601,20 +673,22 @@ const MeetingUtil = {
601
673
  },
602
674
 
603
675
  /**
604
- * Updates the locus info for the meeting with the locus
605
- * information returned from API requests made to Locus
676
+ * Updates the locus info for the meeting with the delta locus
677
+ * returned from requests that include the sequence information
606
678
  * Returns the original response object
607
679
  * @param {Object} meeting The meeting object
608
680
  * @param {Object} response The response of the http request
609
681
  * @returns {Object}
610
682
  */
611
- updateLocusFromApiResponse: (meeting, response) => {
683
+ updateLocusWithDelta: (meeting, response) => {
612
684
  if (!meeting) {
613
685
  return response;
614
686
  }
615
687
 
616
- if (response?.body?.locus) {
617
- meeting.locusInfo.handleLocusAPIResponse(meeting, response.body);
688
+ const locus = response?.body?.locus;
689
+
690
+ if (locus) {
691
+ meeting.locusInfo.handleLocusDelta(locus, meeting);
618
692
  }
619
693
 
620
694
  return response;
@@ -661,7 +735,7 @@ const MeetingUtil = {
661
735
 
662
736
  return meeting
663
737
  .request(options)
664
- .then((response) => MeetingUtil.updateLocusFromApiResponse(meeting, response));
738
+ .then((response) => MeetingUtil.updateLocusWithDelta(meeting, response));
665
739
  };
666
740
 
667
741
  return locusDeltaRequest;
@@ -66,7 +66,6 @@ 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 {DataSet} from '../hashTree/hashTreeParser';
70
69
 
71
70
  let mediaLogger;
72
71
 
@@ -408,13 +407,6 @@ export default class Meetings extends WebexPlugin {
408
407
  * @memberof Meetings
409
408
  */
410
409
  getCorrespondingMeetingByLocus(data) {
411
- if (data.eventType === 'locus.state_message' && data.stateElementsMessage?.locusUrl) {
412
- return this.meetingCollection.getByKey(
413
- MEETING_KEY.LOCUS_URL,
414
- data.stateElementsMessage.locusUrl
415
- );
416
- }
417
-
418
410
  // getting meeting by correlationId. This will happen for the new event
419
411
  // Either the locus
420
412
  // TODO : Add check for the callBack Address
@@ -453,10 +445,7 @@ export default class Meetings extends WebexPlugin {
453
445
  * @private
454
446
  * @memberof Meetings
455
447
  */
456
- private handleLocusEvent(
457
- data: {locusUrl: string; locus: any; dataSets?: DataSet[]},
458
- useRandomDelayForInfo = false
459
- ) {
448
+ private handleLocusEvent(data: {locusUrl: string; locus: any}, useRandomDelayForInfo = false) {
460
449
  let meeting = this.getCorrespondingMeetingByLocus(data);
461
450
 
462
451
  // Special case when locus has got replaced, This only happend once if a replace locus exists
@@ -535,7 +524,7 @@ export default class Meetings extends WebexPlugin {
535
524
  meeting = newMeeting;
536
525
 
537
526
  // It's a new meeting so initialize the locus data
538
- meeting.locusInfo.initialSetup(data.locus, data.dataSets);
527
+ meeting.locusInfo.initialSetup(data.locus);
539
528
  this.checkHandleBreakoutLocus(data.locus);
540
529
  })
541
530
  .catch((e) => {
@@ -1571,11 +1560,12 @@ export default class Meetings extends WebexPlugin {
1571
1560
  {
1572
1561
  // @ts-ignore
1573
1562
  parent: this.webex,
1563
+ },
1564
+ (newMeeting) => {
1565
+ this.meetingCollection.set(newMeeting);
1574
1566
  }
1575
1567
  );
1576
1568
 
1577
- this.meetingCollection.set(meeting);
1578
-
1579
1569
  try {
1580
1570
  // if no participant has joined the scheduled meeting (meaning meeting is not active) and we get a locusEvent,
1581
1571
  // it means the meeting will start in 5-6 min. In that case, we want to fetchMeetingInfo
@@ -1835,10 +1825,7 @@ export default class Meetings extends WebexPlugin {
1835
1825
  }
1836
1826
 
1837
1827
  const associateBreakoutLocus = this.breakoutLocusForHandleLater[existIndex];
1838
- this.handleLocusEvent({
1839
- locus: associateBreakoutLocus,
1840
- locusUrl: associateBreakoutLocus.url,
1841
- });
1828
+ this.handleLocusEvent({locus: associateBreakoutLocus, locusUrl: associateBreakoutLocus.url});
1842
1829
  this.breakoutLocusForHandleLater.splice(existIndex, 1);
1843
1830
  }
1844
1831
 
@@ -109,5 +109,4 @@ export interface Participant {
109
109
  status: ParticipantMediaStatus;
110
110
  type: string;
111
111
  url: ParticipantUrl;
112
- isRemoved: boolean; // JS-SDK internal field to indicate in updates when the participant is removed
113
112
  }