@webex/plugin-meetings 2.38.1 → 2.39.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.
- package/dist/constants.js +5 -1
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/constants.js +14 -0
- package/dist/controls-options-manager/constants.js.map +1 -0
- package/dist/controls-options-manager/enums.js +15 -0
- package/dist/controls-options-manager/enums.js.map +1 -0
- package/dist/controls-options-manager/index.js +203 -0
- package/dist/controls-options-manager/index.js.map +1 -0
- package/dist/controls-options-manager/util.js +28 -0
- package/dist/controls-options-manager/util.js.map +1 -0
- package/dist/meeting/in-meeting-actions.js +8 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +54 -28
- package/dist/meeting/index.js.map +1 -1
- package/package.json +17 -17
- package/src/constants.ts +4 -0
- package/src/controls-options-manager/constants.ts +5 -0
- package/src/controls-options-manager/enums.ts +6 -0
- package/src/controls-options-manager/index.ts +183 -0
- package/src/controls-options-manager/util.ts +20 -0
- package/src/meeting/in-meeting-actions.ts +16 -0
- package/src/meeting/index.ts +49 -26
- package/test/unit/spec/controls-options-manager/index.js +124 -0
- package/test/unit/spec/controls-options-manager/util.js +66 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +8 -0
- package/test/unit/spec/meeting/index.js +15 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/plugin-meetings",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.39.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "Cisco EULA (https://www.cisco.com/c/en/us/products/end-user-license-agreement.html)",
|
|
6
6
|
"contributors": [
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"build": "yarn run -T tsc --declaration true --declarationDir ./types"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@webex/plugin-meetings": "2.
|
|
36
|
-
"@webex/test-helper-chai": "2.
|
|
37
|
-
"@webex/test-helper-mocha": "2.
|
|
38
|
-
"@webex/test-helper-mock-webex": "2.
|
|
39
|
-
"@webex/test-helper-retry": "2.
|
|
40
|
-
"@webex/test-helper-test-users": "2.
|
|
35
|
+
"@webex/plugin-meetings": "2.39.0",
|
|
36
|
+
"@webex/test-helper-chai": "2.39.0",
|
|
37
|
+
"@webex/test-helper-mocha": "2.39.0",
|
|
38
|
+
"@webex/test-helper-mock-webex": "2.39.0",
|
|
39
|
+
"@webex/test-helper-retry": "2.39.0",
|
|
40
|
+
"@webex/test-helper-test-users": "2.39.0",
|
|
41
41
|
"chai": "^4.3.4",
|
|
42
42
|
"chai-as-promised": "^7.1.1",
|
|
43
43
|
"jsdom-global": "3.0.2",
|
|
@@ -45,18 +45,18 @@
|
|
|
45
45
|
"typescript": "^4.7.4"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@webex/common": "2.
|
|
48
|
+
"@webex/common": "2.39.0",
|
|
49
49
|
"@webex/internal-media-core": "0.0.7-beta",
|
|
50
|
-
"@webex/internal-plugin-conversation": "2.
|
|
51
|
-
"@webex/internal-plugin-device": "2.
|
|
52
|
-
"@webex/internal-plugin-mercury": "2.
|
|
53
|
-
"@webex/internal-plugin-metrics": "2.
|
|
54
|
-
"@webex/internal-plugin-support": "2.
|
|
55
|
-
"@webex/internal-plugin-user": "2.
|
|
56
|
-
"@webex/plugin-people": "2.
|
|
57
|
-
"@webex/plugin-rooms": "2.
|
|
50
|
+
"@webex/internal-plugin-conversation": "2.39.0",
|
|
51
|
+
"@webex/internal-plugin-device": "2.39.0",
|
|
52
|
+
"@webex/internal-plugin-mercury": "2.39.0",
|
|
53
|
+
"@webex/internal-plugin-metrics": "2.39.0",
|
|
54
|
+
"@webex/internal-plugin-support": "2.39.0",
|
|
55
|
+
"@webex/internal-plugin-user": "2.39.0",
|
|
56
|
+
"@webex/plugin-people": "2.39.0",
|
|
57
|
+
"@webex/plugin-rooms": "2.39.0",
|
|
58
58
|
"@webex/ts-sdp": "1.0.1",
|
|
59
|
-
"@webex/webex-core": "2.
|
|
59
|
+
"@webex/webex-core": "2.39.0",
|
|
60
60
|
"bowser": "^2.11.0",
|
|
61
61
|
"btoa": "^1.2.1",
|
|
62
62
|
"dotenv": "^4.0.0",
|
package/src/constants.ts
CHANGED
|
@@ -724,6 +724,10 @@ export const DISPLAY_HINTS = {
|
|
|
724
724
|
TRANSCRIPTION_CONTROL_STOP: 'TRANSCRIPTION_CONTROL_STOP',
|
|
725
725
|
WEBEX_ASSISTANT_STATUS_ACTIVE: 'WEBEX_ASSISTANT_STATUS_ACTIVE',
|
|
726
726
|
WAITING_FOR_OTHERS: 'WAITING_FOR_OTHERS',
|
|
727
|
+
ENABLE_MUTE_ON_ENTRY: 'ENABLE_MUTE_ON_ENTRY',
|
|
728
|
+
DISABLE_MUTE_ON_ENTRY: 'DISABLE_MUTE_ON_ENTRY',
|
|
729
|
+
ENABLE_HARD_MUTE: 'ENABLE_HARD_MUTE',
|
|
730
|
+
DISABLE_HARD_MUTE: 'DISABLE_HARD_MUTE',
|
|
727
731
|
};
|
|
728
732
|
|
|
729
733
|
export const SELF_ROLES = {
|
|
@@ -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;
|
|
@@ -57,6 +61,14 @@ export default class InMeetingActions implements IInMeetingActions {
|
|
|
57
61
|
|
|
58
62
|
canStopRecording = null;
|
|
59
63
|
|
|
64
|
+
canSetMuteOnEntry = null;
|
|
65
|
+
|
|
66
|
+
canUnsetMuteOnEntry = null;
|
|
67
|
+
|
|
68
|
+
canSetDisallowUnmute = null;
|
|
69
|
+
|
|
70
|
+
canUnsetDisallowUnmute = null;
|
|
71
|
+
|
|
60
72
|
canRaiseHand = null;
|
|
61
73
|
|
|
62
74
|
canLowerAllHands = null;
|
|
@@ -93,6 +105,10 @@ export default class InMeetingActions implements IInMeetingActions {
|
|
|
93
105
|
canLock: this.canLock,
|
|
94
106
|
canUnlock: this.canUnlock,
|
|
95
107
|
canAssignHost: this.canAssignHost,
|
|
108
|
+
canSetMuteOnEntry: this.canSetMuteOnEntry,
|
|
109
|
+
canUnsetMuteOnEntry: this.canUnsetMuteOnEntry,
|
|
110
|
+
canSetDisallowUnmute: this.canSetDisallowUnmute,
|
|
111
|
+
canUnsetDisallowUnmute: this.canUnsetDisallowUnmute,
|
|
96
112
|
canStartRecording: this.canStartRecording,
|
|
97
113
|
canPauseRecording: this.canPauseRecording,
|
|
98
114
|
canResumeRecording: this.canResumeRecording,
|
package/src/meeting/index.ts
CHANGED
|
@@ -31,6 +31,7 @@ import MeetingRequest from './request';
|
|
|
31
31
|
import Members from '../members/index';
|
|
32
32
|
import MeetingUtil from './util';
|
|
33
33
|
import RecordingUtil from '../recording-controller/util';
|
|
34
|
+
import ControlsOptionsUtil from '../controls-options-manager/util';
|
|
34
35
|
import MediaUtil from '../media/util';
|
|
35
36
|
import Transcription from '../transcription';
|
|
36
37
|
import PasswordError from '../common/errors/password-error';
|
|
@@ -90,6 +91,7 @@ import {Reaction, ReactionType, SkinToneType} from '../reactions/reactions.type'
|
|
|
90
91
|
|
|
91
92
|
import InMeetingActions from './in-meeting-actions';
|
|
92
93
|
import RecordingController from '../recording-controller';
|
|
94
|
+
import ControlsOptionsManager from '../controls-options-manager';
|
|
93
95
|
|
|
94
96
|
const {isBrowser} = BrowserDetection();
|
|
95
97
|
|
|
@@ -455,6 +457,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
455
457
|
queuedMediaUpdates: any[];
|
|
456
458
|
recording: any;
|
|
457
459
|
recordingController: RecordingController;
|
|
460
|
+
controlsOptionsManager: ControlsOptionsManager;
|
|
458
461
|
requiredCaptcha: any;
|
|
459
462
|
shareStatus: string;
|
|
460
463
|
statsAnalyzer: StatsAnalyzer;
|
|
@@ -1002,6 +1005,18 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1002
1005
|
displayHints: [],
|
|
1003
1006
|
});
|
|
1004
1007
|
|
|
1008
|
+
/**
|
|
1009
|
+
* The class that helps to control recording functions: start, stop, pause, resume, etc
|
|
1010
|
+
* @instance
|
|
1011
|
+
* @type {ControlsOptionsManager}
|
|
1012
|
+
* @public
|
|
1013
|
+
* @memberof Meeting
|
|
1014
|
+
*/
|
|
1015
|
+
this.controlsOptionsManager = new ControlsOptionsManager(this.meetingRequest, {
|
|
1016
|
+
locusUrl: this.locusInfo?.url,
|
|
1017
|
+
displayHints: [],
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1005
1020
|
this.setUpLocusInfoListeners();
|
|
1006
1021
|
this.locusInfo.init(attrs.locus ? attrs.locus : {});
|
|
1007
1022
|
this.hasJoinedOnce = false;
|
|
@@ -2020,6 +2035,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2020
2035
|
this.locusUrl = payload;
|
|
2021
2036
|
this.locusId = this.locusUrl?.split('/').pop();
|
|
2022
2037
|
this.recordingController.setLocusUrl(this.locusUrl);
|
|
2038
|
+
this.controlsOptionsManager.setLocusUrl(this.locusUrl);
|
|
2023
2039
|
});
|
|
2024
2040
|
}
|
|
2025
2041
|
|
|
@@ -2085,6 +2101,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2085
2101
|
canAdmitParticipant: MeetingUtil.canAdmitParticipant(payload.info.userDisplayHints),
|
|
2086
2102
|
canLock: MeetingUtil.canUserLock(payload.info.userDisplayHints),
|
|
2087
2103
|
canUnlock: MeetingUtil.canUserUnlock(payload.info.userDisplayHints),
|
|
2104
|
+
canSetDisallowUnmute: ControlsOptionsUtil.canSetDisallowUnmute(
|
|
2105
|
+
payload.info.userDisplayHints
|
|
2106
|
+
),
|
|
2107
|
+
canUnsetDisallowUnmute: ControlsOptionsUtil.canUnsetDisallowUnmute(
|
|
2108
|
+
payload.info.userDisplayHints
|
|
2109
|
+
),
|
|
2110
|
+
canSetMuteOnEntry: ControlsOptionsUtil.canSetMuteOnEntry(payload.info.userDisplayHints),
|
|
2111
|
+
canUnsetMuteOnEntry: ControlsOptionsUtil.canUnsetMuteOnEntry(
|
|
2112
|
+
payload.info.userDisplayHints
|
|
2113
|
+
),
|
|
2088
2114
|
canStartRecording: RecordingUtil.canUserStart(payload.info.userDisplayHints),
|
|
2089
2115
|
canStopRecording: RecordingUtil.canUserStop(payload.info.userDisplayHints),
|
|
2090
2116
|
canPauseRecording: RecordingUtil.canUserPause(payload.info.userDisplayHints),
|
|
@@ -2113,6 +2139,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2113
2139
|
});
|
|
2114
2140
|
|
|
2115
2141
|
this.recordingController.setDisplayHints(payload.info.userDisplayHints);
|
|
2142
|
+
this.controlsOptionsManager.setDisplayHints(payload.info.userDisplayHints);
|
|
2116
2143
|
|
|
2117
2144
|
if (changed) {
|
|
2118
2145
|
Trigger.trigger(
|
|
@@ -4900,32 +4927,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4900
4927
|
)
|
|
4901
4928
|
)
|
|
4902
4929
|
.then(() => this.checkForStopShare(mediaSettings.sendShare, previousSendShareStatus))
|
|
4903
|
-
.then((startShare) => {
|
|
4904
|
-
// This is a special case if we do an /floor grant followed by /media
|
|
4905
|
-
// we actually get a OFFER from the server and a GLAR condition happens
|
|
4906
|
-
if (startShare) {
|
|
4907
|
-
// We are assuming that the clients are connected when doing an update
|
|
4908
|
-
// @ts-ignore
|
|
4909
|
-
return this.share();
|
|
4910
|
-
}
|
|
4911
|
-
|
|
4912
|
-
return Promise.resolve();
|
|
4913
|
-
})
|
|
4914
|
-
.then(() =>
|
|
4915
|
-
logRequest(
|
|
4916
|
-
this.roap.sendRoapMediaRequest({
|
|
4917
|
-
sdp: this.mediaProperties.peerConnection.sdp,
|
|
4918
|
-
roapSeq: this.roapSeq,
|
|
4919
|
-
meeting: this, // or can pass meeting ID
|
|
4920
|
-
}),
|
|
4921
|
-
{
|
|
4922
|
-
header: `${LOG_HEADER} sendRoapMediaRequest being sent`,
|
|
4923
|
-
success: `${LOG_HEADER} sendRoadMediaRequest successful`,
|
|
4924
|
-
failure: `${LOG_HEADER} Error updateMedia on send roap media request, `,
|
|
4925
|
-
}
|
|
4926
|
-
)
|
|
4927
|
-
)
|
|
4928
|
-
.then(() => this.checkForStopShare(mediaSettings.sendShare, previousSendShareStatus))
|
|
4929
4930
|
.then((startShare) => {
|
|
4930
4931
|
// This is a special case if we do an /floor grant followed by /media
|
|
4931
4932
|
// we actually get a OFFER from the server and a GLAR condition happens
|
|
@@ -5552,6 +5553,28 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5552
5553
|
return this.recordingController.startRecording();
|
|
5553
5554
|
}
|
|
5554
5555
|
|
|
5556
|
+
/**
|
|
5557
|
+
* set the mute on entry flag for participants if you're the host
|
|
5558
|
+
* @returns {Promise}
|
|
5559
|
+
* @param {boolean} enabled
|
|
5560
|
+
* @public
|
|
5561
|
+
* @memberof Meeting
|
|
5562
|
+
*/
|
|
5563
|
+
public setMuteOnEntry(enabled: boolean) {
|
|
5564
|
+
return this.controlsOptionsManager.setMuteOnEntry(enabled);
|
|
5565
|
+
}
|
|
5566
|
+
|
|
5567
|
+
/**
|
|
5568
|
+
* set the disallow unmute flag for participants if you're the host
|
|
5569
|
+
* @returns {Promise}
|
|
5570
|
+
* @param {boolean} enabled
|
|
5571
|
+
* @public
|
|
5572
|
+
* @memberof Meeting
|
|
5573
|
+
*/
|
|
5574
|
+
public setDisallowUnmute(enabled: boolean) {
|
|
5575
|
+
return this.controlsOptionsManager.setDisallowUnmute(enabled);
|
|
5576
|
+
}
|
|
5577
|
+
|
|
5555
5578
|
/**
|
|
5556
5579
|
* End the recording of this meeting
|
|
5557
5580
|
* @returns {Promise}
|
|
@@ -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,
|
|
@@ -49,6 +53,10 @@ describe('plugin-meetings', () => {
|
|
|
49
53
|
'canPauseRecording',
|
|
50
54
|
'canResumeRecording',
|
|
51
55
|
'canStopRecording',
|
|
56
|
+
'canSetMuteOnEntry',
|
|
57
|
+
'canUnsetMuteOnEntry',
|
|
58
|
+
'canSetDisallowUnmute',
|
|
59
|
+
'canUnsetDisallowUnmute',
|
|
52
60
|
'canRaiseHand',
|
|
53
61
|
'canLowerAllHands',
|
|
54
62
|
'canLowerSomeoneElsesHand',
|