@webex/plugin-meetings 3.8.0-next.33 → 3.8.0-next.35

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.
@@ -37,6 +37,7 @@ export default class LocusInfo extends EventsScope {
37
37
  services: any;
38
38
  resources: any;
39
39
  mainSessionLocusCache: any;
40
+ self: any;
40
41
  /**
41
42
  * Constructor
42
43
  * @param {function} updateMeeting callback to update the meeting object from an object
@@ -450,6 +450,12 @@ export default class Meeting extends StatelessWebexPlugin {
450
450
  allowMediaInLobby: boolean;
451
451
  localShareInstanceId: string;
452
452
  remoteShareInstanceId: string;
453
+ shareCAEventSentStatus: {
454
+ transmitStart: boolean;
455
+ transmitStop: boolean;
456
+ receiveStart: boolean;
457
+ receiveStop: boolean;
458
+ };
453
459
  turnDiscoverySkippedReason: TurnDiscoverySkipReason;
454
460
  turnServerUsed: boolean;
455
461
  areVoiceaEventsSetup: boolean;
@@ -458,7 +458,7 @@ var Webinar = _webexCore.WebexPlugin.extend({
458
458
  }, _callee7);
459
459
  }))();
460
460
  },
461
- version: "3.8.0-next.33"
461
+ version: "3.8.0-next.35"
462
462
  });
463
463
  var _default = exports.default = Webinar;
464
464
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -43,7 +43,7 @@
43
43
  "@webex/eslint-config-legacy": "0.0.0",
44
44
  "@webex/jest-config-legacy": "0.0.0",
45
45
  "@webex/legacy-tools": "0.0.0",
46
- "@webex/plugin-meetings": "3.8.0-next.33",
46
+ "@webex/plugin-meetings": "3.8.0-next.35",
47
47
  "@webex/plugin-rooms": "3.8.0-next.13",
48
48
  "@webex/test-helper-chai": "3.8.0-next.11",
49
49
  "@webex/test-helper-mocha": "3.8.0-next.11",
@@ -71,7 +71,7 @@
71
71
  "@webex/internal-plugin-metrics": "3.8.0-next.11",
72
72
  "@webex/internal-plugin-support": "3.8.0-next.13",
73
73
  "@webex/internal-plugin-user": "3.8.0-next.11",
74
- "@webex/internal-plugin-voicea": "3.8.0-next.33",
74
+ "@webex/internal-plugin-voicea": "3.8.0-next.35",
75
75
  "@webex/media-helpers": "3.8.0-next.12",
76
76
  "@webex/plugin-people": "3.8.0-next.13",
77
77
  "@webex/plugin-rooms": "3.8.0-next.13",
@@ -92,5 +92,5 @@
92
92
  "//": [
93
93
  "TODO: upgrade jwt-decode when moving to node 18"
94
94
  ],
95
- "version": "3.8.0-next.33"
95
+ "version": "3.8.0-next.35"
96
96
  }
@@ -1,5 +1,5 @@
1
1
  import {isEqual} from 'lodash';
2
- import {BREAKOUTS} from '../constants';
2
+ import {BREAKOUTS, MEETING_STATE} from '../constants';
3
3
 
4
4
  const ControlsUtils: any = {};
5
5
 
@@ -242,30 +242,42 @@ ControlsUtils.isNeedReplaceMembers = (oldControls: any, controls: any) => {
242
242
  }
243
243
 
244
244
  return (
245
- oldControls.breakout.groupId !== controls.breakout.groupId ||
246
- oldControls.breakout.sessionId !== controls.breakout.sessionId
245
+ oldControls?.breakout?.groupId !== controls?.breakout?.groupId ||
246
+ oldControls?.breakout?.sessionId !== controls?.breakout?.sessionId
247
247
  );
248
248
  };
249
249
 
250
250
  /**
251
251
  * determine the switch status between breakout session and main session.
252
- * @param {LocusControls} oldControls
253
- * @param {LocusControls} controls
252
+ * @param {LocusInfo} oldLocus
253
+ * @param {LocusInfo} newLocus
254
254
  * @returns {Object}
255
255
  */
256
- ControlsUtils.getSessionSwitchStatus = (oldControls: any, controls: any) => {
256
+ ControlsUtils.getSessionSwitchStatus = (oldLocus: any, newLocus: any) => {
257
257
  const status = {isReturnToMain: false, isJoinToBreakout: false};
258
258
  // no breakout case
259
- if (!oldControls?.breakout || !controls?.breakout) {
259
+ if (!oldLocus.controls?.breakout || !newLocus.controls?.breakout) {
260
260
  return status;
261
261
  }
262
262
 
263
- status.isReturnToMain =
264
- oldControls.breakout.sessionType === BREAKOUTS.SESSION_TYPES.BREAKOUT &&
265
- controls.breakout.sessionType === BREAKOUTS.SESSION_TYPES.MAIN;
263
+ // It is used to fix the timing issue triggered when the creator leaves session to ensure that the member list is complete
264
+ const needUseCache = !!(
265
+ oldLocus.self?.isCreator &&
266
+ newLocus.participants?.length === 1 &&
267
+ newLocus.participants?.[0].isCreator &&
268
+ newLocus.participants?.[0].state === MEETING_STATE.STATES.JOINED &&
269
+ newLocus.controls?.breakout?.sessionType === BREAKOUTS.SESSION_TYPES.MAIN &&
270
+ newLocus.controls?.breakout?.groups?.length
271
+ );
272
+
273
+ const isReturnToMain =
274
+ oldLocus.controls.breakout.sessionType === BREAKOUTS.SESSION_TYPES.BREAKOUT &&
275
+ newLocus.controls.breakout.sessionType === BREAKOUTS.SESSION_TYPES.MAIN;
276
+
277
+ status.isReturnToMain = needUseCache || isReturnToMain;
266
278
  status.isJoinToBreakout =
267
- oldControls.breakout.sessionType === BREAKOUTS.SESSION_TYPES.MAIN &&
268
- controls.breakout.sessionType === BREAKOUTS.SESSION_TYPES.BREAKOUT;
279
+ oldLocus.controls.breakout.sessionType === BREAKOUTS.SESSION_TYPES.MAIN &&
280
+ newLocus.controls.breakout.sessionType === BREAKOUTS.SESSION_TYPES.BREAKOUT;
269
281
 
270
282
  return status;
271
283
  };
@@ -17,7 +17,9 @@ import {
17
17
  MEETING_REMOVED_REASON,
18
18
  CALL_REMOVED_REASON,
19
19
  RECORDING_STATE,
20
+ BREAKOUTS,
20
21
  } from '../constants';
22
+
21
23
  import InfoUtils from './infoUtils';
22
24
  import FullState from './fullState';
23
25
  import SelfUtils from './selfUtils';
@@ -67,6 +69,7 @@ export default class LocusInfo extends EventsScope {
67
69
  services: any;
68
70
  resources: any;
69
71
  mainSessionLocusCache: any;
72
+ self: any;
70
73
  /**
71
74
  * Constructor
72
75
  * @param {function} updateMeeting callback to update the meeting object from an object
@@ -1706,7 +1709,8 @@ export default class LocusInfo extends EventsScope {
1706
1709
  * @memberof LocusInfo
1707
1710
  */
1708
1711
  getTheLocusToUpdate(newLocus: any) {
1709
- const switchStatus = ControlsUtils.getSessionSwitchStatus(this.controls, newLocus?.controls);
1712
+ const switchStatus = ControlsUtils.getSessionSwitchStatus(this, newLocus);
1713
+
1710
1714
  if (switchStatus.isReturnToMain && this.mainSessionLocusCache) {
1711
1715
  return cloneDeep(this.mainSessionLocusCache);
1712
1716
  }
@@ -647,6 +647,13 @@ export default class Meeting extends StatelessWebexPlugin {
647
647
  allowMediaInLobby: boolean;
648
648
  localShareInstanceId: string;
649
649
  remoteShareInstanceId: string;
650
+ shareCAEventSentStatus: {
651
+ transmitStart: boolean;
652
+ transmitStop: boolean;
653
+ receiveStart: boolean;
654
+ receiveStop: boolean;
655
+ };
656
+
650
657
  turnDiscoverySkippedReason: TurnDiscoverySkipReason;
651
658
  turnServerUsed: boolean;
652
659
  areVoiceaEventsSetup = false;
@@ -1419,6 +1426,19 @@ export default class Meeting extends StatelessWebexPlugin {
1419
1426
  */
1420
1427
  this.remoteShareInstanceId = null;
1421
1428
 
1429
+ /**
1430
+ * Status used for ensuring we do not oversend metrics
1431
+ * @instance
1432
+ * @private
1433
+ * @memberof Meeting
1434
+ */
1435
+ this.shareCAEventSentStatus = {
1436
+ transmitStart: false,
1437
+ transmitStop: false,
1438
+ receiveStart: false,
1439
+ receiveStop: false,
1440
+ };
1441
+
1422
1442
  /**
1423
1443
  * The class that helps to control recording functions: start, stop, pause, resume, etc
1424
1444
  * @instance
@@ -3023,6 +3043,8 @@ export default class Meeting extends StatelessWebexPlugin {
3023
3043
  case SHARE_STATUS.REMOTE_SHARE_ACTIVE: {
3024
3044
  const sendStartedSharingRemote = () => {
3025
3045
  this.remoteShareInstanceId = contentShare.shareInstanceId;
3046
+ this.shareCAEventSentStatus.receiveStart = false;
3047
+ this.shareCAEventSentStatus.receiveStop = false;
3026
3048
 
3027
3049
  Trigger.trigger(
3028
3050
  this,
@@ -3076,6 +3098,7 @@ export default class Meeting extends StatelessWebexPlugin {
3076
3098
  },
3077
3099
  options: {meetingId: this.id},
3078
3100
  });
3101
+
3079
3102
  break;
3080
3103
 
3081
3104
  case SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE:
@@ -3116,6 +3139,8 @@ export default class Meeting extends StatelessWebexPlugin {
3116
3139
  // if we got here, then some remote participant has stolen
3117
3140
  // the presentation from another remote participant
3118
3141
  this.remoteShareInstanceId = contentShare.shareInstanceId;
3142
+ this.shareCAEventSentStatus.receiveStart = false;
3143
+ this.shareCAEventSentStatus.receiveStop = false;
3119
3144
 
3120
3145
  Trigger.trigger(
3121
3146
  this,
@@ -6771,30 +6796,42 @@ export default class Meeting extends StatelessWebexPlugin {
6771
6796
  EVENT_TRIGGERS.MEETING_MEDIA_LOCAL_STARTED,
6772
6797
  data
6773
6798
  );
6774
- // @ts-ignore
6775
- this.webex.internal.newMetrics.submitClientEvent({
6776
- name: 'client.media.tx.start',
6777
- payload: {
6778
- mediaType: data.mediaType,
6779
- shareInstanceId: data.mediaType === 'share' ? this.localShareInstanceId : undefined,
6780
- },
6781
- options: {
6782
- meetingId: this.id,
6783
- },
6784
- });
6799
+ if (data.mediaType !== 'share' || !this.shareCAEventSentStatus.transmitStart) {
6800
+ // @ts-ignore
6801
+ this.webex.internal.newMetrics.submitClientEvent({
6802
+ name: 'client.media.tx.start',
6803
+ payload: {
6804
+ mediaType: data.mediaType,
6805
+ shareInstanceId: data.mediaType === 'share' ? this.localShareInstanceId : undefined,
6806
+ },
6807
+ options: {
6808
+ meetingId: this.id,
6809
+ },
6810
+ });
6811
+
6812
+ if (data.mediaType === 'share') {
6813
+ this.shareCAEventSentStatus.transmitStart = true;
6814
+ }
6815
+ }
6785
6816
  });
6786
6817
  this.statsAnalyzer.on(StatsAnalyzerEventNames.LOCAL_MEDIA_STOPPED, (data) => {
6787
- // @ts-ignore
6788
- this.webex.internal.newMetrics.submitClientEvent({
6789
- name: 'client.media.tx.stop',
6790
- payload: {
6791
- mediaType: data.mediaType,
6792
- shareInstanceId: data.mediaType === 'share' ? this.localShareInstanceId : undefined,
6793
- },
6794
- options: {
6795
- meetingId: this.id,
6796
- },
6797
- });
6818
+ if (data.mediaType !== 'share' || !this.shareCAEventSentStatus.transmitStop) {
6819
+ // @ts-ignore
6820
+ this.webex.internal.newMetrics.submitClientEvent({
6821
+ name: 'client.media.tx.stop',
6822
+ payload: {
6823
+ mediaType: data.mediaType,
6824
+ shareInstanceId: data.mediaType === 'share' ? this.localShareInstanceId : undefined,
6825
+ },
6826
+ options: {
6827
+ meetingId: this.id,
6828
+ },
6829
+ });
6830
+
6831
+ if (data.mediaType === 'share') {
6832
+ this.shareCAEventSentStatus.transmitStop = true;
6833
+ }
6834
+ }
6798
6835
  });
6799
6836
  this.statsAnalyzer.on(StatsAnalyzerEventNames.REMOTE_MEDIA_STARTED, (data) => {
6800
6837
  Trigger.trigger(
@@ -6806,57 +6843,65 @@ export default class Meeting extends StatelessWebexPlugin {
6806
6843
  EVENT_TRIGGERS.MEETING_MEDIA_REMOTE_STARTED,
6807
6844
  data
6808
6845
  );
6809
- // @ts-ignore
6810
- this.webex.internal.newMetrics.submitClientEvent({
6811
- name: 'client.media.rx.start',
6812
- payload: {
6813
- mediaType: data.mediaType,
6814
- shareInstanceId: data.mediaType === 'share' ? this.remoteShareInstanceId : undefined,
6815
- },
6816
- options: {
6817
- meetingId: this.id,
6818
- },
6819
- });
6820
-
6821
- if (data.mediaType === 'share') {
6846
+ if (data.mediaType !== 'share' || !this.shareCAEventSentStatus.receiveStart) {
6822
6847
  // @ts-ignore
6823
6848
  this.webex.internal.newMetrics.submitClientEvent({
6824
- name: 'client.media.render.start',
6849
+ name: 'client.media.rx.start',
6825
6850
  payload: {
6826
- mediaType: 'share',
6827
- shareInstanceId: this.remoteShareInstanceId,
6851
+ mediaType: data.mediaType,
6852
+ shareInstanceId: data.mediaType === 'share' ? this.remoteShareInstanceId : undefined,
6828
6853
  },
6829
6854
  options: {
6830
6855
  meetingId: this.id,
6831
6856
  },
6832
6857
  });
6858
+
6859
+ if (data.mediaType === 'share') {
6860
+ // @ts-ignore
6861
+ this.webex.internal.newMetrics.submitClientEvent({
6862
+ name: 'client.media.render.start',
6863
+ payload: {
6864
+ mediaType: 'share',
6865
+ shareInstanceId: this.remoteShareInstanceId,
6866
+ },
6867
+ options: {
6868
+ meetingId: this.id,
6869
+ },
6870
+ });
6871
+
6872
+ this.shareCAEventSentStatus.receiveStart = true;
6873
+ }
6833
6874
  }
6834
6875
  });
6835
6876
  this.statsAnalyzer.on(StatsAnalyzerEventNames.REMOTE_MEDIA_STOPPED, (data) => {
6836
- // @ts-ignore
6837
- this.webex.internal.newMetrics.submitClientEvent({
6838
- name: 'client.media.rx.stop',
6839
- payload: {
6840
- mediaType: data.mediaType,
6841
- shareInstanceId: data.mediaType === 'share' ? this.remoteShareInstanceId : undefined,
6842
- },
6843
- options: {
6844
- meetingId: this.id,
6845
- },
6846
- });
6847
-
6848
- if (data.mediaType === 'share') {
6877
+ if (data.mediaType !== 'share' || !this.shareCAEventSentStatus.receiveStop) {
6849
6878
  // @ts-ignore
6850
6879
  this.webex.internal.newMetrics.submitClientEvent({
6851
- name: 'client.media.render.stop',
6880
+ name: 'client.media.rx.stop',
6852
6881
  payload: {
6853
- mediaType: 'share',
6854
- shareInstanceId: this.remoteShareInstanceId,
6882
+ mediaType: data.mediaType,
6883
+ shareInstanceId: data.mediaType === 'share' ? this.remoteShareInstanceId : undefined,
6855
6884
  },
6856
6885
  options: {
6857
6886
  meetingId: this.id,
6858
6887
  },
6859
6888
  });
6889
+
6890
+ if (data.mediaType === 'share') {
6891
+ // @ts-ignore
6892
+ this.webex.internal.newMetrics.submitClientEvent({
6893
+ name: 'client.media.render.stop',
6894
+ payload: {
6895
+ mediaType: 'share',
6896
+ shareInstanceId: this.remoteShareInstanceId,
6897
+ },
6898
+ options: {
6899
+ meetingId: this.id,
6900
+ },
6901
+ });
6902
+
6903
+ this.shareCAEventSentStatus.receiveStop = true;
6904
+ }
6860
6905
  }
6861
6906
  });
6862
6907
  };
@@ -7077,6 +7122,12 @@ export default class Meeting extends StatelessWebexPlugin {
7077
7122
  networkQualityMonitor: this.networkQualityMonitor,
7078
7123
  isMultistream: this.isMultistream,
7079
7124
  });
7125
+ this.shareCAEventSentStatus = {
7126
+ transmitStart: false,
7127
+ transmitStop: false,
7128
+ receiveStart: false,
7129
+ receiveStop: false,
7130
+ };
7080
7131
  this.setupStatsAnalyzerEventHandlers();
7081
7132
  this.networkQualityMonitor.on(
7082
7133
  NetworkQualityEventNames.NETWORK_QUALITY,
@@ -9341,6 +9392,8 @@ export default class Meeting extends StatelessWebexPlugin {
9341
9392
 
9342
9393
  if (floorRequestNeeded) {
9343
9394
  this.localShareInstanceId = uuid.v4();
9395
+ this.shareCAEventSentStatus.transmitStart = false;
9396
+ this.shareCAEventSentStatus.transmitStop = false;
9344
9397
 
9345
9398
  // @ts-ignore
9346
9399
  this.webex.internal.newMetrics.submitClientEvent({
@@ -1,6 +1,10 @@
1
1
  import {assert} from '@webex/test-helper-chai';
2
2
  import ControlsUtils from '@webex/plugin-meetings/src/locus-info/controlsUtils';
3
3
  import controlsUtils from "@webex/plugin-meetings/src/locus-info/controlsUtils";
4
+ import {
5
+ MEETING_STATE,
6
+ BREAKOUTS,
7
+ } from '../../../../src/constants';
4
8
 
5
9
  const defaultControls = {
6
10
  entryExitTone: {
@@ -432,28 +436,76 @@ describe('plugin-meetings', () => {
432
436
 
433
437
  describe('getSessionSwitchStatus', () => {
434
438
  it('if no breakout control, return switch status both false', () => {
435
- const oldControls = {};
436
- const newControls = {};
437
- assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldControls, newControls), {
439
+ const oldLocus = {};
440
+ const newLocus = {};
441
+ assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldLocus, newLocus), {
438
442
  isReturnToMain: false, isJoinToBreakout: false
439
443
  });
440
444
  });
441
445
 
442
446
  it('if switch session from breakout to main, return isReturnToMain as true', () => {
443
- const oldControls = {breakout: {sessionType: 'BREAKOUT'}};
444
- const newControls = {breakout: {sessionType: 'MAIN'}};
445
- assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldControls, newControls), {
447
+ const oldLocus = {controls: {breakout: {sessionType: 'BREAKOUT'}}};
448
+ const newLocus = {controls: {breakout: {sessionType: 'MAIN'}}};
449
+ assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldLocus, newLocus), {
446
450
  isReturnToMain: true, isJoinToBreakout: false
447
451
  });
448
452
  });
449
453
 
450
454
  it('if switch session from main to breakout, return isJoinToBreakout as true', () => {
451
- const oldControls = {breakout: {sessionType: 'MAIN'}};
452
- const newControls = {breakout: {sessionType: 'BREAKOUT'}};
453
- assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldControls, newControls), {
455
+ const oldLocus = {controls: {breakout: {sessionType: 'MAIN'}}};
456
+ const newLocus = {controls: {breakout: {sessionType: 'BREAKOUT'}}};
457
+ assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldLocus, newLocus), {
454
458
  isReturnToMain: false, isJoinToBreakout: true
455
459
  });
456
460
  });
461
+
462
+ it('if needUseCache conditions are met, return isJoinToBreakout as true', () => {
463
+ const oldLocus = {
464
+ self: { isCreator: true },
465
+ controls: { breakout: { sessionType: BREAKOUTS.SESSION_TYPES.MAIN} },
466
+ };
467
+
468
+ const newLocus = {
469
+ participants: [
470
+ { isCreator: true, state: MEETING_STATE.STATES.JOINED },
471
+ ],
472
+ controls: {
473
+ breakout: {
474
+ sessionType: BREAKOUTS.SESSION_TYPES.MAIN,
475
+ groups: [{ id: 'group1' }]
476
+ },
477
+ },
478
+ };
479
+
480
+ assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldLocus, newLocus), {
481
+ isReturnToMain: true,
482
+ isJoinToBreakout: false
483
+ });
484
+ });
485
+
486
+ it('if needUseCache conditions are not met, return newLocus and isReturnToMain as false', () => {
487
+ const oldLocus = {
488
+ self: { isCreator: false },
489
+ controls: { breakout: { sessionType: BREAKOUTS.SESSION_TYPES.BREAKOUT} },
490
+ };
491
+
492
+ const newLocus = {
493
+ participants: [
494
+ { isCreator: true, state: MEETING_STATE.STATES.JOINED },
495
+ ],
496
+ controls: {
497
+ breakout: {
498
+ sessionType: BREAKOUTS.SESSION_TYPES.BREAKOUT,
499
+ groups: []
500
+ },
501
+ },
502
+ };
503
+
504
+ assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldLocus, newLocus), {
505
+ isReturnToMain: false,
506
+ isJoinToBreakout: false
507
+ });
508
+ });
457
509
  });
458
510
 
459
511
  describe('#isMainSessionDTO', () => {