@webex/plugin-meetings 3.0.0-beta.24 → 3.0.0-beta.26

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 (38) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +5 -1
  4. package/dist/constants.js.map +1 -1
  5. package/dist/controls-options-manager/constants.js +14 -0
  6. package/dist/controls-options-manager/constants.js.map +1 -0
  7. package/dist/controls-options-manager/enums.js +15 -0
  8. package/dist/controls-options-manager/enums.js.map +1 -0
  9. package/dist/controls-options-manager/index.js +203 -0
  10. package/dist/controls-options-manager/index.js.map +1 -0
  11. package/dist/controls-options-manager/util.js +28 -0
  12. package/dist/controls-options-manager/util.js.map +1 -0
  13. package/dist/meeting/in-meeting-actions.js +8 -0
  14. package/dist/meeting/in-meeting-actions.js.map +1 -1
  15. package/dist/meeting/index.js +59 -12
  16. package/dist/meeting/index.js.map +1 -1
  17. package/dist/multistream/remoteMediaManager.js +4 -3
  18. package/dist/multistream/remoteMediaManager.js.map +1 -1
  19. package/dist/types/constants.d.ts +4 -0
  20. package/dist/types/controls-options-manager/constants.d.ts +4 -0
  21. package/dist/types/controls-options-manager/enums.d.ts +5 -0
  22. package/dist/types/controls-options-manager/index.d.ts +120 -0
  23. package/dist/types/controls-options-manager/util.d.ts +7 -0
  24. package/dist/types/meeting/in-meeting-actions.d.ts +8 -0
  25. package/dist/types/meeting/index.d.ts +18 -0
  26. package/package.json +19 -19
  27. package/src/constants.ts +4 -0
  28. package/src/controls-options-manager/constants.ts +5 -0
  29. package/src/controls-options-manager/enums.ts +6 -0
  30. package/src/controls-options-manager/index.ts +183 -0
  31. package/src/controls-options-manager/util.ts +20 -0
  32. package/src/meeting/in-meeting-actions.ts +16 -0
  33. package/src/meeting/index.ts +49 -0
  34. package/src/multistream/remoteMediaManager.ts +2 -2
  35. package/test/unit/spec/controls-options-manager/index.js +124 -0
  36. package/test/unit/spec/controls-options-manager/util.js +66 -0
  37. package/test/unit/spec/meeting/in-meeting-actions.ts +8 -0
  38. package/test/unit/spec/meeting/index.js +15 -0
@@ -0,0 +1,183 @@
1
+ import {camelCase} from 'lodash';
2
+ import PermissionError from '../common/errors/permission';
3
+ import {CONTROLS, HTTP_VERBS} from '../constants';
4
+ import MeetingRequest from '../meeting/request';
5
+ import LoggerProxy from '../common/logs/logger-proxy';
6
+ import Setting from './enums';
7
+ import Util from './util';
8
+ import {CAN_SET, CAN_UNSET, ENABLED} from './constants';
9
+
10
+ /**
11
+ * docs
12
+ * https://sqbu-github.cisco.com/pages/WebExSquared/locus/guides/mute.html
13
+ * https://confluence-eng-gpk2.cisco.com/conf/display/LOCUS/Hard+Mute+and+Audio+Privacy#HardMuteandAudioPrivacy-SelfMuteonEntry
14
+ * https://confluence-eng-gpk2.cisco.com/conf/pages/viewpage.action?spaceKey=UC&title=WEBEX-124454%3A+UCF%3A+Hard+mute+support+for+Teams+joining+Webex+meeting
15
+ * https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-180867
16
+ * https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-393351
17
+ */
18
+
19
+ /**
20
+ * @description ControlsOptionsManager is responsible for handling the behavior of participant controls when somebody joins a meeting
21
+ * @export
22
+ * @private
23
+ * @class Recording
24
+ */
25
+ export default class ControlsOptionsManager {
26
+ /**
27
+ * @instance
28
+ * @type {MeetingRequest}
29
+ * @private
30
+ * @memberof ControlsOptionsManager
31
+ */
32
+ private request: MeetingRequest;
33
+
34
+ /**
35
+ * @instance
36
+ * @type {Array}
37
+ * @private
38
+ * @memberof ControlsOptionsManager
39
+ */
40
+ private displayHints: Array<string> = [];
41
+
42
+ /**
43
+ * @instance
44
+ * @type {string}
45
+ * @private
46
+ * @memberof ControlsOptionsManager
47
+ */
48
+ private locusUrl: string;
49
+
50
+ /**
51
+ * @param {MeetingRequest} request
52
+ * @param {Object} options
53
+ * @constructor
54
+ * @memberof ControlsOptionsManager
55
+ */
56
+ constructor(
57
+ request: MeetingRequest,
58
+ options?: {
59
+ locusUrl: string;
60
+ displayHints?: Array<string>;
61
+ }
62
+ ) {
63
+ this.initialize(request);
64
+ this.set(options);
65
+ }
66
+
67
+ /**
68
+ * @param {MeetingRequest} request
69
+ * @returns {void}
70
+ * @private
71
+ * @memberof ControlsOptionsManager
72
+ */
73
+ private initialize(request: MeetingRequest) {
74
+ this.request = request;
75
+ }
76
+
77
+ /**
78
+ * @param {Object} options
79
+ * @returns {void}
80
+ * @public
81
+ * @memberof ControlsOptionsManager
82
+ */
83
+ public set(options?: {locusUrl: string; displayHints?: Array<string>}) {
84
+ this.extract(options);
85
+ }
86
+
87
+ /**
88
+ * @param {string} url
89
+ * @returns {void}
90
+ * @public
91
+ * @memberof ControlsOptionsManager
92
+ */
93
+ public setLocusUrl(url: string) {
94
+ this.locusUrl = url;
95
+ }
96
+
97
+ /**
98
+ * @param {Array} hints
99
+ * @returns {void}
100
+ * @public
101
+ * @memberof ControlsOptionsManager
102
+ */
103
+ public setDisplayHints(hints: Array<string>) {
104
+ this.displayHints = hints;
105
+ }
106
+
107
+ /**
108
+ * @returns {string}
109
+ * @public
110
+ * @memberof ControlsOptionsManager
111
+ */
112
+ public getLocusUrl() {
113
+ return this.locusUrl;
114
+ }
115
+
116
+ /**
117
+ * @returns {Array}
118
+ * @public
119
+ * @memberof ControlsOptionsManager
120
+ */
121
+ public getDisplayHints() {
122
+ return this.displayHints;
123
+ }
124
+
125
+ /**
126
+ * @param {Object} options
127
+ * @returns {void}
128
+ * @private
129
+ * @memberof ControlsOptionsManager
130
+ */
131
+ private extract(options?: {locusUrl: string; displayHints?: Array<string>}) {
132
+ this.setDisplayHints(options?.displayHints);
133
+ this.setLocusUrl(options?.locusUrl);
134
+ }
135
+
136
+ /**
137
+ * @param {Setting} setting
138
+ * @param {boolean} enabled
139
+ * @private
140
+ * @memberof ControlsOptionsManager
141
+ * @returns {Promise}
142
+ */
143
+ private setControls(setting: Setting, enabled: boolean): Promise<any> {
144
+ LoggerProxy.logger.log(`ControlsOptionsManager:index#setControls --> ${setting} [${enabled}]`);
145
+
146
+ if (Util?.[`${enabled ? CAN_SET : CAN_UNSET}${setting}`](this.displayHints)) {
147
+ // @ts-ignore
148
+ return this.request.request({
149
+ uri: `${this.locusUrl}/${CONTROLS}`,
150
+ body: {
151
+ [camelCase(setting)]: {
152
+ [ENABLED]: enabled,
153
+ },
154
+ },
155
+ method: HTTP_VERBS.PATCH,
156
+ });
157
+ }
158
+
159
+ return Promise.reject(
160
+ new PermissionError(`${setting} [${enabled}] not allowed, due to moderator property.`)
161
+ );
162
+ }
163
+
164
+ /**
165
+ * @public
166
+ * @param {boolean} enabled
167
+ * @memberof ControlsOptionsManager
168
+ * @returns {Promise}
169
+ */
170
+ public setMuteOnEntry(enabled: boolean): Promise<any> {
171
+ return this.setControls(Setting.muteOnEntry, enabled);
172
+ }
173
+
174
+ /**
175
+ * @public
176
+ * @param {boolean} enabled
177
+ * @memberof ControlsOptionsManager
178
+ * @returns {Promise}
179
+ */
180
+ public setDisallowUnmute(enabled: boolean): Promise<any> {
181
+ return this.setControls(Setting.disallowUnmute, enabled);
182
+ }
183
+ }
@@ -0,0 +1,20 @@
1
+ import {DISPLAY_HINTS} from '../constants';
2
+
3
+ const canSetMuteOnEntry = (displayHints: Array<string>): boolean =>
4
+ displayHints.includes(DISPLAY_HINTS.ENABLE_MUTE_ON_ENTRY);
5
+
6
+ const canSetDisallowUnmute = (displayHints: Array<string>): boolean =>
7
+ displayHints.includes(DISPLAY_HINTS.ENABLE_HARD_MUTE);
8
+
9
+ const canUnsetMuteOnEntry = (displayHints: Array<string>): boolean =>
10
+ displayHints.includes(DISPLAY_HINTS.DISABLE_MUTE_ON_ENTRY);
11
+
12
+ const canUnsetDisallowUnmute = (displayHints: Array<string>): boolean =>
13
+ displayHints.includes(DISPLAY_HINTS.DISABLE_HARD_MUTE);
14
+
15
+ export default {
16
+ canSetMuteOnEntry,
17
+ canSetDisallowUnmute,
18
+ canUnsetMuteOnEntry,
19
+ canUnsetDisallowUnmute,
20
+ };
@@ -13,6 +13,10 @@ interface IInMeetingActions {
13
13
  canAdmitParticipant?: boolean;
14
14
  canLock?: boolean;
15
15
  canUnlock?: boolean;
16
+ canSetMuteOnEntry?: boolean;
17
+ canUnsetMuteOnEntry?: boolean;
18
+ canSetDisallowUnmute?: boolean;
19
+ canUnsetDisallowUnmute?: boolean;
16
20
  canAssignHost?: boolean;
17
21
  canStartRecording?: boolean;
18
22
  canPauseRecording?: boolean;
@@ -59,6 +63,14 @@ export default class InMeetingActions implements IInMeetingActions {
59
63
 
60
64
  canStopRecording = null;
61
65
 
66
+ canSetMuteOnEntry = null;
67
+
68
+ canUnsetMuteOnEntry = null;
69
+
70
+ canSetDisallowUnmute = null;
71
+
72
+ canUnsetDisallowUnmute = null;
73
+
62
74
  canRaiseHand = null;
63
75
 
64
76
  canLowerAllHands = null;
@@ -99,6 +111,10 @@ export default class InMeetingActions implements IInMeetingActions {
99
111
  canLock: this.canLock,
100
112
  canUnlock: this.canUnlock,
101
113
  canAssignHost: this.canAssignHost,
114
+ canSetMuteOnEntry: this.canSetMuteOnEntry,
115
+ canUnsetMuteOnEntry: this.canUnsetMuteOnEntry,
116
+ canSetDisallowUnmute: this.canSetDisallowUnmute,
117
+ canUnsetDisallowUnmute: this.canUnsetDisallowUnmute,
102
118
  canStartRecording: this.canStartRecording,
103
119
  canPauseRecording: this.canPauseRecording,
104
120
  canResumeRecording: this.canResumeRecording,
@@ -37,6 +37,7 @@ import MeetingRequest from './request';
37
37
  import Members from '../members/index';
38
38
  import MeetingUtil from './util';
39
39
  import RecordingUtil from '../recording-controller/util';
40
+ import ControlsOptionsUtil from '../controls-options-manager/util';
40
41
  import MediaUtil from '../media/util';
41
42
  import Transcription from '../transcription';
42
43
  import {Reactions, SkinTones} from '../reactions/reactions';
@@ -106,6 +107,7 @@ import Breakouts from '../breakouts';
106
107
  import InMeetingActions from './in-meeting-actions';
107
108
  import {REACTION_RELAY_TYPES} from '../reactions/constants';
108
109
  import RecordingController from '../recording-controller';
110
+ import ControlsOptionsManager from '../controls-options-manager';
109
111
 
110
112
  const {isBrowser} = BrowserDetection();
111
113
 
@@ -485,6 +487,7 @@ export default class Meeting extends StatelessWebexPlugin {
485
487
  recording: any;
486
488
  remoteMediaManager: RemoteMediaManager | null;
487
489
  recordingController: RecordingController;
490
+ controlsOptionsManager: ControlsOptionsManager;
488
491
  requiredCaptcha: any;
489
492
  receiveSlotManager: ReceiveSlotManager;
490
493
  shareStatus: string;
@@ -1098,6 +1101,18 @@ export default class Meeting extends StatelessWebexPlugin {
1098
1101
  displayHints: [],
1099
1102
  });
1100
1103
 
1104
+ /**
1105
+ * The class that helps to control recording functions: start, stop, pause, resume, etc
1106
+ * @instance
1107
+ * @type {ControlsOptionsManager}
1108
+ * @public
1109
+ * @memberof Meeting
1110
+ */
1111
+ this.controlsOptionsManager = new ControlsOptionsManager(this.meetingRequest, {
1112
+ locusUrl: this.locusInfo?.url,
1113
+ displayHints: [],
1114
+ });
1115
+
1101
1116
  this.setUpLocusInfoListeners();
1102
1117
  this.locusInfo.init(attrs.locus ? attrs.locus : {});
1103
1118
  this.hasJoinedOnce = false;
@@ -2187,6 +2202,7 @@ export default class Meeting extends StatelessWebexPlugin {
2187
2202
  this.locusUrl = payload;
2188
2203
  this.locusId = this.locusUrl?.split('/').pop();
2189
2204
  this.recordingController.setLocusUrl(this.locusUrl);
2205
+ this.controlsOptionsManager.setLocusUrl(this.locusUrl);
2190
2206
  });
2191
2207
  }
2192
2208
 
@@ -2252,6 +2268,16 @@ export default class Meeting extends StatelessWebexPlugin {
2252
2268
  canAdmitParticipant: MeetingUtil.canAdmitParticipant(payload.info.userDisplayHints),
2253
2269
  canLock: MeetingUtil.canUserLock(payload.info.userDisplayHints),
2254
2270
  canUnlock: MeetingUtil.canUserUnlock(payload.info.userDisplayHints),
2271
+ canSetDisallowUnmute: ControlsOptionsUtil.canSetDisallowUnmute(
2272
+ payload.info.userDisplayHints
2273
+ ),
2274
+ canUnsetDisallowUnmute: ControlsOptionsUtil.canUnsetDisallowUnmute(
2275
+ payload.info.userDisplayHints
2276
+ ),
2277
+ canSetMuteOnEntry: ControlsOptionsUtil.canSetMuteOnEntry(payload.info.userDisplayHints),
2278
+ canUnsetMuteOnEntry: ControlsOptionsUtil.canUnsetMuteOnEntry(
2279
+ payload.info.userDisplayHints
2280
+ ),
2255
2281
  canStartRecording: RecordingUtil.canUserStart(payload.info.userDisplayHints),
2256
2282
  canStopRecording: RecordingUtil.canUserStop(payload.info.userDisplayHints),
2257
2283
  canPauseRecording: RecordingUtil.canUserPause(payload.info.userDisplayHints),
@@ -2288,6 +2314,7 @@ export default class Meeting extends StatelessWebexPlugin {
2288
2314
  });
2289
2315
 
2290
2316
  this.recordingController.setDisplayHints(payload.info.userDisplayHints);
2317
+ this.controlsOptionsManager.setDisplayHints(payload.info.userDisplayHints);
2291
2318
 
2292
2319
  if (changed) {
2293
2320
  Trigger.trigger(
@@ -6131,6 +6158,28 @@ export default class Meeting extends StatelessWebexPlugin {
6131
6158
  return this.recordingController.startRecording();
6132
6159
  }
6133
6160
 
6161
+ /**
6162
+ * set the mute on entry flag for participants if you're the host
6163
+ * @returns {Promise}
6164
+ * @param {boolean} enabled
6165
+ * @public
6166
+ * @memberof Meeting
6167
+ */
6168
+ public setMuteOnEntry(enabled: boolean) {
6169
+ return this.controlsOptionsManager.setMuteOnEntry(enabled);
6170
+ }
6171
+
6172
+ /**
6173
+ * set the disallow unmute flag for participants if you're the host
6174
+ * @returns {Promise}
6175
+ * @param {boolean} enabled
6176
+ * @public
6177
+ * @memberof Meeting
6178
+ */
6179
+ public setDisallowUnmute(enabled: boolean) {
6180
+ return this.controlsOptionsManager.setDisallowUnmute(enabled);
6181
+ }
6182
+
6134
6183
  /**
6135
6184
  * End the recording of this meeting
6136
6185
  * @returns {Promise}
@@ -115,7 +115,7 @@ const TwoMainPlusSixSmallLayout: VideoLayout = {
115
115
 
116
116
  // A strip of 8 small video panes (thumbnails) displayed at the top of a remote screenshare:
117
117
  const RemoteScreenShareWithSmallThumbnailsLayout: VideoLayout = {
118
- // screenShareVideo: {size: 'best'}, // todo: SPARK-393485: uncomment this once backend supports screen sharing
118
+ screenShareVideo: {size: 'best'},
119
119
  activeSpeakerVideoPaneGroups: [
120
120
  {
121
121
  id: 'thumbnails',
@@ -153,7 +153,7 @@ const Stage2x2With6ThumbnailsLayout: VideoLayout = {
153
153
  export const DefaultConfiguration: Configuration = {
154
154
  audio: {
155
155
  numOfActiveSpeakerStreams: 3,
156
- numOfScreenShareStreams: 0, // todo: SPARK-393485: change to 1 once backend supports screen sharing
156
+ numOfScreenShareStreams: 1,
157
157
  },
158
158
  video: {
159
159
  preferLiveVideo: true,
@@ -0,0 +1,124 @@
1
+ import ControlsOptionsManager from '@webex/plugin-meetings/src/controls-options-manager';
2
+ import sinon from 'sinon';
3
+ import {assert} from '@webex/test-helper-chai';
4
+ import { HTTP_VERBS } from '@webex/plugin-meetings/src/constants';
5
+
6
+ describe('plugin-meetings', () => {
7
+ describe('controls-options-manager tests', () => {
8
+ describe('index', () => {
9
+ let request;
10
+
11
+ describe('class tests', () => {
12
+ it('can set and extract new values later on', () => {
13
+ const manager = new ControlsOptionsManager({});
14
+ assert.isUndefined(manager.getLocusUrl());
15
+ manager.set({
16
+ locusUrl: 'test/id',
17
+ })
18
+ assert(manager.getLocusUrl(), 'test/id');
19
+ });
20
+ });
21
+
22
+ describe('Mute On Entry', () => {
23
+ let manager;
24
+
25
+ beforeEach(() => {
26
+ request = {
27
+ request: sinon.stub().returns(Promise.resolve()),
28
+ };
29
+
30
+ manager = new ControlsOptionsManager(request);
31
+
32
+ manager.set({
33
+ locusUrl: 'test/id',
34
+ displayHints: [],
35
+ })
36
+ });
37
+
38
+ describe('setMuteOnEntry', () => {
39
+ it('rejects when correct display hint is not present enabled=false', () => {
40
+ const result = manager.setMuteOnEntry(false);
41
+
42
+ assert.notCalled(request.request);
43
+
44
+ assert.isRejected(result);
45
+ });
46
+
47
+ it('rejects when correct display hint is not present enabled=true', () => {
48
+ const result = manager.setMuteOnEntry(true);
49
+
50
+ assert.notCalled(request.request);
51
+
52
+ assert.isRejected(result);
53
+ });
54
+
55
+ it('can set mute on entry when the display hint is available enabled=true', () => {
56
+ manager.setDisplayHints(['ENABLE_MUTE_ON_ENTRY']);
57
+
58
+ const result = manager.setMuteOnEntry(true);
59
+
60
+ assert.calledWith(request.request, { uri: 'test/id/controls',
61
+ body: { muteOnEntry: { enabled: true } },
62
+ method: HTTP_VERBS.PATCH});
63
+
64
+ assert.deepEqual(result, request.request.firstCall.returnValue);
65
+ });
66
+
67
+ it('can set mute on entry when the display hint is available enabled=false', () => {
68
+ manager.setDisplayHints(['DISABLE_MUTE_ON_ENTRY']);
69
+
70
+ const result = manager.setMuteOnEntry(false);
71
+
72
+ assert.calledWith(request.request, { uri: 'test/id/controls',
73
+ body: { muteOnEntry: { enabled: false } },
74
+ method: HTTP_VERBS.PATCH});
75
+
76
+ assert.deepEqual(result, request.request.firstCall.returnValue);
77
+ });
78
+ });
79
+
80
+ describe('setDisallowUnmute', () => {
81
+ it('rejects when correct display hint is not present enabled=false', () => {
82
+ const result = manager.setDisallowUnmute(false);
83
+
84
+ assert.notCalled(request.request);
85
+
86
+ assert.isRejected(result);
87
+ });
88
+
89
+ it('rejects when correct display hint is not present enabled=true', () => {
90
+ const result = manager.setDisallowUnmute(true);
91
+
92
+ assert.notCalled(request.request);
93
+
94
+ assert.isRejected(result);
95
+ });
96
+
97
+ it('can set mute on entry when the display hint is available enabled=true', () => {
98
+ manager.setDisplayHints(['ENABLE_HARD_MUTE']);
99
+
100
+ const result = manager.setDisallowUnmute(true);
101
+
102
+ assert.calledWith(request.request, { uri: 'test/id/controls',
103
+ body: { disallowUnmute: { enabled: true } },
104
+ method: HTTP_VERBS.PATCH});
105
+
106
+ assert.deepEqual(result, request.request.firstCall.returnValue);
107
+ });
108
+
109
+ it('can set mute on entry when the display hint is available enabled=false', () => {
110
+ manager.setDisplayHints(['DISABLE_HARD_MUTE']);
111
+
112
+ const result = manager.setDisallowUnmute(false);
113
+
114
+ assert.calledWith(request.request, { uri: 'test/id/controls',
115
+ body: { disallowUnmute: { enabled: false } },
116
+ method: HTTP_VERBS.PATCH});
117
+
118
+ assert.deepEqual(result, request.request.firstCall.returnValue);
119
+ });
120
+ });
121
+ });
122
+ });
123
+ });
124
+ });
@@ -0,0 +1,66 @@
1
+ import ControlsOptionsUtil from '@webex/plugin-meetings/src/controls-options-manager/util';
2
+ import { assert } from 'chai';
3
+
4
+ describe('plugin-meetings', () => {
5
+ describe('controls-option-manager tests', () => {
6
+ describe('util tests', () => {
7
+
8
+ let locusInfo;
9
+
10
+ beforeEach(() => {
11
+ locusInfo = {
12
+ parsedLocus: {
13
+ info: {
14
+ userDisplayHints: [],
15
+ },
16
+ },
17
+ };
18
+ });
19
+
20
+ describe('canUserSetMuteOnEntry', () => {
21
+ it('can set mute on entry enable', () => {
22
+ locusInfo.parsedLocus.info.userDisplayHints.push('ENABLE_MUTE_ON_ENTRY');
23
+
24
+ assert.equal(ControlsOptionsUtil.canSetMuteOnEntry(locusInfo.parsedLocus.info.userDisplayHints), true);
25
+ });
26
+
27
+ it('can set mute on entry disable', () => {
28
+ locusInfo.parsedLocus.info.userDisplayHints.push('DISABLE_MUTE_ON_ENTRY');
29
+
30
+ assert.equal(ControlsOptionsUtil.canUnsetMuteOnEntry(locusInfo.parsedLocus.info.userDisplayHints), true);
31
+ });
32
+
33
+ it('rejects when correct display hint is not present for setting mute on entry', () => {
34
+ assert.equal(ControlsOptionsUtil.canSetMuteOnEntry(locusInfo.parsedLocus.info.userDisplayHints), false);
35
+ });
36
+
37
+ it('rejects when correct display hint is not present for unsetting mute on entry', () => {
38
+ assert.equal(ControlsOptionsUtil.canUnsetMuteOnEntry(locusInfo.parsedLocus.info.userDisplayHints), false);
39
+ });
40
+ });
41
+
42
+ describe('canSetDisallowUnmute', () => {
43
+ it('can set disallow unmute enable', () => {
44
+ locusInfo.parsedLocus.info.userDisplayHints.push('ENABLE_HARD_MUTE');
45
+
46
+ assert.equal(ControlsOptionsUtil.canSetDisallowUnmute(locusInfo.parsedLocus.info.userDisplayHints), true);
47
+ });
48
+
49
+ it('can set disallow unmute disable', () => {
50
+ locusInfo.parsedLocus.info.userDisplayHints.push('DISABLE_HARD_MUTE');
51
+
52
+ assert.equal(ControlsOptionsUtil.canUnsetDisallowUnmute(locusInfo.parsedLocus.info.userDisplayHints), true);
53
+ });
54
+
55
+ it('rejects when correct display hint is not present', () => {
56
+ assert.equal(ControlsOptionsUtil.canSetDisallowUnmute(locusInfo.parsedLocus.info.userDisplayHints), false);
57
+ });
58
+
59
+ it('rejects when correct display hint is not present', () => {
60
+ assert.equal(ControlsOptionsUtil.canUnsetDisallowUnmute(locusInfo.parsedLocus.info.userDisplayHints), false);
61
+ });
62
+ });
63
+
64
+ });
65
+ });
66
+ });
@@ -13,6 +13,10 @@ describe('plugin-meetings', () => {
13
13
  canStartRecording: null,
14
14
  canPauseRecording: null,
15
15
  canResumeRecording: null,
16
+ canSetMuteOnEntry: null,
17
+ canUnsetMuteOnEntry: null,
18
+ canSetDisallowUnmute: null,
19
+ canUnsetDisallowUnmute: null,
16
20
  canStopRecording: null,
17
21
  canRaiseHand: null,
18
22
  canLowerAllHands: null,
@@ -51,6 +55,10 @@ describe('plugin-meetings', () => {
51
55
  'canPauseRecording',
52
56
  'canResumeRecording',
53
57
  'canStopRecording',
58
+ 'canSetMuteOnEntry',
59
+ 'canUnsetMuteOnEntry',
60
+ 'canSetDisallowUnmute',
61
+ 'canUnsetDisallowUnmute',
54
62
  'canRaiseHand',
55
63
  'canLowerAllHands',
56
64
  'canLowerSomeoneElsesHand',
@@ -38,6 +38,7 @@ import Media from '@webex/plugin-meetings/src/media/index';
38
38
  import ReconnectionManager from '@webex/plugin-meetings/src/reconnection-manager';
39
39
  import MediaUtil from '@webex/plugin-meetings/src/media/util';
40
40
  import RecordingUtil from '@webex/plugin-meetings/src/recording-controller/util';
41
+ import ControlsOptionsUtil from '@webex/plugin-meetings/src/controls-options-manager/util';
41
42
  import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
42
43
  import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config';
43
44
  import TriggerProxy from '@webex/plugin-meetings/src/common/events/trigger-proxy';
@@ -4419,6 +4420,7 @@ describe('plugin-meetings', () => {
4419
4420
 
4420
4421
  meeting.members = {locusUrlUpdate: sinon.stub().returns(Promise.resolve(test1))};
4421
4422
  meeting.recordingController = {setLocusUrl: sinon.stub().returns(undefined)};
4423
+ meeting.controlsOptionsManager = {setLocusUrl: sinon.stub().returns(undefined)};
4422
4424
 
4423
4425
  meeting.breakouts.locusUrlUpdate = sinon.stub();
4424
4426
 
@@ -4430,6 +4432,7 @@ describe('plugin-meetings', () => {
4430
4432
  assert.calledOnceWithExactly(meeting.breakouts.locusUrlUpdate, newLocusUrl);
4431
4433
  assert.calledWith(meeting.members.locusUrlUpdate, newLocusUrl);
4432
4434
  assert.calledWith(meeting.recordingController.setLocusUrl, newLocusUrl);
4435
+ assert.calledWith(meeting.controlsOptionsManager.setLocusUrl, newLocusUrl);
4433
4436
  assert.equal(meeting.locusUrl, newLocusUrl);
4434
4437
  assert(meeting.locusId, '12345');
4435
4438
  done();
@@ -4870,6 +4873,10 @@ describe('plugin-meetings', () => {
4870
4873
  let canUserStopSpy;
4871
4874
  let canUserPauseSpy;
4872
4875
  let canUserResumeSpy;
4876
+ let canSetMuteOnEntrySpy;
4877
+ let canUnsetMuteOnEntrySpy;
4878
+ let canSetDisallowUnmuteSpy;
4879
+ let canUnsetDisallowUnmuteSpy;
4873
4880
  let canUserRaiseHandSpy;
4874
4881
  let bothLeaveAndEndMeetingAvailableSpy;
4875
4882
  let canUserLowerAllHandsSpy;
@@ -4887,6 +4894,10 @@ describe('plugin-meetings', () => {
4887
4894
  canUserStopSpy = sinon.spy(RecordingUtil, 'canUserStop');
4888
4895
  canUserPauseSpy = sinon.spy(RecordingUtil, 'canUserPause');
4889
4896
  canUserResumeSpy = sinon.spy(RecordingUtil, 'canUserResume');
4897
+ canSetMuteOnEntrySpy = sinon.spy(ControlsOptionsUtil, 'canSetMuteOnEntry');
4898
+ canUnsetMuteOnEntrySpy = sinon.spy(ControlsOptionsUtil, 'canUnsetMuteOnEntry');
4899
+ canSetDisallowUnmuteSpy = sinon.spy(ControlsOptionsUtil, 'canSetDisallowUnmute');
4900
+ canUnsetDisallowUnmuteSpy = sinon.spy(ControlsOptionsUtil, 'canUnsetDisallowUnmute');
4890
4901
  inMeetingActionsSetSpy = sinon.spy(meeting.inMeetingActions, 'set');
4891
4902
  canUserRaiseHandSpy = sinon.spy(MeetingUtil, 'canUserRaiseHand');
4892
4903
  canUserLowerAllHandsSpy = sinon.spy(MeetingUtil, 'canUserLowerAllHands');
@@ -4932,6 +4943,10 @@ describe('plugin-meetings', () => {
4932
4943
  assert.calledWith(canUserStopSpy, payload.info.userDisplayHints);
4933
4944
  assert.calledWith(canUserPauseSpy, payload.info.userDisplayHints);
4934
4945
  assert.calledWith(canUserResumeSpy, payload.info.userDisplayHints);
4946
+ assert.calledWith(canSetMuteOnEntrySpy, payload.info.userDisplayHints);
4947
+ assert.calledWith(canUnsetMuteOnEntrySpy, payload.info.userDisplayHints);
4948
+ assert.calledWith(canSetDisallowUnmuteSpy, payload.info.userDisplayHints);
4949
+ assert.calledWith(canUnsetDisallowUnmuteSpy, payload.info.userDisplayHints);
4935
4950
  assert.calledWith(canUserRaiseHandSpy, payload.info.userDisplayHints);
4936
4951
  assert.calledWith(bothLeaveAndEndMeetingAvailableSpy, payload.info.userDisplayHints);
4937
4952
  assert.calledWith(canUserLowerAllHandsSpy, payload.info.userDisplayHints);