@webex/plugin-meetings 2.19.2 → 2.20.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 +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/locus-info/index.js +9 -0
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +29 -7
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/meeting/index.js +8 -0
- package/dist/meeting/index.js.map +1 -1
- package/package.json +5 -5
- package/src/constants.ts +2 -0
- package/src/locus-info/index.js +11 -0
- package/src/locus-info/selfUtils.js +13 -0
- package/src/meeting/index.js +14 -0
- package/test/unit/spec/locus-info/index.js +46 -0
- package/test/unit/spec/locus-info/selfUtils.js +40 -0
- package/test/unit/spec/meeting/index.js +20 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/plugin-meetings",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"contributors": [
|
|
@@ -24,15 +24,15 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@babel/runtime-corejs2": "^7.14.8",
|
|
27
|
-
"@webex/webex-core": "2.
|
|
28
|
-
"@webex/internal-plugin-mercury": "2.
|
|
29
|
-
"@webex/internal-plugin-conversation": "2.
|
|
27
|
+
"@webex/webex-core": "2.20.0",
|
|
28
|
+
"@webex/internal-plugin-mercury": "2.20.0",
|
|
29
|
+
"@webex/internal-plugin-conversation": "2.20.0",
|
|
30
30
|
"webrtc-adapter": "^7.7.0",
|
|
31
31
|
"lodash": "^4.17.21",
|
|
32
32
|
"uuid": "^3.3.2",
|
|
33
33
|
"global": "^4.4.0",
|
|
34
34
|
"ip-anonymize": "^0.1.0",
|
|
35
|
-
"@webex/common": "2.
|
|
35
|
+
"@webex/common": "2.20.0",
|
|
36
36
|
"bowser": "^2.11.0",
|
|
37
37
|
"sdp-transform": "^2.12.0",
|
|
38
38
|
"btoa": "^1.2.1",
|
package/src/constants.ts
CHANGED
|
@@ -284,6 +284,7 @@ export const EVENT_TRIGGERS = {
|
|
|
284
284
|
MEETING_SELF_UNMUTED_BY_OTHERS: 'meeting:self:unmutedByOthers',
|
|
285
285
|
MEETING_SELF_REQUESTED_TO_UNMUTE: 'meeting:self:requestedToUnmute',
|
|
286
286
|
MEETING_SELF_PHONE_AUDIO_UPDATE: 'meeting:self:phoneAudioUpdate',
|
|
287
|
+
MEETING_CONTROLS_LAYOUT_UPDATE: 'meeting:layout:update',
|
|
287
288
|
MEMBERS_UPDATE: 'members:update',
|
|
288
289
|
MEMBERS_CONTENT_UPDATE: 'members:content:update',
|
|
289
290
|
MEMBERS_HOST_UPDATE: 'members:host:update',
|
|
@@ -486,6 +487,7 @@ export const LOCUS = {
|
|
|
486
487
|
|
|
487
488
|
export const LOCUSINFO = {
|
|
488
489
|
EVENTS: {
|
|
490
|
+
CONTROLS_MEETING_LAYOUT_UPDATED: 'CONTROLS_MEETING_LAYOUT_UPDATED',
|
|
489
491
|
CONTROLS_RECORDING_UPDATED: 'CONTROLS_RECORDING_UPDATED',
|
|
490
492
|
CONTROLS_MEETING_TRANSCRIBE_UPDATED: 'CONTROLS_MEETING_TRANSCRIBE_UPDATED',
|
|
491
493
|
CONTROLS_MEETING_CONTAINER_UPDATED: 'CONTROLS_MEETING_CONTAINER_UPDATED',
|
package/src/locus-info/index.js
CHANGED
|
@@ -927,6 +927,17 @@ export default class LocusInfo extends EventsScope {
|
|
|
927
927
|
this.compareAndUpdateFlags.compareHostAndSelf = false;
|
|
928
928
|
}
|
|
929
929
|
|
|
930
|
+
if (parsedSelves.updates.layoutChanged) {
|
|
931
|
+
this.emitScoped(
|
|
932
|
+
{
|
|
933
|
+
file: 'locus-info',
|
|
934
|
+
function: 'updateSelf'
|
|
935
|
+
},
|
|
936
|
+
LOCUSINFO.EVENTS.CONTROLS_MEETING_LAYOUT_UPDATED,
|
|
937
|
+
{layout: parsedSelves.current.layout}
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
|
|
930
941
|
if (parsedSelves.updates.isMediaInactiveOrReleased) {
|
|
931
942
|
this.emitScoped(
|
|
932
943
|
{
|
|
@@ -58,12 +58,15 @@ SelfUtils.parse = (self, deviceId) => {
|
|
|
58
58
|
removed: self.removed,
|
|
59
59
|
roles: SelfUtils.getRoles(self),
|
|
60
60
|
isUserUnadmitted: self.state === _IDLE_ && joinedWith?.intent?.type === _WAIT_,
|
|
61
|
+
layout: SelfUtils.getLayout(self)
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
return null;
|
|
65
66
|
};
|
|
66
67
|
|
|
68
|
+
SelfUtils.getLayout = (self) => (Array.isArray(self?.controls?.layouts) ? self.controls.layouts[0].type : undefined);
|
|
69
|
+
|
|
67
70
|
SelfUtils.getRoles = (self) => (self?.controls?.role?.roles || []).reduce((roles, role) => {
|
|
68
71
|
if (role.hasRole) {
|
|
69
72
|
roles.push(role.type);
|
|
@@ -85,6 +88,7 @@ SelfUtils.getSelves = (oldSelf, newSelf, deviceId) => {
|
|
|
85
88
|
updates.moderatorChanged = SelfUtils.moderatorChanged(previous, current);
|
|
86
89
|
updates.isMediaInactiveOrReleased = SelfUtils.wasMediaInactiveOrReleased(previous, current);
|
|
87
90
|
updates.isUserObserving = SelfUtils.isDeviceObserving(previous, current);
|
|
91
|
+
updates.layoutChanged = SelfUtils.layoutChanged(previous, current);
|
|
88
92
|
|
|
89
93
|
updates.isMediaInactive = SelfUtils.isMediaInactive(previous, current);
|
|
90
94
|
updates.audioStateChange = previous?.currentMediaStatus.audio !== current.currentMediaStatus.audio;
|
|
@@ -105,6 +109,15 @@ SelfUtils.getSelves = (oldSelf, newSelf, deviceId) => {
|
|
|
105
109
|
*/
|
|
106
110
|
SelfUtils.isJoined = (self) => self?.state === _JOINED_;
|
|
107
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Validate if the Meeting Layout Controls Layout has changed.
|
|
114
|
+
*
|
|
115
|
+
* @param {Self} previous - Previous self state
|
|
116
|
+
* @param {Self} current - Current self state [per event]
|
|
117
|
+
* @returns {boolean} - If the MEeting Layout Controls Layout has changed.
|
|
118
|
+
*/
|
|
119
|
+
SelfUtils.layoutChanged = (previous, current) => current?.layout && previous?.layout !== current?.layout;
|
|
120
|
+
|
|
108
121
|
|
|
109
122
|
SelfUtils.isMediaInactive = (previous, current) => {
|
|
110
123
|
if (
|
package/src/meeting/index.js
CHANGED
|
@@ -5216,6 +5216,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5216
5216
|
}
|
|
5217
5217
|
this.lastVideoLayoutInfo = cloneDeep(layoutInfo);
|
|
5218
5218
|
|
|
5219
|
+
this.locusInfo.once(LOCUSINFO.EVENTS.CONTROLS_MEETING_LAYOUT_UPDATED, (envelope) => {
|
|
5220
|
+
Trigger.trigger(
|
|
5221
|
+
this,
|
|
5222
|
+
{
|
|
5223
|
+
file: 'meeting/index',
|
|
5224
|
+
function: 'changeVideoLayout',
|
|
5225
|
+
},
|
|
5226
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_LAYOUT_UPDATE,
|
|
5227
|
+
{
|
|
5228
|
+
layout: envelope.layout
|
|
5229
|
+
}
|
|
5230
|
+
);
|
|
5231
|
+
});
|
|
5232
|
+
|
|
5219
5233
|
return this.meetingRequest
|
|
5220
5234
|
.changeVideoLayoutDebounced({
|
|
5221
5235
|
locusUrl: this.locusInfo.self.url,
|
|
@@ -428,6 +428,52 @@ describe('plugin-meetings', () => {
|
|
|
428
428
|
});
|
|
429
429
|
|
|
430
430
|
describe('#updateSelf', () => {
|
|
431
|
+
it('should trigger CONTROLS_MEETING_LAYOUT_UPDATED when the meeting layout controls change', () => {
|
|
432
|
+
const layoutType = 'EXAMPLE TYPE';
|
|
433
|
+
|
|
434
|
+
locusInfo.self = undefined;
|
|
435
|
+
const selfWithLayoutChanged = cloneDeep(self);
|
|
436
|
+
|
|
437
|
+
selfWithLayoutChanged.controls.layouts = [{
|
|
438
|
+
type: layoutType,
|
|
439
|
+
}];
|
|
440
|
+
|
|
441
|
+
locusInfo.emitScoped = sinon.stub();
|
|
442
|
+
locusInfo.updateSelf(selfWithLayoutChanged, []);
|
|
443
|
+
|
|
444
|
+
assert.calledWith(locusInfo.emitScoped, {
|
|
445
|
+
file: 'locus-info',
|
|
446
|
+
function: 'updateSelf'
|
|
447
|
+
},
|
|
448
|
+
LOCUSINFO.EVENTS.CONTROLS_MEETING_LAYOUT_UPDATED,
|
|
449
|
+
{layout: layoutType});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('should not trigger CONTROLS_MEETING_LAYOUT_UPDATED when the meeting layout controls did not change', () => {
|
|
453
|
+
const layoutType = 'EXAMPLE TYPE';
|
|
454
|
+
|
|
455
|
+
locusInfo.self = undefined;
|
|
456
|
+
const selfWithLayoutChanged = cloneDeep(self);
|
|
457
|
+
|
|
458
|
+
selfWithLayoutChanged.controls.layouts = [{
|
|
459
|
+
type: layoutType,
|
|
460
|
+
}];
|
|
461
|
+
|
|
462
|
+
// Set the layout prior to stubbing to validate it does not change.
|
|
463
|
+
locusInfo.updateSelf(selfWithLayoutChanged, []);
|
|
464
|
+
|
|
465
|
+
locusInfo.emitScoped = sinon.stub();
|
|
466
|
+
|
|
467
|
+
locusInfo.updateSelf(selfWithLayoutChanged, []);
|
|
468
|
+
|
|
469
|
+
assert.neverCalledWith(locusInfo.emitScoped, {
|
|
470
|
+
file: 'locus-info',
|
|
471
|
+
function: 'updateSelf'
|
|
472
|
+
},
|
|
473
|
+
LOCUSINFO.EVENTS.CONTROLS_MEETING_LAYOUT_UPDATED,
|
|
474
|
+
{layout: layoutType});
|
|
475
|
+
});
|
|
476
|
+
|
|
431
477
|
it('should trigger MEDIA_INACTIVITY on server media inactivity', () => {
|
|
432
478
|
locusInfo.self = self;
|
|
433
479
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {assert} from '@webex/test-helper-chai';
|
|
2
2
|
import Sinon from 'sinon';
|
|
3
|
+
import {cloneDeep} from 'lodash';
|
|
3
4
|
|
|
4
5
|
import SelfUtils from '@webex/plugin-meetings/src/locus-info/selfUtils';
|
|
5
6
|
|
|
@@ -7,6 +8,23 @@ import {self} from './selfConstant';
|
|
|
7
8
|
|
|
8
9
|
describe('plugin-meetings', () => {
|
|
9
10
|
describe('selfUtils', () => {
|
|
11
|
+
describe('layoutChanged', () => {
|
|
12
|
+
it('should return true if the layout has changed', () => {
|
|
13
|
+
const parsedSelf = SelfUtils.parse(self);
|
|
14
|
+
const clonedSelf = cloneDeep(parsedSelf);
|
|
15
|
+
|
|
16
|
+
clonedSelf.layout = 'DIFFERENT';
|
|
17
|
+
|
|
18
|
+
assert.deepEqual(SelfUtils.layoutChanged(parsedSelf, clonedSelf), true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should return false if the layout has not changed', () => {
|
|
22
|
+
const parsedSelf = SelfUtils.parse(self);
|
|
23
|
+
|
|
24
|
+
assert.deepEqual(SelfUtils.layoutChanged(parsedSelf, parsedSelf), false);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
10
28
|
describe('parse', () => {
|
|
11
29
|
it('parse calls getRoles and returns the resulting roles', () => {
|
|
12
30
|
const getRolesSpy = Sinon.spy(SelfUtils, 'getRoles');
|
|
@@ -17,6 +35,28 @@ describe('plugin-meetings', () => {
|
|
|
17
35
|
|
|
18
36
|
assert.deepEqual(parsedSelf.roles, ['PRESENTER']);
|
|
19
37
|
});
|
|
38
|
+
|
|
39
|
+
it('calls getLayout and returns the resulting layout', () => {
|
|
40
|
+
const spy = Sinon.spy(SelfUtils, 'getLayout');
|
|
41
|
+
const parsedSelf = SelfUtils.parse(self);
|
|
42
|
+
|
|
43
|
+
assert.calledWith(spy, self);
|
|
44
|
+
assert.deepEqual(parsedSelf.layout, self.controls.layouts[0].type);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('getLayout', () => {
|
|
49
|
+
it('should get supplied layout', () => {
|
|
50
|
+
assert.deepEqual(SelfUtils.getLayout(self), self.controls.layouts[0].type);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should return undefined if the new self does not have a provided layout', () => {
|
|
54
|
+
const mutatedSelf = cloneDeep(self);
|
|
55
|
+
|
|
56
|
+
delete mutatedSelf.controls.layouts;
|
|
57
|
+
|
|
58
|
+
assert.deepEqual(SelfUtils.getLayout(mutatedSelf), undefined);
|
|
59
|
+
});
|
|
20
60
|
});
|
|
21
61
|
|
|
22
62
|
describe('getRoles', () => {
|
|
@@ -10,6 +10,17 @@ import {assert} from '@webex/test-helper-chai';
|
|
|
10
10
|
import {Credentials} from '@webex/webex-core';
|
|
11
11
|
import Support from '@webex/internal-plugin-support';
|
|
12
12
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
13
|
+
import {
|
|
14
|
+
FLOOR_ACTION,
|
|
15
|
+
SHARE_STATUS,
|
|
16
|
+
MEETING_INFO_FAILURE_REASON,
|
|
17
|
+
PASSWORD_STATUS,
|
|
18
|
+
EVENTS,
|
|
19
|
+
EVENT_TRIGGERS,
|
|
20
|
+
_SIP_URI_,
|
|
21
|
+
_MEETING_ID_,
|
|
22
|
+
LOCUSINFO,
|
|
23
|
+
} from '@webex/plugin-meetings/src/constants';
|
|
13
24
|
|
|
14
25
|
import * as StatsAnalyzerModule from '@webex/plugin-meetings/src/statsAnalyzer';
|
|
15
26
|
import EventsScope from '@webex/plugin-meetings/src/common/events/events-scope';
|
|
@@ -31,16 +42,6 @@ import TriggerProxy from '@webex/plugin-meetings/src/common/events/trigger-proxy
|
|
|
31
42
|
import BrowserDetection from '@webex/plugin-meetings/src/common/browser-detection';
|
|
32
43
|
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
33
44
|
import {eventType} from '@webex/plugin-meetings/src/metrics/config';
|
|
34
|
-
import {
|
|
35
|
-
FLOOR_ACTION,
|
|
36
|
-
SHARE_STATUS,
|
|
37
|
-
MEETING_INFO_FAILURE_REASON,
|
|
38
|
-
PASSWORD_STATUS,
|
|
39
|
-
EVENTS,
|
|
40
|
-
EVENT_TRIGGERS,
|
|
41
|
-
_SIP_URI_,
|
|
42
|
-
_MEETING_ID_,
|
|
43
|
-
} from '@webex/plugin-meetings/src/constants';
|
|
44
45
|
import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
|
|
45
46
|
|
|
46
47
|
import locus from '../fixture/locus';
|
|
@@ -1912,6 +1913,15 @@ describe('plugin-meetings', () => {
|
|
|
1912
1913
|
};
|
|
1913
1914
|
});
|
|
1914
1915
|
|
|
1916
|
+
it('should listen once for CONTROLS_MEETING_LAYOUT_UPDATED', async () => {
|
|
1917
|
+
// const spy = sinon.spy(TriggerProxy, 'trigger');
|
|
1918
|
+
const spy = sinon.spy(meeting.locusInfo, 'once');
|
|
1919
|
+
|
|
1920
|
+
await meeting.changeVideoLayout('Equal');
|
|
1921
|
+
|
|
1922
|
+
assert.calledWith(spy, LOCUSINFO.EVENTS.CONTROLS_MEETING_LAYOUT_UPDATED);
|
|
1923
|
+
});
|
|
1924
|
+
|
|
1915
1925
|
it('should have receiveVideo true and remote video track should exist', () => {
|
|
1916
1926
|
assert.equal(meeting.mediaProperties.mediaDirection.receiveVideo, true);
|
|
1917
1927
|
assert.exists(meeting.mediaProperties.remoteVideoTrack);
|