@webex/plugin-meetings 3.11.0-next.3 → 3.11.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.
@@ -70,6 +70,7 @@ export type AddMediaOptions = {
70
70
  remoteMediaManagerConfig?: RemoteMediaManagerConfiguration;
71
71
  bundlePolicy?: BundlePolicy;
72
72
  allowMediaInLobby?: boolean;
73
+ allowPublishMediaInLobby?: boolean;
73
74
  additionalMediaOptions?: AdditionalMediaOptions;
74
75
  };
75
76
  export type AdditionalMediaOptions = {
@@ -405,6 +406,10 @@ export default class Meeting extends StatelessWebexPlugin {
405
406
  keepAliveTimerId: NodeJS.Timeout;
406
407
  lastVideoLayoutInfo: any;
407
408
  locusInfo: any;
409
+ isUserUnadmitted?: boolean;
410
+ joinedWith?: any;
411
+ selfId?: string;
412
+ roles: any[];
408
413
  locusMediaRequest?: LocusMediaRequest;
409
414
  mediaProperties: MediaProperties;
410
415
  mediaRequestManagers: {
@@ -438,7 +443,6 @@ export default class Meeting extends StatelessWebexPlugin {
438
443
  endCallInitJoinReq: any;
439
444
  endJoinReqResp: any;
440
445
  endLocalSDPGenRemoteSDPRecvDelay: any;
441
- joinedWith: any;
442
446
  locusId: any;
443
447
  startCallInitJoinReq: any;
444
448
  startJoinReqResp: any;
@@ -453,12 +457,10 @@ export default class Meeting extends StatelessWebexPlugin {
453
457
  permissionTokenReceivedLocalTime: number;
454
458
  resourceId: any;
455
459
  resourceUrl: string;
456
- selfId: string;
457
460
  state: any;
458
461
  localAudioStreamMuteStateHandler: () => void;
459
462
  localVideoStreamMuteStateHandler: () => void;
460
463
  localOutputTrackChangeHandler: () => void;
461
- roles: any[];
462
464
  environment: string;
463
465
  namespace: string;
464
466
  allowMediaInLobby: boolean;
@@ -506,7 +506,7 @@ var Webinar = _webexCore.WebexPlugin.extend({
506
506
  }, _callee8);
507
507
  }))();
508
508
  },
509
- version: "3.11.0-next.3"
509
+ version: "3.11.0-next.4"
510
510
  });
511
511
  var _default = exports.default = Webinar;
512
512
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -93,5 +93,5 @@
93
93
  "//": [
94
94
  "TODO: upgrade jwt-decode when moving to node 18"
95
95
  ],
96
- "version": "3.11.0-next.3"
96
+ "version": "3.11.0-next.4"
97
97
  }
@@ -250,6 +250,7 @@ export type AddMediaOptions = {
250
250
  remoteMediaManagerConfig?: RemoteMediaManagerConfiguration; // applies only to multistream meetings
251
251
  bundlePolicy?: BundlePolicy; // applies only to multistream meetings
252
252
  allowMediaInLobby?: boolean; // allows adding media when in the lobby
253
+ allowPublishMediaInLobby?: boolean; // allows publishing media when in the lobby, if not specified, default value false is used
253
254
  additionalMediaOptions?: AdditionalMediaOptions; // allows adding additional options like send/receive audio/video
254
255
  };
255
256
 
@@ -623,6 +624,13 @@ export default class Meeting extends StatelessWebexPlugin {
623
624
  keepAliveTimerId: NodeJS.Timeout;
624
625
  lastVideoLayoutInfo: any;
625
626
  locusInfo: any;
627
+ // this group of properties is populated via updateMeetingObject() that's registered as a callback with LocusInfo
628
+ isUserUnadmitted?: boolean;
629
+ joinedWith?: any;
630
+ selfId?: string;
631
+ roles: any[];
632
+ // ... there is more ... see SelfUtils.parse()
633
+ // end of the group
626
634
  locusMediaRequest?: LocusMediaRequest;
627
635
  mediaProperties: MediaProperties;
628
636
  mediaRequestManagers: {
@@ -657,7 +665,6 @@ export default class Meeting extends StatelessWebexPlugin {
657
665
  endCallInitJoinReq: any;
658
666
  endJoinReqResp: any;
659
667
  endLocalSDPGenRemoteSDPRecvDelay: any;
660
- joinedWith: any;
661
668
  locusId: any;
662
669
  startCallInitJoinReq: any;
663
670
  startJoinReqResp: any;
@@ -672,12 +679,10 @@ export default class Meeting extends StatelessWebexPlugin {
672
679
  permissionTokenReceivedLocalTime: number;
673
680
  resourceId: any;
674
681
  resourceUrl: string;
675
- selfId: string;
676
682
  state: any;
677
683
  localAudioStreamMuteStateHandler: () => void;
678
684
  localVideoStreamMuteStateHandler: () => void;
679
685
  localOutputTrackChangeHandler: () => void;
680
- roles: any[];
681
686
  environment: string;
682
687
  namespace = MEETINGS;
683
688
  allowMediaInLobby: boolean;
@@ -8026,6 +8031,7 @@ export default class Meeting extends StatelessWebexPlugin {
8026
8031
  remoteMediaManagerConfig,
8027
8032
  bundlePolicy = 'max-bundle',
8028
8033
  additionalMediaOptions = {},
8034
+ allowPublishMediaInLobby = false,
8029
8035
  } = options;
8030
8036
 
8031
8037
  const {
@@ -8046,7 +8052,6 @@ export default class Meeting extends StatelessWebexPlugin {
8046
8052
  const ipver = MeetingUtil.getIpVersion(this.webex); // used just for metrics
8047
8053
 
8048
8054
  // If the user is unjoined or guest waiting in lobby dont allow the user to addMedia
8049
- // @ts-ignore - isUserUnadmitted coming from SelfUtil
8050
8055
  if (this.isUserUnadmitted && !this.wirelessShare && !this.allowMediaInLobby) {
8051
8056
  throw new UserInLobbyError();
8052
8057
  }
@@ -8091,7 +8096,13 @@ export default class Meeting extends StatelessWebexPlugin {
8091
8096
  this.brbState = createBrbState(this, false);
8092
8097
 
8093
8098
  try {
8094
- await this.setUpLocalStreamReferences(localStreams);
8099
+ // if we're in a lobby and allowPublishMediaInLobby==false, we don't want to
8100
+ // setup local streams for publishing, because if we ever end up admitted to the meeting
8101
+ // but Locus event about it for us is delayed or missed, others could see/hear our user's video/audio
8102
+ // while the user would still think they're in the lobby
8103
+ if (allowPublishMediaInLobby || !this.isUserUnadmitted) {
8104
+ await this.setUpLocalStreamReferences(localStreams);
8105
+ }
8095
8106
 
8096
8107
  this.setMercuryListener();
8097
8108
 
@@ -3028,6 +3028,111 @@ describe('plugin-meetings', () => {
3028
3028
  checkWorking({allowMediaInLobby: true});
3029
3029
  });
3030
3030
 
3031
+ const setupLobbyTest = () => {
3032
+ meeting.roap.doTurnDiscovery = sinon
3033
+ .stub()
3034
+ .resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
3035
+
3036
+ meeting.meetingState = 'ACTIVE';
3037
+ meeting.locusInfo.parsedLocus = {self: {state: 'IDLE'}};
3038
+ meeting.isUserUnadmitted = true;
3039
+
3040
+ // Mock locusMediaRequest
3041
+ meeting.locusMediaRequest = {
3042
+ send: sinon.stub().resolves(),
3043
+ isConfluenceCreated: sinon.stub().returns(false),
3044
+ };
3045
+
3046
+ sinon.stub(RemoteMediaManagerModule, 'RemoteMediaManager').returns({
3047
+ start: sinon.stub().resolves(),
3048
+ on: sinon.stub(),
3049
+ logAllReceiveSlots: sinon.stub(),
3050
+ });
3051
+
3052
+ meeting.isMultistream = true;
3053
+
3054
+ const createFakeStream = (id) => ({
3055
+ on: sinon.stub(),
3056
+ off: sinon.stub(),
3057
+ userMuted: false,
3058
+ systemMuted: false,
3059
+ get muted() {
3060
+ return this.userMuted || this.systemMuted;
3061
+ },
3062
+ setUnmuteAllowed: sinon.stub(),
3063
+ setUserMuted: sinon.stub(),
3064
+ outputStream: {
3065
+ getTracks: () => [{id}],
3066
+ },
3067
+ getSettings: sinon.stub().returns({}),
3068
+ });
3069
+
3070
+ return {
3071
+ fakeMicrophoneStream: createFakeStream('fake mic'),
3072
+ fakeCameraStream: createFakeStream('fake camera'),
3073
+ };
3074
+ };
3075
+
3076
+ it('should not publish any local streams when in the lobby and allowPublishMediaInLobby is false', async () => {
3077
+ const {fakeMicrophoneStream, fakeCameraStream} = setupLobbyTest();
3078
+
3079
+ const publishStreamStub = sinon.stub();
3080
+ fakeMediaConnection.createSendSlot = sinon.stub().returns({
3081
+ publishStream: publishStreamStub,
3082
+ unpublishStream: sinon.stub(),
3083
+ setNamedMediaGroups: sinon.stub(),
3084
+ });
3085
+
3086
+ await meeting.addMedia({
3087
+ allowMediaInLobby: true,
3088
+ allowPublishMediaInLobby: false,
3089
+ audioEnabled: true,
3090
+ videoEnabled: true,
3091
+ localStreams: {
3092
+ microphone: fakeMicrophoneStream,
3093
+ camera: fakeCameraStream,
3094
+ },
3095
+ });
3096
+
3097
+ assert.notCalled(publishStreamStub);
3098
+ });
3099
+
3100
+ it('should publish local streams when in the lobby and allowPublishMediaInLobby is true', async () => {
3101
+ const {fakeMicrophoneStream, fakeCameraStream} = setupLobbyTest();
3102
+
3103
+ const audioSlot = {
3104
+ publishStream: sinon.stub(),
3105
+ unpublishStream: sinon.stub(),
3106
+ setNamedMediaGroups: sinon.stub(),
3107
+ };
3108
+ const videoSlot = {
3109
+ publishStream: sinon.stub(),
3110
+ unpublishStream: sinon.stub(),
3111
+ setNamedMediaGroups: sinon.stub(),
3112
+ };
3113
+
3114
+ fakeMediaConnection.createSendSlot = sinon.stub().callsFake((mediaType) => {
3115
+ if (mediaType === 'AUDIO-MAIN') {
3116
+ return audioSlot;
3117
+ }
3118
+ return videoSlot;
3119
+ });
3120
+
3121
+ await meeting.addMedia({
3122
+ allowMediaInLobby: true,
3123
+ allowPublishMediaInLobby: true,
3124
+ audioEnabled: true,
3125
+ videoEnabled: true,
3126
+ localStreams: {
3127
+ microphone: fakeMicrophoneStream,
3128
+ camera: fakeCameraStream,
3129
+ },
3130
+ });
3131
+
3132
+ assert.calledOnceWithExactly(audioSlot.publishStream, fakeMicrophoneStream);
3133
+ assert.calledOnceWithExactly(videoSlot.publishStream, fakeCameraStream);
3134
+ });
3135
+
3031
3136
  it('should create rtcMetrics and pass them to Media.createMediaConnection()', async () => {
3032
3137
  const setIntervalOriginal = window.setInterval;
3033
3138
  window.setInterval = sinon.stub().returns(1);