@webex/plugin-meetings 2.60.0-next.2 → 2.60.0-next.4

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.
@@ -178,6 +178,12 @@ export type AddMediaOptions = {
178
178
  allowMediaInLobby?: boolean; // allows adding media when in the lobby
179
179
  };
180
180
 
181
+ export type CallStateForMetrics = {
182
+ correlationId?: string;
183
+ joinTrigger?: string;
184
+ loginType?: string;
185
+ };
186
+
181
187
  export const MEDIA_UPDATE_TYPE = {
182
188
  TRANSCODED_MEDIA_CONNECTION: 'TRANSCODED_MEDIA_CONNECTION',
183
189
  SHARE_FLOOR_REQUEST: 'SHARE_FLOOR_REQUEST',
@@ -470,7 +476,7 @@ export default class Meeting extends StatelessWebexPlugin {
470
476
  annotation: any;
471
477
  webinar: any;
472
478
  conversationUrl: string;
473
- correlationId: string;
479
+ callStateForMetrics: CallStateForMetrics;
474
480
  destination: string;
475
481
  destinationType: string;
476
482
  deviceUrl: string;
@@ -538,6 +544,7 @@ export default class Meeting extends StatelessWebexPlugin {
538
544
  requiredCaptcha: any;
539
545
  receiveSlotManager: ReceiveSlotManager;
540
546
  selfUserPolicies: any;
547
+ enforceVBGImagesURL: string;
541
548
  shareStatus: string;
542
549
  screenShareFloorState: ScreenShareFloorStatus;
543
550
  statsAnalyzer: StatsAnalyzer;
@@ -612,20 +619,22 @@ export default class Meeting extends StatelessWebexPlugin {
612
619
  */
613
620
  this.id = uuid.v4();
614
621
  /**
615
- * Correlation ID used for network tracking of meeting
622
+ * Call state used for metrics
616
623
  * @instance
617
- * @type {String}
624
+ * @type {CallStateForMetrics}
618
625
  * @readonly
619
626
  * @public
620
627
  * @memberof Meeting
621
628
  */
622
- if (attrs.correlationId) {
629
+ this.callStateForMetrics = attrs.callStateForMetrics || {};
630
+ const correlationId = attrs.correlationId || attrs.callStateForMetrics?.correlationId;
631
+ if (correlationId) {
623
632
  LoggerProxy.logger.log(
624
- `Meetings:index#constructor --> Initializing the meeting object with correlation id from app ${this.correlationId}`
633
+ `Meetings:index#constructor --> Initializing the meeting object with correlation id from app ${correlationId}`
625
634
  );
626
- this.correlationId = attrs.correlationId;
635
+ this.callStateForMetrics.correlationId = correlationId;
627
636
  } else {
628
- this.correlationId = this.id;
637
+ this.callStateForMetrics.correlationId = this.id;
629
638
  }
630
639
  /**
631
640
  * @instance
@@ -1361,6 +1370,22 @@ export default class Meeting extends StatelessWebexPlugin {
1361
1370
  return this.type === 'CALL';
1362
1371
  }
1363
1372
 
1373
+ /**
1374
+ * Getter - Returns callStateForMetrics.correlationId
1375
+ * @returns {string}
1376
+ */
1377
+ get correlationId() {
1378
+ return this.callStateForMetrics.correlationId;
1379
+ }
1380
+
1381
+ /**
1382
+ * Setter - sets callStateForMetrics.correlationId
1383
+ * @param {string} correlationId
1384
+ */
1385
+ set correlationId(correlationId: string) {
1386
+ this.callStateForMetrics.correlationId = correlationId;
1387
+ }
1388
+
1364
1389
  /**
1365
1390
  * Internal method for fetching meeting info
1366
1391
  *
@@ -1502,15 +1527,21 @@ export default class Meeting extends StatelessWebexPlugin {
1502
1527
  : this.destination;
1503
1528
  const destinationType = isStartingSpaceInstantV2Meeting ? _MEETING_LINK_ : this.destinationType;
1504
1529
 
1505
- const timeLeft = this.getPermissionTokenTimeLeftInSec();
1530
+ const permissionTokenExpiryInfo = this.getPermissionTokenExpiryInfo();
1531
+
1532
+ const timeLeft = permissionTokenExpiryInfo?.timeLeft;
1533
+ const expiryTime = permissionTokenExpiryInfo?.expiryTime;
1534
+ const currentTime = permissionTokenExpiryInfo?.currentTime;
1506
1535
 
1507
1536
  LoggerProxy.logger.info(
1508
- `Meeting:index#refreshPermissionToken --> refreshing permission token, destinationType=${destinationType}, timeLeft=${timeLeft}, reason=${reason}`
1537
+ `Meeting:index#refreshPermissionToken --> refreshing permission token, destinationType=${destinationType}, timeLeft=${timeLeft}, permissionTokenExpiry=${expiryTime}, currentTimestamp=${currentTime},reason=${reason}`
1509
1538
  );
1510
1539
 
1511
1540
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.PERMISSION_TOKEN_REFRESH, {
1512
1541
  correlationId: this.correlationId,
1513
1542
  timeLeft,
1543
+ expiryTime,
1544
+ currentTime,
1514
1545
  reason,
1515
1546
  destinationType,
1516
1547
  });
@@ -3286,6 +3317,11 @@ export default class Meeting extends StatelessWebexPlugin {
3286
3317
  }) &&
3287
3318
  this.meetingInfo?.video?.supportHDV) ||
3288
3319
  !this.arePolicyRestrictionsSupported(),
3320
+ enforceVirtualBackground:
3321
+ ControlsOptionsUtil.hasPolicies({
3322
+ requiredPolicies: [SELF_POLICY.ENFORCE_VIRTUAL_BACKGROUND],
3323
+ policies: this.selfUserPolicies,
3324
+ }) && this.arePolicyRestrictionsSupported(),
3289
3325
  supportHQV:
3290
3326
  (ControlsOptionsUtil.hasPolicies({
3291
3327
  requiredPolicies: [SELF_POLICY.SUPPORT_HQV],
@@ -3502,6 +3538,7 @@ export default class Meeting extends StatelessWebexPlugin {
3502
3538
  */
3503
3539
  setSelfUserPolicies() {
3504
3540
  this.selfUserPolicies = this.permissionTokenPayload?.permission?.userPolicies;
3541
+ this.enforceVBGImagesURL = this.permissionTokenPayload?.permission?.enforceVBGImagesURL;
3505
3542
  }
3506
3543
 
3507
3544
  /**
@@ -3605,8 +3642,7 @@ export default class Meeting extends StatelessWebexPlugin {
3605
3642
  * @memberof Meeting
3606
3643
  */
3607
3644
  closeRemoteStreams() {
3608
- const {remoteAudioStream, remoteVideoStream, remoteShareStream, shareAudioStream} =
3609
- this.mediaProperties;
3645
+ const {remoteAudioStream, remoteVideoStream, remoteShareStream} = this.mediaProperties;
3610
3646
 
3611
3647
  /**
3612
3648
  * Triggers an event to the developer
@@ -3647,7 +3683,6 @@ export default class Meeting extends StatelessWebexPlugin {
3647
3683
  stopStream(remoteAudioStream, EVENT_TYPES.REMOTE_AUDIO),
3648
3684
  stopStream(remoteVideoStream, EVENT_TYPES.REMOTE_VIDEO),
3649
3685
  stopStream(remoteShareStream, EVENT_TYPES.REMOTE_SHARE),
3650
- stopStream(shareAudioStream, EVENT_TYPES.REMOTE_SHARE_AUDIO),
3651
3686
  ]);
3652
3687
  }
3653
3688
 
@@ -3718,11 +3753,16 @@ export default class Meeting extends StatelessWebexPlugin {
3718
3753
  private async setLocalShareVideoStream(localDisplayStream?: LocalDisplayStream) {
3719
3754
  const oldStream = this.mediaProperties.shareVideoStream;
3720
3755
 
3756
+ oldStream?.off(StreamEventNames.MuteStateChange, this.handleShareVideoStreamMuteStateChange);
3721
3757
  oldStream?.off(StreamEventNames.Ended, this.handleShareVideoStreamEnded);
3722
3758
  oldStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
3723
3759
 
3724
3760
  this.mediaProperties.setLocalShareVideoStream(localDisplayStream);
3725
3761
 
3762
+ localDisplayStream?.on(
3763
+ StreamEventNames.MuteStateChange,
3764
+ this.handleShareVideoStreamMuteStateChange
3765
+ );
3726
3766
  localDisplayStream?.on(StreamEventNames.Ended, this.handleShareVideoStreamEnded);
3727
3767
  localDisplayStream?.on(
3728
3768
  LocalStreamEventNames.OutputTrackChange,
@@ -3778,7 +3818,7 @@ export default class Meeting extends StatelessWebexPlugin {
3778
3818
  functionName: string;
3779
3819
  isPublished: boolean;
3780
3820
  mediaType: MediaType;
3781
- stream: MediaStream;
3821
+ stream: LocalStream;
3782
3822
  }) {
3783
3823
  const {functionName, isPublished, mediaType, stream} = options;
3784
3824
  Trigger.trigger(
@@ -3812,12 +3852,16 @@ export default class Meeting extends StatelessWebexPlugin {
3812
3852
  videoStream?.off(StreamEventNames.MuteStateChange, this.localVideoStreamMuteStateHandler);
3813
3853
  videoStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
3814
3854
 
3815
- shareAudioStream?.off(StreamEventNames.MuteStateChange, this.handleShareAudioStreamEnded);
3855
+ shareAudioStream?.off(StreamEventNames.Ended, this.handleShareAudioStreamEnded);
3816
3856
  shareAudioStream?.off(
3817
3857
  LocalStreamEventNames.OutputTrackChange,
3818
3858
  this.localOutputTrackChangeHandler
3819
3859
  );
3820
- shareVideoStream?.off(StreamEventNames.MuteStateChange, this.handleShareVideoStreamEnded);
3860
+ shareVideoStream?.off(
3861
+ StreamEventNames.MuteStateChange,
3862
+ this.handleShareVideoStreamMuteStateChange
3863
+ );
3864
+ shareVideoStream?.off(StreamEventNames.Ended, this.handleShareVideoStreamEnded);
3821
3865
  shareVideoStream?.off(
3822
3866
  LocalStreamEventNames.OutputTrackChange,
3823
3867
  this.localOutputTrackChangeHandler
@@ -3959,14 +4003,25 @@ export default class Meeting extends StatelessWebexPlugin {
3959
4003
  }
3960
4004
 
3961
4005
  /**
3962
- * Convenience method to set the correlation id for the Meeting
3963
- * @param {String} id correlation id to set on the class
4006
+ * Convenience method to set the correlation id for the callStateForMetrics
4007
+ * @param {String} id correlation id to set on the callStateForMetrics
3964
4008
  * @returns {undefined}
3965
4009
  * @public
3966
4010
  * @memberof Meeting
3967
4011
  */
3968
4012
  public setCorrelationId(id: string) {
3969
- this.correlationId = id;
4013
+ this.callStateForMetrics.correlationId = id;
4014
+ }
4015
+
4016
+ /**
4017
+ * Update the callStateForMetrics
4018
+ * @param {CallStateForMetrics} callStateForMetrics updated values for callStateForMetrics
4019
+ * @returns {undefined}
4020
+ * @public
4021
+ * @memberof Meeting
4022
+ */
4023
+ public updateCallStateForMetrics(callStateForMetrics: CallStateForMetrics) {
4024
+ this.callStateForMetrics = {...this.callStateForMetrics, ...callStateForMetrics};
3970
4025
  }
3971
4026
 
3972
4027
  /**
@@ -4618,7 +4673,7 @@ export default class Meeting extends StatelessWebexPlugin {
4618
4673
  this.webex.internal.newMetrics.submitClientEvent({
4619
4674
  name: 'client.call.initiated',
4620
4675
  payload: {
4621
- trigger: 'user-interaction',
4676
+ trigger: this.callStateForMetrics.joinTrigger || 'user-interaction',
4622
4677
  isRoapCallEnabled: true,
4623
4678
  pstnAudioType: options?.pstnAudioType,
4624
4679
  },
@@ -6185,7 +6240,7 @@ export default class Meeting extends StatelessWebexPlugin {
6185
6240
  const LOG_HEADER = 'Meeting:index#addMedia -->';
6186
6241
  LoggerProxy.logger.info(`${LOG_HEADER} called with: ${JSON.stringify(options)}`);
6187
6242
 
6188
- if (this.meetingState !== FULL_STATE.ACTIVE) {
6243
+ if (options.allowMediaInLobby !== true && this.meetingState !== FULL_STATE.ACTIVE) {
6189
6244
  throw new MeetingNotActiveError();
6190
6245
  }
6191
6246
 
@@ -6920,6 +6975,11 @@ export default class Meeting extends StatelessWebexPlugin {
6920
6975
  .then(() => {
6921
6976
  this.screenShareFloorState = ScreenShareFloorStatus.GRANTED;
6922
6977
 
6978
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_SHARE_SUCCESS, {
6979
+ correlation_id: this.correlationId,
6980
+ locus_id: this.locusUrl.split('/').pop(),
6981
+ });
6982
+
6923
6983
  return Promise.resolve();
6924
6984
  })
6925
6985
  .catch((error) => {
@@ -7341,6 +7401,23 @@ export default class Meeting extends StatelessWebexPlugin {
7341
7401
  }
7342
7402
  };
7343
7403
 
7404
+ /**
7405
+ * Functionality for when a share video is muted or unmuted.
7406
+ * @private
7407
+ * @memberof Meeting
7408
+ * @param {boolean} muted
7409
+ * @returns {undefined}
7410
+ */
7411
+ private handleShareVideoStreamMuteStateChange = (muted: boolean) => {
7412
+ LoggerProxy.logger.log(
7413
+ `Meeting:index#handleShareVideoStreamMuteStateChange --> Share video stream mute state changed to muted ${muted}`
7414
+ );
7415
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE, {
7416
+ correlationId: this.correlationId,
7417
+ muted,
7418
+ });
7419
+ };
7420
+
7344
7421
  /**
7345
7422
  * Functionality for when a share video is ended.
7346
7423
  * @private
@@ -7707,10 +7784,12 @@ export default class Meeting extends StatelessWebexPlugin {
7707
7784
  .update({
7708
7785
  // TODO: RoapMediaConnection is not ready to use stream classes yet, so we pass the raw MediaStreamTrack for now
7709
7786
  localTracks: {
7710
- audio: this.mediaProperties.audioStream?.outputTrack || null,
7711
- video: this.mediaProperties.videoStream?.outputTrack || null,
7712
- screenShareVideo: this.mediaProperties.shareVideoStream?.outputTrack || null,
7713
- screenShareAudio: this.mediaProperties.shareAudioStream?.outputTrack || null,
7787
+ audio: this.mediaProperties.audioStream?.outputStream?.getTracks()[0] || null,
7788
+ video: this.mediaProperties.videoStream?.outputStream?.getTracks()[0] || null,
7789
+ screenShareVideo:
7790
+ this.mediaProperties.shareVideoStream?.outputStream?.getTracks()[0] || null,
7791
+ screenShareAudio:
7792
+ this.mediaProperties.shareAudioStream?.outputStream?.getTracks()[0] || null,
7714
7793
  },
7715
7794
  direction: {
7716
7795
  audio: Media.getDirection(
@@ -7899,12 +7978,12 @@ export default class Meeting extends StatelessWebexPlugin {
7899
7978
  }
7900
7979
 
7901
7980
  /**
7902
- * Gets the time left in seconds till the permission token expires
7981
+ * Gets permission token expiry information including timeLeft, expiryTime, currentTime
7903
7982
  * (from the time the function has been fired)
7904
7983
  *
7905
- * @returns {number} time left in seconds
7984
+ * @returns {object} containing timeLeft, expiryTime, currentTime
7906
7985
  */
7907
- public getPermissionTokenTimeLeftInSec(): number | undefined {
7986
+ public getPermissionTokenExpiryInfo() {
7908
7987
  if (!this.permissionTokenPayload) {
7909
7988
  return undefined;
7910
7989
  }
@@ -7917,7 +7996,9 @@ export default class Meeting extends StatelessWebexPlugin {
7917
7996
 
7918
7997
  // substract current time from the permissionTokenExp
7919
7998
  // (permissionTokenExp is a epoch timestamp, not a time to live duration)
7920
- return (permissionTokenExpValue - now) / 1000;
7999
+ const timeLeft = (permissionTokenExpValue - now) / 1000;
8000
+
8001
+ return {timeLeft, expiryTime: permissionTokenExpValue, currentTime: now};
7921
8002
  }
7922
8003
 
7923
8004
  /**
@@ -7929,9 +8010,9 @@ export default class Meeting extends StatelessWebexPlugin {
7929
8010
  * @returns {Promise<void>}
7930
8011
  */
7931
8012
  public checkAndRefreshPermissionToken(threshold: number, reason: string): Promise<void> {
7932
- const permissionTokenTimeLeft = this.getPermissionTokenTimeLeftInSec();
8013
+ const timeLeft = this.getPermissionTokenExpiryInfo()?.timeLeft;
7933
8014
 
7934
- if (permissionTokenTimeLeft !== undefined && permissionTokenTimeLeft <= threshold) {
8015
+ if (timeLeft !== undefined && timeLeft <= threshold) {
7935
8016
  return this.refreshPermissionToken(reason);
7936
8017
  }
7937
8018
 
@@ -49,7 +49,7 @@ import {
49
49
  import BEHAVIORAL_METRICS from '../metrics/constants';
50
50
  import MeetingInfo from '../meeting-info';
51
51
  import MeetingInfoV2 from '../meeting-info/meeting-info-v2';
52
- import Meeting from '../meeting';
52
+ import Meeting, {CallStateForMetrics} from '../meeting';
53
53
  import PersonalMeetingRoom from '../personal-meeting-room';
54
54
  import Reachability from '../reachability';
55
55
  import Request from './request';
@@ -1025,13 +1025,14 @@ export default class Meetings extends WebexPlugin {
1025
1025
  }
1026
1026
 
1027
1027
  /**
1028
- * Create a meeting.
1028
+ * Create a meeting or return an existing meeting.
1029
1029
  * @param {string} destination - sipURL, phonenumber, or locus object}
1030
1030
  * @param {string} [type] - the optional specified type, such as locusId
1031
1031
  * @param {Boolean} useRandomDelayForInfo - whether a random delay should be added to fetching meeting info
1032
1032
  * @param {Object} infoExtraParams extra parameters to be provided when fetching meeting info
1033
- * @param {string} correlationId - the optional specified correlationId
1033
+ * @param {string} correlationId - the optional specified correlationId (callStateForMetrics.correlationId can be provided instead)
1034
1034
  * @param {Boolean} failOnMissingMeetingInfo - whether to throw an error if meeting info fails to fetch (for calls that are not 1:1 or content share)
1035
+ * @param {CallStateForMetrics} callStateForMetrics - information about call state for metrics
1035
1036
  * @returns {Promise<Meeting>} A new Meeting.
1036
1037
  * @public
1037
1038
  * @memberof Meetings
@@ -1042,7 +1043,8 @@ export default class Meetings extends WebexPlugin {
1042
1043
  useRandomDelayForInfo = false,
1043
1044
  infoExtraParams = {},
1044
1045
  correlationId: string = undefined,
1045
- failOnMissingMeetingInfo = false
1046
+ failOnMissingMeetingInfo = false,
1047
+ callStateForMetrics: CallStateForMetrics = undefined
1046
1048
  ) {
1047
1049
  // TODO: type should be from a dictionary
1048
1050
 
@@ -1050,6 +1052,10 @@ export default class Meetings extends WebexPlugin {
1050
1052
  // type. This must be performed prior to determining if the meeting is
1051
1053
  // found in the collection, as we mutate the destination for hydra person
1052
1054
  // id values.
1055
+ if (correlationId) {
1056
+ callStateForMetrics = {...(callStateForMetrics || {}), correlationId};
1057
+ }
1058
+
1053
1059
  return (
1054
1060
  this.meetingInfo
1055
1061
  .fetchInfoOptions(destination, type)
@@ -1096,7 +1102,7 @@ export default class Meetings extends WebexPlugin {
1096
1102
  type,
1097
1103
  useRandomDelayForInfo,
1098
1104
  infoExtraParams,
1099
- correlationId,
1105
+ callStateForMetrics,
1100
1106
  failOnMissingMeetingInfo
1101
1107
  ).then((createdMeeting: any) => {
1102
1108
  // If the meeting was successfully created.
@@ -1143,6 +1149,7 @@ export default class Meetings extends WebexPlugin {
1143
1149
  return Promise.resolve(createdMeeting);
1144
1150
  });
1145
1151
  }
1152
+ meeting.setCallStateForMetrics(callStateForMetrics);
1146
1153
 
1147
1154
  // Return the existing meeting.
1148
1155
  return Promise.resolve(meeting);
@@ -1155,7 +1162,7 @@ export default class Meetings extends WebexPlugin {
1155
1162
  * @param {String} type see create()
1156
1163
  * @param {Boolean} useRandomDelayForInfo whether a random delay should be added to fetching meeting info
1157
1164
  * @param {Object} infoExtraParams extra parameters to be provided when fetching meeting info
1158
- * @param {String} correlationId the optional specified correlationId
1165
+ * @param {CallStateForMetrics} callStateForMetrics - information about call state for metrics
1159
1166
  * @param {Boolean} failOnMissingMeetingInfo - whether to throw an error if meeting info fails to fetch (for calls that are not 1:1 or content share)
1160
1167
  * @returns {Promise} a new meeting instance complete with meeting info and destination
1161
1168
  * @private
@@ -1166,7 +1173,7 @@ export default class Meetings extends WebexPlugin {
1166
1173
  type: string = null,
1167
1174
  useRandomDelayForInfo = false,
1168
1175
  infoExtraParams = {},
1169
- correlationId: string = undefined,
1176
+ callStateForMetrics: CallStateForMetrics = undefined,
1170
1177
  failOnMissingMeetingInfo = false
1171
1178
  ) {
1172
1179
  const meeting = new Meeting(
@@ -1181,7 +1188,7 @@ export default class Meetings extends WebexPlugin {
1181
1188
  meetingInfoProvider: this.meetingInfo,
1182
1189
  destination,
1183
1190
  destinationType: type,
1184
- correlationId,
1191
+ callStateForMetrics,
1185
1192
  },
1186
1193
  {
1187
1194
  // @ts-ignore
@@ -1219,7 +1226,7 @@ export default class Meetings extends WebexPlugin {
1219
1226
  () =>
1220
1227
  meeting.fetchMeetingInfo({
1221
1228
  extraParams: infoExtraParams,
1222
- sendCAevents: !!correlationId, // if client sends correlation id as argument of public create(), then it means that this meeting creation is part of a pre-join intent from user
1229
+ sendCAevents: !!callStateForMetrics?.correlationId, // if client sends correlation id as argument of public create(), then it means that this meeting creation is part of a pre-join intent from user
1223
1230
  }),
1224
1231
  waitingTime
1225
1232
  );
@@ -1227,7 +1234,7 @@ export default class Meetings extends WebexPlugin {
1227
1234
  } else {
1228
1235
  await meeting.fetchMeetingInfo({
1229
1236
  extraParams: infoExtraParams,
1230
- sendCAevents: !!correlationId, // if client sends correlation id as argument of public create(), then it means that this meeting creation is part of a pre-join intent from user
1237
+ sendCAevents: !!callStateForMetrics?.correlationId, // if client sends correlation id as argument of public create(), then it means that this meeting creation is part of a pre-join intent from user
1231
1238
  });
1232
1239
  }
1233
1240
  } catch (err) {
@@ -26,9 +26,11 @@ const BEHAVIORAL_METRICS = {
26
26
  MEETING_MEDIA_INACTIVE: 'js_sdk_meeting_media_inactive',
27
27
  MEETING_RECONNECT_FAILURE: 'js_sdk_meeting_reconnect_failures',
28
28
  MEETING_MAX_REJOIN_FAILURE: 'js_sdk_meeting_max_rejoin_failure',
29
+ MEETING_SHARE_SUCCESS: 'js_sdk_meeting_share_success',
29
30
  MEETING_SHARE_FAILURE: 'js_sdk_meeting_share_failures',
30
31
  MEETING_START_WHITEBOARD_SHARE_FAILURE: 'js_sdk_meeting_start_whiteboard_share_failures',
31
32
  MEETING_STOP_WHITEBOARD_SHARE_FAILURE: 'js_sdk_meeting_stop_whiteboard_share_failures',
33
+ MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE: 'js_sdk_meeting_share_video_mute_state_change',
32
34
  MUTE_AUDIO_FAILURE: 'js_sdk_mute_audio_failures',
33
35
  MUTE_VIDEO_FAILURE: 'js_sdk_mute_video_failures',
34
36
  SET_MEETING_QUALITY_FAILURE: 'js_sdk_set_meeting_quality_failures',
@@ -17,11 +17,11 @@ import {
17
17
  RECONNECTION_STATE,
18
18
  } from '../constants';
19
19
  import BEHAVIORAL_METRICS from '../metrics/constants';
20
- import ReconnectionError from '../common/errors/reconnection';
21
20
  import ReconnectInProgress from '../common/errors/reconnection-in-progress';
22
21
  import Metrics from '../metrics';
23
22
  import Meeting from '../meeting';
24
23
  import {MediaRequestManager} from '../multistream/mediaRequestManager';
24
+ import ReconnectionError from '../common/errors/reconnection';
25
25
 
26
26
  /**
27
27
  * Used to indicate that the reconnect logic needs to be retried.
@@ -228,7 +228,6 @@ export default class ReconnectionManager {
228
228
  */
229
229
  public cleanUp() {
230
230
  this.reset();
231
- this.meeting = null;
232
231
  }
233
232
 
234
233
  /**
@@ -16,16 +16,32 @@ describe('createMediaConnection', () => {
16
16
  id: 'any fake track'
17
17
  }
18
18
  const fakeAudioStream = {
19
- outputTrack: fakeTrack,
19
+ outputStream: {
20
+ getTracks: () => {
21
+ return [fakeTrack];
22
+ }
23
+ }
20
24
  };
21
25
  const fakeVideoStream = {
22
- outputTrack: fakeTrack,
26
+ outputStream: {
27
+ getTracks: () => {
28
+ return [fakeTrack];
29
+ }
30
+ }
23
31
  };
24
32
  const fakeShareVideoStream = {
25
- outputTrack: fakeTrack,
33
+ outputStream: {
34
+ getTracks: () => {
35
+ return [fakeTrack];
36
+ }
37
+ }
26
38
  };
27
39
  const fakeShareAudioStream = {
28
- outputTrack: fakeTrack,
40
+ outputStream: {
41
+ getTracks: () => {
42
+ return [fakeTrack];
43
+ }
44
+ }
29
45
  };
30
46
  afterEach(() => {
31
47
  sinon.restore();
@@ -74,6 +74,7 @@ describe('plugin-meetings', () => {
74
74
  supportHQV: null,
75
75
  supportHDV: null,
76
76
  canShareWhiteBoard: null,
77
+ enforceVirtualBackground: null,
77
78
  ...expected,
78
79
  };
79
80
 
@@ -153,6 +154,7 @@ describe('plugin-meetings', () => {
153
154
  'supportHQV',
154
155
  'supportHDV',
155
156
  'canShareWhiteBoard',
157
+ 'enforceVirtualBackground',
156
158
  ].forEach((key) => {
157
159
  it(`get and set for ${key} work as expected`, () => {
158
160
  const inMeetingActions = new InMeetingActions();