@webex/plugin-meetings 2.26.2 → 2.28.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 +10 -3
- package/dist/constants.js.map +1 -1
- package/dist/locus-info/embeddedAppsUtils.js +88 -0
- package/dist/locus-info/embeddedAppsUtils.js.map +1 -0
- package/dist/locus-info/index.js +26 -1
- package/dist/locus-info/index.js.map +1 -1
- package/dist/meeting/index.js +330 -308
- package/dist/meeting/index.js.map +1 -1
- package/package.json +5 -5
- package/src/constants.ts +8 -1
- package/src/locus-info/embeddedAppsUtils.js +51 -0
- package/src/locus-info/index.js +27 -0
- package/src/meeting/index.js +23 -0
- package/test/unit/spec/locus-info/embeddedAppsUtils.js +102 -0
- package/test/unit/spec/locus-info/index.js +54 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/plugin-meetings",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.28.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.28.0",
|
|
28
|
+
"@webex/internal-plugin-mercury": "2.28.0",
|
|
29
|
+
"@webex/internal-plugin-conversation": "2.28.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.28.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
|
@@ -302,6 +302,7 @@ export const EVENT_TRIGGERS = {
|
|
|
302
302
|
MEETING_ACTIONS_UPDATE: 'meeting:actionsUpdate',
|
|
303
303
|
MEETING_STATE_CHANGE: 'meeting:stateChange',
|
|
304
304
|
MEETING_MEETING_CONTAINER_UPDATE: 'meeting:meetingContainer:update',
|
|
305
|
+
MEETING_EMBEDDED_APPS_UPDATE: 'meeting:embeddedApps:update',
|
|
305
306
|
MEDIA_QUALITY: 'media:quality',
|
|
306
307
|
MEETINGS_NETWORK_DISCONNECTED: 'network:disconnected',
|
|
307
308
|
MEETINGS_NETWORK_CONNECTED: 'network:connected',
|
|
@@ -508,7 +509,8 @@ export const LOCUSINFO = {
|
|
|
508
509
|
MEETING_LOCKED: 'MEETING_LOCKED',
|
|
509
510
|
MEETING_UNLOCKED: 'MEETING_UNLOCKED',
|
|
510
511
|
SELF_OBSERVING: 'SELF_OBSERVING',
|
|
511
|
-
DISCONNECT_DUE_TO_INACTIVITY: 'DISCONNECT_DUE_TO_INACTIVITY'
|
|
512
|
+
DISCONNECT_DUE_TO_INACTIVITY: 'DISCONNECT_DUE_TO_INACTIVITY',
|
|
513
|
+
EMBEDDED_APPS_UPDATED: 'EMBEDDED_APPS_UPDATED'
|
|
512
514
|
}
|
|
513
515
|
};
|
|
514
516
|
|
|
@@ -1053,3 +1055,8 @@ export const BNR_STATUS = {
|
|
|
1053
1055
|
SHOULD_DISABLE: 'SHOULD_DISABLE',
|
|
1054
1056
|
NOT_ENABLED: 'NOT_ENABLED'
|
|
1055
1057
|
};
|
|
1058
|
+
|
|
1059
|
+
export const EMBEDDED_APP_TYPES = {
|
|
1060
|
+
SLIDO: 'SLIDO',
|
|
1061
|
+
OTHER: 'OTHER'
|
|
1062
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {EMBEDDED_APP_TYPES} from '../constants';
|
|
2
|
+
|
|
3
|
+
const EmbeddedAppsUtils = {};
|
|
4
|
+
|
|
5
|
+
const SLIDO_REGEX = /.sli.do\//;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parse the relevant values that we care about
|
|
9
|
+
* @param {Object} embeddedApp - raw embedded app object
|
|
10
|
+
* @returns {Object} parsedObject - parsed embedded app object
|
|
11
|
+
*/
|
|
12
|
+
EmbeddedAppsUtils.parseApp = (embeddedApp) => {
|
|
13
|
+
const parsedApp = {...embeddedApp};
|
|
14
|
+
|
|
15
|
+
parsedApp.type = EMBEDDED_APP_TYPES.OTHER;
|
|
16
|
+
const url = parsedApp.instanceInfo?.appInstanceUrl;
|
|
17
|
+
|
|
18
|
+
if (url && url.match(SLIDO_REGEX)) {
|
|
19
|
+
parsedApp.type = EMBEDDED_APP_TYPES.SLIDO;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return parsedApp;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Determines if two embedded apps arrays are similar.
|
|
27
|
+
* NOTE: This is a simple test for performance reasons.
|
|
28
|
+
* @param {any[]} apps1 - an array of apps
|
|
29
|
+
* @param {any[]} apps2 - an array of apps
|
|
30
|
+
* @returns {boolean} true if the arrays are different
|
|
31
|
+
*/
|
|
32
|
+
EmbeddedAppsUtils.areSimilar = (apps1, apps2) => {
|
|
33
|
+
if (apps1?.length !== apps2?.length) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (apps1?.[0]?.state !== apps2?.[0]?.state) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return true;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Parse the array of embedded apps
|
|
45
|
+
* @param {array} embeddedApps
|
|
46
|
+
* @returns {array} result - new array of parsed embedded app objects
|
|
47
|
+
*/
|
|
48
|
+
EmbeddedAppsUtils.parse = (embeddedApps) => embeddedApps && embeddedApps.map(EmbeddedAppsUtils.parseApp);
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
export default EmbeddedAppsUtils;
|
package/src/locus-info/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import FullState from '../locus-info/fullState';
|
|
|
24
24
|
import SelfUtils from '../locus-info/selfUtils';
|
|
25
25
|
import HostUtils from '../locus-info/hostUtils';
|
|
26
26
|
import ControlsUtils from '../locus-info/controlsUtils';
|
|
27
|
+
import EmbeddedAppsUtils from '../locus-info/embeddedAppsUtils';
|
|
27
28
|
import MediaSharesUtils from '../locus-info/mediaSharesUtils';
|
|
28
29
|
import LocusDeltaParser from '../locus-info/parser';
|
|
29
30
|
|
|
@@ -151,6 +152,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
151
152
|
this.updateLocusUrl(locus.url);
|
|
152
153
|
this.updateFullState(locus.fullState);
|
|
153
154
|
this.updateMeetingInfo(locus.info);
|
|
155
|
+
this.updateEmbeddedApps(locus.embeddedApps);
|
|
154
156
|
// self and participants generate sipUrl for 1:1 meeting
|
|
155
157
|
this.updateSelf(locus.self, locus.participants);
|
|
156
158
|
this.updateHostInfo(locus.host);
|
|
@@ -315,6 +317,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
315
317
|
this.updateSequence(locus.sequence);
|
|
316
318
|
this.updateMemberShip(locus.membership);
|
|
317
319
|
this.updateIdentifiers(locus.identities);
|
|
320
|
+
this.updateEmbeddedApps(locus.embeddedApps);
|
|
318
321
|
this.compareAndUpdate();
|
|
319
322
|
// update which required to compare different objects from locus
|
|
320
323
|
}
|
|
@@ -841,6 +844,30 @@ export default class LocusInfo extends EventsScope {
|
|
|
841
844
|
}
|
|
842
845
|
}
|
|
843
846
|
|
|
847
|
+
/**
|
|
848
|
+
* @param {Object} embeddedApps
|
|
849
|
+
* @returns {undefined}
|
|
850
|
+
* @memberof LocusInfo
|
|
851
|
+
*/
|
|
852
|
+
updateEmbeddedApps(embeddedApps) {
|
|
853
|
+
// don't do anything if the arrays of apps haven't changed significantly
|
|
854
|
+
if (EmbeddedAppsUtils.areSimilar(this.embeddedApps, embeddedApps)) {
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
this.parsedLocus.embeddedApps = EmbeddedAppsUtils.parse(embeddedApps);
|
|
859
|
+
|
|
860
|
+
this.emitScoped(
|
|
861
|
+
{
|
|
862
|
+
file: 'locus-info',
|
|
863
|
+
function: 'updateEmbeddedApps'
|
|
864
|
+
},
|
|
865
|
+
LOCUSINFO.EVENTS.EMBEDDED_APPS_UPDATED,
|
|
866
|
+
embeddedApps
|
|
867
|
+
);
|
|
868
|
+
this.embeddedApps = embeddedApps;
|
|
869
|
+
}
|
|
870
|
+
|
|
844
871
|
/**
|
|
845
872
|
* handles when the locus.mediaShares is updated
|
|
846
873
|
* @param {Object} mediaShares the locus.mediaShares property
|
package/src/meeting/index.js
CHANGED
|
@@ -1065,6 +1065,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1065
1065
|
this.setUpLocusParticipantsListener();
|
|
1066
1066
|
this.setupLocusControlsListener();
|
|
1067
1067
|
this.setUpLocusMediaSharesListener();
|
|
1068
|
+
this.setUpLocusEmbeddedAppsListener();
|
|
1068
1069
|
this.setUpLocusInfoMeetingInfoListener();
|
|
1069
1070
|
this.setUpLocusInfoAssignHostListener();
|
|
1070
1071
|
this.setUpLocusInfoMediaInactiveListener();
|
|
@@ -1905,6 +1906,28 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1905
1906
|
});
|
|
1906
1907
|
}
|
|
1907
1908
|
|
|
1909
|
+
/**
|
|
1910
|
+
* Set up the locus info embedded apps listener
|
|
1911
|
+
* @returns {undefined}
|
|
1912
|
+
* @private
|
|
1913
|
+
* @memberof meeting
|
|
1914
|
+
*/
|
|
1915
|
+
setUpLocusEmbeddedAppsListener() {
|
|
1916
|
+
this.locusInfo.on(LOCUSINFO.EVENTS.EMBEDDED_APPS_UPDATED, (embeddedApps) => {
|
|
1917
|
+
if (embeddedApps) {
|
|
1918
|
+
Trigger.trigger(
|
|
1919
|
+
this,
|
|
1920
|
+
{
|
|
1921
|
+
file: 'meeting/index',
|
|
1922
|
+
function: 'setUpLocusEmbeddedAppsListener'
|
|
1923
|
+
},
|
|
1924
|
+
EVENT_TRIGGERS.MEETING_EMBEDDED_APPS_UPDATE,
|
|
1925
|
+
embeddedApps
|
|
1926
|
+
);
|
|
1927
|
+
}
|
|
1928
|
+
});
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1908
1931
|
/**
|
|
1909
1932
|
* Internal function to listen to the self object changes
|
|
1910
1933
|
* @returns {undefined}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {assert} from '@webex/test-helper-chai';
|
|
2
|
+
import {cloneDeep} from 'lodash';
|
|
3
|
+
|
|
4
|
+
import EmbeddedAppUtils from '@webex/plugin-meetings/src/locus-info/embeddedAppsUtils';
|
|
5
|
+
|
|
6
|
+
describe('plugin-meetings', () => {
|
|
7
|
+
describe('embeddedAppsUtils', () => {
|
|
8
|
+
const slidoApp = {
|
|
9
|
+
url: 'https://hecate-b.wbx2.com/apps/api/v1/locus/7a4994a7',
|
|
10
|
+
sequence: 138849877016800000,
|
|
11
|
+
appId: 'Y2lzY29zcGFyazovL3VzL0FQUExJQ0FUSU9OLzQxODc1MGQ0LTM3ZDctNGY2MC1hOWE3LWEwZTE1NDFhNjRkNg',
|
|
12
|
+
instanceInfo: {
|
|
13
|
+
appInstanceUrl: 'https://webex.sli.do/participant/event/mFKKjcYxzx9h31eyWgngFS?clusterId=eu1',
|
|
14
|
+
externalAppInstanceUrl: '',
|
|
15
|
+
title: 'Active session'
|
|
16
|
+
},
|
|
17
|
+
state: 'STARTED',
|
|
18
|
+
lastModified: '2022-10-13T21:01:41.680Z'
|
|
19
|
+
};
|
|
20
|
+
const otherApp = {
|
|
21
|
+
url: 'https://hecate-b.wbx2.com/apps/api/v1/locus/7a4994a7',
|
|
22
|
+
sequence: 138849877016800000,
|
|
23
|
+
appId: 'some-other-app-id',
|
|
24
|
+
instanceInfo: {
|
|
25
|
+
appInstanceUrl: 'https://webex.someotherapp.com/mFKKjcYxzx9h31eyWgngFS?clusterId=eu1',
|
|
26
|
+
externalAppInstanceUrl: '',
|
|
27
|
+
title: 'Active session'
|
|
28
|
+
},
|
|
29
|
+
state: 'STARTED',
|
|
30
|
+
lastModified: '2022-10-13T21:01:31.680Z'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe('parseApp', () => {
|
|
34
|
+
it('returns a parsed embedded app with type of SLIDO', () => {
|
|
35
|
+
const parsedApp = EmbeddedAppUtils.parseApp(slidoApp);
|
|
36
|
+
const expectedApp = {...slidoApp, ...{type: 'SLIDO'}};
|
|
37
|
+
|
|
38
|
+
assert.deepEqual(parsedApp, expectedApp);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns a parsed embedded app with type of OTHER', () => {
|
|
42
|
+
const parsedApp = EmbeddedAppUtils.parseApp(otherApp);
|
|
43
|
+
const expectedApp = {...otherApp, ...{type: 'OTHER'}};
|
|
44
|
+
|
|
45
|
+
assert.deepEqual(parsedApp, expectedApp);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('parse', () => {
|
|
50
|
+
it('returns a copy of embeddedApps', () => {
|
|
51
|
+
const embeddedApps = [slidoApp];
|
|
52
|
+
const parsedApps = EmbeddedAppUtils.parse(embeddedApps);
|
|
53
|
+
|
|
54
|
+
assert.notStrictEqual(parsedApps, embeddedApps);
|
|
55
|
+
assert.equal(parsedApps.length, embeddedApps.length);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('areSimilar', () => {
|
|
60
|
+
it('returns true if the apps are the same', () => {
|
|
61
|
+
const apps1 = [slidoApp];
|
|
62
|
+
const apps2 = [cloneDeep(slidoApp)];
|
|
63
|
+
|
|
64
|
+
const different = EmbeddedAppUtils.areSimilar(apps1, apps2);
|
|
65
|
+
|
|
66
|
+
assert.equal(different, true);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('returns false if the number of apps is different', () => {
|
|
70
|
+
const apps1 = [slidoApp];
|
|
71
|
+
const apps2 = [cloneDeep(slidoApp), cloneDeep(slidoApp)];
|
|
72
|
+
|
|
73
|
+
const different = EmbeddedAppUtils.areSimilar(apps1, apps2);
|
|
74
|
+
|
|
75
|
+
assert.equal(different, false);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('returns false if the state of the first apps is different', () => {
|
|
79
|
+
const apps1 = [slidoApp];
|
|
80
|
+
const apps2 = [cloneDeep(slidoApp)];
|
|
81
|
+
|
|
82
|
+
apps2[0].state = 'STOPPED';
|
|
83
|
+
|
|
84
|
+
const different = EmbeddedAppUtils.areSimilar(apps1, apps2);
|
|
85
|
+
|
|
86
|
+
assert.equal(different, false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('handles null apps', () => {
|
|
90
|
+
assert.equal(EmbeddedAppUtils.areSimilar(null, null), true);
|
|
91
|
+
assert.equal(EmbeddedAppUtils.areSimilar(null, [slidoApp]), false);
|
|
92
|
+
assert.equal(EmbeddedAppUtils.areSimilar([slidoApp], null), false);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('handles empty apps', () => {
|
|
96
|
+
assert.equal(EmbeddedAppUtils.areSimilar([], []), true);
|
|
97
|
+
assert.equal(EmbeddedAppUtils.areSimilar([], [slidoApp]), false);
|
|
98
|
+
assert.equal(EmbeddedAppUtils.areSimilar([slidoApp], []), false);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -828,6 +828,60 @@ describe('plugin-meetings', () => {
|
|
|
828
828
|
});
|
|
829
829
|
});
|
|
830
830
|
|
|
831
|
+
describe('#updateEmbeddedApps()', () => {
|
|
832
|
+
const newEmbeddedApps = [{
|
|
833
|
+
url: 'https://hecate-b.wbx2.com/apps/api/v1/locus/7a4994a7',
|
|
834
|
+
sequence: 138849877016800000,
|
|
835
|
+
appId: 'Y2lzY29zcGFyazovL3VzL0FQUExJQ0FUSU9OLzQxODc1MGQ0LTM3ZDctNGY2MC1hOWE3LWEwZTE1NDFhNjRkNg',
|
|
836
|
+
instanceInfo: {
|
|
837
|
+
appInstanceUrl: 'https://webex.sli.do/participant/event/mFKKjcYxzx9h31eyWgngFS?clusterId=eu1',
|
|
838
|
+
externalAppInstanceUrl: '',
|
|
839
|
+
title: 'Active session'
|
|
840
|
+
},
|
|
841
|
+
state: 'STARTED',
|
|
842
|
+
lastModified: '2022-10-13T21:01:41.680Z'
|
|
843
|
+
}];
|
|
844
|
+
|
|
845
|
+
it('updates the embeddedApps object', () => {
|
|
846
|
+
const prev = locusInfo.embeddedApps;
|
|
847
|
+
|
|
848
|
+
locusInfo.updateEmbeddedApps(newEmbeddedApps);
|
|
849
|
+
|
|
850
|
+
assert.notEqual(locusInfo.embeddedApps, prev);
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
it('does not emit EMBEDDED_APPS_UPDATED when apps didn\'t change', () => {
|
|
854
|
+
locusInfo.updateEmbeddedApps(newEmbeddedApps);
|
|
855
|
+
|
|
856
|
+
locusInfo.emitScoped = sinon.stub();
|
|
857
|
+
|
|
858
|
+
const clonedApps = cloneDeep(newEmbeddedApps);
|
|
859
|
+
|
|
860
|
+
locusInfo.updateEmbeddedApps(clonedApps);
|
|
861
|
+
|
|
862
|
+
assert.notCalled(locusInfo.emitScoped);
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
it('emits EMBEDDED_APPS_UPDATED when apps changed', () => {
|
|
866
|
+
locusInfo.updateEmbeddedApps(newEmbeddedApps);
|
|
867
|
+
|
|
868
|
+
locusInfo.emitScoped = sinon.stub();
|
|
869
|
+
|
|
870
|
+
const clonedApps = cloneDeep(newEmbeddedApps);
|
|
871
|
+
|
|
872
|
+
clonedApps[0].state = 'STOPPED';
|
|
873
|
+
|
|
874
|
+
locusInfo.updateEmbeddedApps(clonedApps);
|
|
875
|
+
|
|
876
|
+
assert.calledWith(locusInfo.emitScoped, {
|
|
877
|
+
file: 'locus-info',
|
|
878
|
+
function: 'updateEmbeddedApps'
|
|
879
|
+
},
|
|
880
|
+
LOCUSINFO.EVENTS.EMBEDDED_APPS_UPDATED,
|
|
881
|
+
clonedApps);
|
|
882
|
+
});
|
|
883
|
+
});
|
|
884
|
+
|
|
831
885
|
describe('#LocusDeltaEvents', () => {
|
|
832
886
|
const fakeMeeting = 'fakeMeeting';
|
|
833
887
|
let sandbox = null;
|