@webex/plugin-meetings 3.0.0-beta.83 → 3.0.0-beta.85
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/breakouts/breakout.js +3 -1
- package/dist/breakouts/breakout.js.map +1 -1
- package/dist/breakouts/index.js +13 -10
- package/dist/breakouts/index.js.map +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/locus-info/controlsUtils.js +25 -0
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +118 -4
- package/dist/locus-info/index.js.map +1 -1
- package/dist/meeting/index.js +26 -16
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +9 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meetings/index.js +10 -4
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +20 -7
- package/dist/meetings/util.js.map +1 -1
- package/dist/roap/index.js +14 -11
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/turnDiscovery.js +91 -28
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/locus-info/index.d.ts +37 -0
- package/dist/types/meeting/request.d.ts +1 -0
- package/dist/types/roap/turnDiscovery.d.ts +14 -0
- package/package.json +18 -18
- package/src/breakouts/breakout.ts +1 -0
- package/src/breakouts/index.ts +7 -3
- package/src/constants.ts +1 -0
- package/src/locus-info/controlsUtils.ts +28 -0
- package/src/locus-info/index.ts +110 -6
- package/src/meeting/index.ts +10 -0
- package/src/meeting/request.ts +8 -0
- package/src/meetings/index.ts +12 -4
- package/src/meetings/util.ts +23 -12
- package/src/roap/index.ts +15 -11
- package/src/roap/turnDiscovery.ts +45 -17
- package/test/unit/spec/breakouts/index.ts +3 -3
- package/test/unit/spec/locus-info/controlsUtils.js +47 -1
- package/test/unit/spec/locus-info/index.js +153 -2
- package/test/unit/spec/meeting/index.js +35 -0
- package/test/unit/spec/meeting/request.js +12 -0
- package/test/unit/spec/meetings/index.js +29 -21
- package/test/unit/spec/meetings/utils.js +49 -9
- package/test/unit/spec/roap/index.ts +9 -8
- package/test/unit/spec/roap/turnDiscovery.ts +21 -0
package/src/breakouts/index.ts
CHANGED
|
@@ -49,6 +49,7 @@ const Breakouts = WebexPlugin.extend({
|
|
|
49
49
|
|
|
50
50
|
derived: {
|
|
51
51
|
isInMainSession: {
|
|
52
|
+
cache: false,
|
|
52
53
|
deps: ['sessionType'],
|
|
53
54
|
/**
|
|
54
55
|
* Returns true if the user is in the main session
|
|
@@ -59,6 +60,7 @@ const Breakouts = WebexPlugin.extend({
|
|
|
59
60
|
},
|
|
60
61
|
},
|
|
61
62
|
isActiveBreakout: {
|
|
63
|
+
cache: false, // fix issue: sometimes the derived will not change even if the deps changed
|
|
62
64
|
deps: ['sessionType', 'status'],
|
|
63
65
|
/**
|
|
64
66
|
* Returns true if the breakout status is active
|
|
@@ -72,6 +74,7 @@ const Breakouts = WebexPlugin.extend({
|
|
|
72
74
|
},
|
|
73
75
|
},
|
|
74
76
|
breakoutGroupId: {
|
|
77
|
+
cache: false,
|
|
75
78
|
deps: ['groups'],
|
|
76
79
|
/**
|
|
77
80
|
* Returns the actived group id
|
|
@@ -399,13 +402,14 @@ const Breakouts = WebexPlugin.extend({
|
|
|
399
402
|
|
|
400
403
|
/**
|
|
401
404
|
* Create new breakout sessions
|
|
402
|
-
* @param {object}
|
|
405
|
+
* @param {object} params -- breakout session group
|
|
403
406
|
* @returns {Promise}
|
|
404
407
|
*/
|
|
405
|
-
async create(
|
|
408
|
+
async create(params) {
|
|
409
|
+
const payload = {...params};
|
|
406
410
|
const body = {
|
|
407
411
|
...(this.editLock && !!this.editLock.token ? {editlock: {token: this.editLock.token}} : {}),
|
|
408
|
-
...{groups: [
|
|
412
|
+
...{groups: [payload]},
|
|
409
413
|
};
|
|
410
414
|
// @ts-ignore
|
|
411
415
|
const breakInfo = await this.webex
|
package/src/constants.ts
CHANGED
|
@@ -581,6 +581,7 @@ export const LOCUSINFO = {
|
|
|
581
581
|
CONTROLS_MEETING_BREAKOUT_UPDATED: 'CONTROLS_MEETING_BREAKOUT_UPDATED',
|
|
582
582
|
CONTROLS_MEETING_CONTAINER_UPDATED: 'CONTROLS_MEETING_CONTAINER_UPDATED',
|
|
583
583
|
CONTROLS_ENTRY_EXIT_TONE_UPDATED: 'CONTROLS_ENTRY_EXIT_TONE_UPDATED',
|
|
584
|
+
CONTROLS_JOIN_BREAKOUT_FROM_MAIN: 'CONTROLS_JOIN_BREAKOUT_FROM_MAIN',
|
|
584
585
|
SELF_UNADMITTED_GUEST: 'SELF_UNADMITTED_GUEST',
|
|
585
586
|
SELF_ADMITTED_GUEST: 'SELF_ADMITTED_GUEST',
|
|
586
587
|
SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED: 'SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {isEqual} from 'lodash';
|
|
2
|
+
import {BREAKOUTS} from '../constants';
|
|
2
3
|
|
|
3
4
|
const ControlsUtils: any = {};
|
|
4
5
|
|
|
@@ -138,4 +139,31 @@ ControlsUtils.isNeedReplaceMembers = (oldControls: any, controls: any) => {
|
|
|
138
139
|
);
|
|
139
140
|
};
|
|
140
141
|
|
|
142
|
+
/**
|
|
143
|
+
* determine the switch status between breakout session and main session.
|
|
144
|
+
* @param {LocusControls} oldControls
|
|
145
|
+
* @param {LocusControls} controls
|
|
146
|
+
* @returns {Object}
|
|
147
|
+
*/
|
|
148
|
+
ControlsUtils.getSessionSwitchStatus = (oldControls: any, controls: any) => {
|
|
149
|
+
const status = {isReturnToMain: false, isJoinToBreakout: false};
|
|
150
|
+
// no breakout case
|
|
151
|
+
if (!oldControls?.breakout || !controls?.breakout) {
|
|
152
|
+
return status;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
status.isReturnToMain =
|
|
156
|
+
oldControls.breakout.sessionType === BREAKOUTS.SESSION_TYPES.BREAKOUT &&
|
|
157
|
+
controls.breakout.sessionType === BREAKOUTS.SESSION_TYPES.MAIN;
|
|
158
|
+
status.isJoinToBreakout =
|
|
159
|
+
oldControls.breakout.sessionType === BREAKOUTS.SESSION_TYPES.MAIN &&
|
|
160
|
+
controls.breakout.sessionType === BREAKOUTS.SESSION_TYPES.BREAKOUT;
|
|
161
|
+
|
|
162
|
+
return status;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
ControlsUtils.isMainSessionDTO = (locus: any) => {
|
|
166
|
+
return locus?.controls?.breakout?.sessionType !== BREAKOUTS.SESSION_TYPES.BREAKOUT;
|
|
167
|
+
};
|
|
168
|
+
|
|
141
169
|
export default ControlsUtils;
|
package/src/locus-info/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {isEqual} from 'lodash';
|
|
1
|
+
import {isArray, isEqual, mergeWith, cloneDeep} from 'lodash';
|
|
2
2
|
|
|
3
3
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
4
4
|
import EventsScope from '../common/events/events-scope';
|
|
@@ -64,7 +64,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
64
64
|
replace: any;
|
|
65
65
|
url: any;
|
|
66
66
|
services: any;
|
|
67
|
-
|
|
67
|
+
mainSessionLocusCache: any;
|
|
68
68
|
/**
|
|
69
69
|
* Constructor
|
|
70
70
|
* @param {boolean} updateMeeting true if the meeting should be updated
|
|
@@ -180,6 +180,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
180
180
|
*/
|
|
181
181
|
this.deltaParticipants = [];
|
|
182
182
|
|
|
183
|
+
this.updateLocusCache(locus);
|
|
183
184
|
// above section only updates the locusInfo object
|
|
184
185
|
// The below section makes sure it updates the locusInfo as well as updates the meeting object
|
|
185
186
|
this.updateParticipants(locus.participants);
|
|
@@ -203,6 +204,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
203
204
|
* @memberof LocusInfo
|
|
204
205
|
*/
|
|
205
206
|
initialSetup(locus: object) {
|
|
207
|
+
this.updateLocusCache(locus);
|
|
206
208
|
this.onFullLocus(locus);
|
|
207
209
|
|
|
208
210
|
// Change it to true after it receives it first locus object
|
|
@@ -218,7 +220,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
218
220
|
parse(meeting: any, data: any) {
|
|
219
221
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
220
222
|
const {eventType} = data;
|
|
221
|
-
|
|
223
|
+
const locus = this.getTheLocusToUpdate(data.locus);
|
|
222
224
|
LoggerProxy.logger.info(`Locus-info:index#parse --> received locus data: ${eventType}`);
|
|
223
225
|
|
|
224
226
|
switch (eventType) {
|
|
@@ -236,16 +238,16 @@ export default class LocusInfo extends EventsScope {
|
|
|
236
238
|
case LOCUSEVENT.PARTICIPANT_DECLINED:
|
|
237
239
|
case LOCUSEVENT.FLOOR_GRANTED:
|
|
238
240
|
case LOCUSEVENT.FLOOR_RELEASED:
|
|
239
|
-
this.onFullLocus(
|
|
241
|
+
this.onFullLocus(locus, eventType);
|
|
240
242
|
break;
|
|
241
243
|
case LOCUSEVENT.DIFFERENCE:
|
|
242
|
-
this.handleLocusDelta(
|
|
244
|
+
this.handleLocusDelta(locus, meeting);
|
|
243
245
|
break;
|
|
244
246
|
|
|
245
247
|
default:
|
|
246
248
|
// Why will there be a event with no eventType ????
|
|
247
249
|
// we may not need this, we can get full locus
|
|
248
|
-
this.handleLocusDelta(
|
|
250
|
+
this.handleLocusDelta(locus, meeting);
|
|
249
251
|
}
|
|
250
252
|
}
|
|
251
253
|
|
|
@@ -1395,4 +1397,106 @@ export default class LocusInfo extends EventsScope {
|
|
|
1395
1397
|
this.identities = identities;
|
|
1396
1398
|
}
|
|
1397
1399
|
}
|
|
1400
|
+
|
|
1401
|
+
/**
|
|
1402
|
+
* check the locus is main session's one or not, if is main session's, update main session cache
|
|
1403
|
+
* @param {Object} locus
|
|
1404
|
+
* @returns {undefined}
|
|
1405
|
+
* @memberof LocusInfo
|
|
1406
|
+
*/
|
|
1407
|
+
updateLocusCache(locus: any) {
|
|
1408
|
+
const isMainSessionDTO = ControlsUtils.isMainSessionDTO(locus);
|
|
1409
|
+
if (isMainSessionDTO) {
|
|
1410
|
+
this.updateMainSessionLocusCache(locus);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* if return from breakout to main session, need to use cached main session DTO since locus won't send the full locus (participants)
|
|
1416
|
+
* if join breakout from main session, need to query main locus url (if response with 403 means no privilege, need to clear the cache)
|
|
1417
|
+
* @param {Object} newLocus
|
|
1418
|
+
* @returns {Object}
|
|
1419
|
+
* @memberof LocusInfo
|
|
1420
|
+
*/
|
|
1421
|
+
getTheLocusToUpdate(newLocus: any) {
|
|
1422
|
+
const switchStatus = ControlsUtils.getSessionSwitchStatus(this.controls, newLocus.controls);
|
|
1423
|
+
if (switchStatus.isReturnToMain && this.mainSessionLocusCache) {
|
|
1424
|
+
return cloneDeep(this.mainSessionLocusCache);
|
|
1425
|
+
}
|
|
1426
|
+
if (switchStatus.isJoinToBreakout) {
|
|
1427
|
+
this.emitScoped(
|
|
1428
|
+
{
|
|
1429
|
+
file: 'locus-info',
|
|
1430
|
+
function: 'updateControls',
|
|
1431
|
+
},
|
|
1432
|
+
LOCUSINFO.EVENTS.CONTROLS_JOIN_BREAKOUT_FROM_MAIN,
|
|
1433
|
+
{
|
|
1434
|
+
mainLocusUrl: this.url,
|
|
1435
|
+
}
|
|
1436
|
+
);
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
return newLocus;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
/**
|
|
1443
|
+
* merge participants by participant id
|
|
1444
|
+
* @param {Array} participants
|
|
1445
|
+
* @param {Array} sourceParticipants
|
|
1446
|
+
* @returns {Array} merged participants
|
|
1447
|
+
* @memberof LocusInfo
|
|
1448
|
+
*/
|
|
1449
|
+
// eslint-disable-next-line class-methods-use-this
|
|
1450
|
+
mergeParticipants(participants, sourceParticipants) {
|
|
1451
|
+
if (!sourceParticipants || !sourceParticipants.length) return participants;
|
|
1452
|
+
if (!participants || !participants.length) {
|
|
1453
|
+
return sourceParticipants;
|
|
1454
|
+
}
|
|
1455
|
+
sourceParticipants.forEach((participant) => {
|
|
1456
|
+
const existIndex = participants.findIndex((p) => p.id === participant.id);
|
|
1457
|
+
if (existIndex > -1) {
|
|
1458
|
+
participants.splice(existIndex, 1, participant);
|
|
1459
|
+
} else {
|
|
1460
|
+
participants.push(participant);
|
|
1461
|
+
}
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
return participants;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
/**
|
|
1468
|
+
* need cache main sessions' participants since locus will not send the full list when cohost/host leave breakout
|
|
1469
|
+
* @param {Object} mainLocus
|
|
1470
|
+
* @returns {undefined}
|
|
1471
|
+
* @memberof LocusInfo
|
|
1472
|
+
*/
|
|
1473
|
+
updateMainSessionLocusCache(mainLocus: any) {
|
|
1474
|
+
if (!mainLocus) {
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
const locusClone = cloneDeep(mainLocus);
|
|
1478
|
+
if (this.mainSessionLocusCache) {
|
|
1479
|
+
// eslint-disable-next-line consistent-return
|
|
1480
|
+
mergeWith(this.mainSessionLocusCache, locusClone, (objValue, srcValue, key) => {
|
|
1481
|
+
if (isArray(objValue)) {
|
|
1482
|
+
if (key === 'participants') {
|
|
1483
|
+
return this.mergeParticipants(objValue, srcValue);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
return srcValue; // just replace the old ones
|
|
1487
|
+
}
|
|
1488
|
+
});
|
|
1489
|
+
} else {
|
|
1490
|
+
this.mainSessionLocusCache = locusClone;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
/**
|
|
1495
|
+
* clear main session cache
|
|
1496
|
+
* @returns {undefined}
|
|
1497
|
+
* @memberof LocusInfo
|
|
1498
|
+
*/
|
|
1499
|
+
clearMainSessionLocusCache() {
|
|
1500
|
+
this.mainSessionLocusCache = null;
|
|
1501
|
+
}
|
|
1398
1502
|
}
|
package/src/meeting/index.ts
CHANGED
|
@@ -1991,6 +1991,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1991
1991
|
);
|
|
1992
1992
|
});
|
|
1993
1993
|
|
|
1994
|
+
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_JOIN_BREAKOUT_FROM_MAIN, ({mainLocusUrl}) => {
|
|
1995
|
+
this.meetingRequest.getLocusStatusByUrl(mainLocusUrl).catch((error) => {
|
|
1996
|
+
// clear main session cache when attendee join into breakout and forbidden to get locus from main locus url,
|
|
1997
|
+
// which means main session is not active for the attendee
|
|
1998
|
+
if (error?.statusCode === 403) {
|
|
1999
|
+
this.locusInfo.clearMainSessionLocusCache();
|
|
2000
|
+
}
|
|
2001
|
+
});
|
|
2002
|
+
});
|
|
2003
|
+
|
|
1994
2004
|
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_ENTRY_EXIT_TONE_UPDATED, ({entryExitTone}) => {
|
|
1995
2005
|
Trigger.trigger(
|
|
1996
2006
|
this,
|
package/src/meeting/request.ts
CHANGED
package/src/meetings/index.ts
CHANGED
|
@@ -246,8 +246,14 @@ export default class Meetings extends WebexPlugin {
|
|
|
246
246
|
|
|
247
247
|
const isSelfJoined = newLocus?.self?.state === _JOINED_;
|
|
248
248
|
const isSelfMoved = newLocus?.self?.state === _LEFT_ && newLocus?.self?.reason === _MOVED_;
|
|
249
|
-
|
|
250
|
-
const
|
|
249
|
+
// @ts-ignore
|
|
250
|
+
const deviceFromNewLocus = MeetingsUtil.getThisDevice(newLocus, this.webex.internal.device.url);
|
|
251
|
+
const isNewLocusJoinThisDevice = MeetingsUtil.joinedOnThisDevice(
|
|
252
|
+
meeting,
|
|
253
|
+
newLocus,
|
|
254
|
+
// @ts-ignore
|
|
255
|
+
this.webex.internal.device.url
|
|
256
|
+
);
|
|
251
257
|
const isBreakoutLocusJoinThisDevice =
|
|
252
258
|
breakoutLocus?.joinedWith?.correlationId &&
|
|
253
259
|
breakoutLocus.joinedWith.correlationId === meeting?.correlationId;
|
|
@@ -305,8 +311,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
305
311
|
*/
|
|
306
312
|
private isNeedHandleLocusDTO(meeting: any, newLocus: any) {
|
|
307
313
|
if (newLocus) {
|
|
308
|
-
const isNewLocusAsBreakout =
|
|
309
|
-
newLocus.controls?.breakout?.sessionType === BREAKOUTS.SESSION_TYPES.BREAKOUT;
|
|
314
|
+
const isNewLocusAsBreakout = MeetingsUtil.isBreakoutLocusDTO(newLocus);
|
|
310
315
|
const isSelfMoved = newLocus?.self?.state === _LEFT_ && newLocus?.self?.reason === _MOVED_;
|
|
311
316
|
if (!meeting) {
|
|
312
317
|
if (isNewLocusAsBreakout) {
|
|
@@ -375,6 +380,9 @@ export default class Meetings extends WebexPlugin {
|
|
|
375
380
|
);
|
|
376
381
|
}
|
|
377
382
|
|
|
383
|
+
if (meeting && !MeetingsUtil.isBreakoutLocusDTO(data.locus)) {
|
|
384
|
+
meeting.locusInfo.updateMainSessionLocusCache(data.locus);
|
|
385
|
+
}
|
|
378
386
|
if (!this.isNeedHandleLocusDTO(meeting, data.locus)) {
|
|
379
387
|
LoggerProxy.logger.log(
|
|
380
388
|
`Meetings:index#handleLocusEvent --> doesn't need to process locus event`
|
package/src/meetings/util.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/* globals window */
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
_LOCUS_ID_,
|
|
5
|
-
_INCOMING_,
|
|
6
4
|
_CREATED_,
|
|
7
|
-
|
|
5
|
+
_INCOMING_,
|
|
6
|
+
_JOINED_,
|
|
7
|
+
_LEFT_,
|
|
8
|
+
_LOCUS_ID_,
|
|
9
|
+
_MOVED_,
|
|
10
|
+
BREAKOUTS,
|
|
8
11
|
CORRELATION_ID,
|
|
9
12
|
EVENT_TRIGGERS,
|
|
13
|
+
LOCUSEVENT,
|
|
10
14
|
ROAP,
|
|
11
|
-
_LEFT_,
|
|
12
|
-
_MOVED_,
|
|
13
|
-
_JOINED_,
|
|
14
15
|
} from '../constants';
|
|
15
16
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
16
17
|
import Trigger from '../common/events/trigger-proxy';
|
|
@@ -228,13 +229,12 @@ MeetingsUtil.checkH264Support = async function checkH264Support(options: {
|
|
|
228
229
|
/**
|
|
229
230
|
* get device from locus data
|
|
230
231
|
* @param {Object} newLocus new locus data
|
|
232
|
+
* @param {String} deviceUrl current device url
|
|
231
233
|
* @returns {Object}
|
|
232
234
|
*/
|
|
233
|
-
MeetingsUtil.getThisDevice = (newLocus: any) => {
|
|
235
|
+
MeetingsUtil.getThisDevice = (newLocus: any, deviceUrl: string) => {
|
|
234
236
|
if (newLocus?.self?.devices?.length > 0) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return thisDevice;
|
|
237
|
+
return newLocus.self.devices.find((device) => device.url === deviceUrl);
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
return null;
|
|
@@ -244,10 +244,11 @@ MeetingsUtil.getThisDevice = (newLocus: any) => {
|
|
|
244
244
|
* get self device joined status from locus data
|
|
245
245
|
* @param {Object} meeting current meeting data
|
|
246
246
|
* @param {Object} newLocus new locus data
|
|
247
|
+
* @param {String} deviceUrl current device url
|
|
247
248
|
* @returns {Object}
|
|
248
249
|
*/
|
|
249
|
-
MeetingsUtil.joinedOnThisDevice = (meeting: any, newLocus: any) => {
|
|
250
|
-
const thisDevice = MeetingsUtil.getThisDevice(newLocus);
|
|
250
|
+
MeetingsUtil.joinedOnThisDevice = (meeting: any, newLocus: any, deviceUrl: string) => {
|
|
251
|
+
const thisDevice = MeetingsUtil.getThisDevice(newLocus, deviceUrl);
|
|
251
252
|
if (thisDevice) {
|
|
252
253
|
if (!thisDevice.correlationId || meeting?.correlationId === thisDevice.correlationId) {
|
|
253
254
|
return (
|
|
@@ -260,4 +261,14 @@ MeetingsUtil.joinedOnThisDevice = (meeting: any, newLocus: any) => {
|
|
|
260
261
|
return false;
|
|
261
262
|
};
|
|
262
263
|
|
|
264
|
+
/**
|
|
265
|
+
* check the new locus is breakout session's one or not
|
|
266
|
+
* @param {Object} newLocus new locus data
|
|
267
|
+
* @returns {boolean}
|
|
268
|
+
* @private
|
|
269
|
+
* @memberof Meetings
|
|
270
|
+
*/
|
|
271
|
+
MeetingsUtil.isBreakoutLocusDTO = (newLocus: any) => {
|
|
272
|
+
return newLocus?.controls?.breakout?.sessionType === BREAKOUTS.SESSION_TYPES.BREAKOUT;
|
|
273
|
+
};
|
|
263
274
|
export default MeetingsUtil;
|
package/src/roap/index.ts
CHANGED
|
@@ -199,19 +199,23 @@ export default class Roap extends StatelessWebexPlugin {
|
|
|
199
199
|
// When reconnecting, it's important that the first roap message being sent out has empty media id.
|
|
200
200
|
// Normally this is the roap offer, but when TURN discovery is enabled,
|
|
201
201
|
// then this is the TURN discovery request message
|
|
202
|
-
|
|
202
|
+
return this.turnDiscovery
|
|
203
|
+
.isSkipped(meeting)
|
|
204
|
+
.then((isTurnDiscoverySkipped) => {
|
|
205
|
+
const sendEmptyMediaId = reconnect && isTurnDiscoverySkipped;
|
|
203
206
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
207
|
+
return this.roapRequest.sendRoap({
|
|
208
|
+
roapMessage,
|
|
209
|
+
correlationId: meeting.correlationId,
|
|
210
|
+
locusSelfUrl: meeting.selfUrl,
|
|
211
|
+
mediaId: sendEmptyMediaId ? '' : meeting.mediaId,
|
|
212
|
+
audioMuted: meeting.audio?.isLocallyMuted(),
|
|
213
|
+
videoMuted: meeting.video?.isLocallyMuted(),
|
|
214
|
+
meetingId: meeting.id,
|
|
215
|
+
preferTranscoding: !meeting.isMultistream,
|
|
216
|
+
});
|
|
214
217
|
})
|
|
218
|
+
|
|
215
219
|
.then(({locus, mediaConnections}) => {
|
|
216
220
|
if (mediaConnections) {
|
|
217
221
|
meeting.updateMediaConnections(mediaConnections);
|
|
@@ -221,6 +221,48 @@ export default class TurnDiscovery {
|
|
|
221
221
|
});
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Gets the reason why reachability is skipped.
|
|
226
|
+
*
|
|
227
|
+
* @param {Meeting} meeting
|
|
228
|
+
* @returns {Promise<string>} Promise with empty string if reachability is not skipped or a reason if it is skipped
|
|
229
|
+
*/
|
|
230
|
+
private async getSkipReason(meeting: Meeting): Promise<string> {
|
|
231
|
+
// @ts-ignore - fix type
|
|
232
|
+
const isAnyClusterReachable = await meeting.webex.meetings.reachability.isAnyClusterReachable();
|
|
233
|
+
|
|
234
|
+
if (isAnyClusterReachable) {
|
|
235
|
+
LoggerProxy.logger.info(
|
|
236
|
+
'Roap:turnDiscovery#getSkipReason --> reachability has not failed, skipping TURN discovery'
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
return 'reachability';
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// @ts-ignore - fix type
|
|
243
|
+
if (!meeting.config.experimental.enableTurnDiscovery) {
|
|
244
|
+
LoggerProxy.logger.info(
|
|
245
|
+
'Roap:turnDiscovery#getSkipReason --> TURN discovery disabled in config, skipping it'
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
return 'config';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return '';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Checks if TURN discovery is skipped.
|
|
256
|
+
*
|
|
257
|
+
* @param {Meeting} meeting
|
|
258
|
+
* @returns {Boolean} true if TURN discovery is being skipped, false if it is being done
|
|
259
|
+
*/
|
|
260
|
+
async isSkipped(meeting) {
|
|
261
|
+
const skipReason = await this.getSkipReason(meeting);
|
|
262
|
+
|
|
263
|
+
return !!skipReason;
|
|
264
|
+
}
|
|
265
|
+
|
|
224
266
|
/**
|
|
225
267
|
* Retrieves TURN server information from the backend by doing
|
|
226
268
|
* a roap message exchange:
|
|
@@ -239,29 +281,15 @@ export default class TurnDiscovery {
|
|
|
239
281
|
* @returns {Promise}
|
|
240
282
|
*/
|
|
241
283
|
async doTurnDiscovery(meeting: Meeting, isReconnecting?: boolean) {
|
|
242
|
-
|
|
243
|
-
const isAnyClusterReachable = await meeting.webex.meetings.reachability.isAnyClusterReachable();
|
|
244
|
-
|
|
245
|
-
if (isAnyClusterReachable) {
|
|
246
|
-
LoggerProxy.logger.info(
|
|
247
|
-
'Roap:turnDiscovery#doTurnDiscovery --> reachability has not failed, skipping TURN discovery'
|
|
248
|
-
);
|
|
284
|
+
const turnDiscoverySkippedReason = await this.getSkipReason(meeting);
|
|
249
285
|
|
|
286
|
+
if (turnDiscoverySkippedReason) {
|
|
250
287
|
return {
|
|
251
288
|
turnServerInfo: undefined,
|
|
252
|
-
turnDiscoverySkippedReason
|
|
289
|
+
turnDiscoverySkippedReason,
|
|
253
290
|
};
|
|
254
291
|
}
|
|
255
292
|
|
|
256
|
-
// @ts-ignore - fix type
|
|
257
|
-
if (!meeting.config.experimental.enableTurnDiscovery) {
|
|
258
|
-
LoggerProxy.logger.info(
|
|
259
|
-
'Roap:turnDiscovery#doTurnDiscovery --> TURN discovery disabled in config, skipping it'
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
return {turnServerInfo: undefined, turnDiscoverySkippedReason: 'config'};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
293
|
return this.sendRoapTurnDiscoveryRequest(meeting, isReconnecting)
|
|
266
294
|
.then(() => this.waitForTurnDiscoveryResponse())
|
|
267
295
|
.then(() => this.sendRoapOK(meeting))
|
|
@@ -868,7 +868,7 @@ describe('plugin-meetings', () => {
|
|
|
868
868
|
describe('create', () => {
|
|
869
869
|
it('response not include groups info', async () => {
|
|
870
870
|
const sessions = [{name: 'session1', anyoneCanJoin: true}];
|
|
871
|
-
const result = await breakouts.create(sessions);
|
|
871
|
+
const result = await breakouts.create({sessions});
|
|
872
872
|
|
|
873
873
|
assert.equal(result, 'REQUEST_RETURN_VALUE');
|
|
874
874
|
});
|
|
@@ -890,7 +890,7 @@ describe('plugin-meetings', () => {
|
|
|
890
890
|
})
|
|
891
891
|
);
|
|
892
892
|
|
|
893
|
-
const result = await breakouts.create(sessions);
|
|
893
|
+
const result = await breakouts.create({sessions});
|
|
894
894
|
|
|
895
895
|
assert.equal(breakouts.groups[0].id, '455556a4-37cd-4baa-89bc-8730581a1cc0');
|
|
896
896
|
});
|
|
@@ -908,7 +908,7 @@ describe('plugin-meetings', () => {
|
|
|
908
908
|
);
|
|
909
909
|
|
|
910
910
|
await assert.isRejected(
|
|
911
|
-
breakouts.create(sessions),
|
|
911
|
+
breakouts.create({sessions}),
|
|
912
912
|
BreakoutEditLockedError,
|
|
913
913
|
'Edit lock token mismatch'
|
|
914
914
|
);
|
|
@@ -176,6 +176,52 @@ describe('plugin-meetings', () => {
|
|
|
176
176
|
const newControls = {breakout: {sessionId: 'sessionId1', groupId: 'groupId'}};
|
|
177
177
|
assert.equal(controlsUtils.isNeedReplaceMembers(oldControls, newControls), false);
|
|
178
178
|
});
|
|
179
|
-
})
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('getSessionSwitchStatus', () => {
|
|
182
|
+
it('if no breakout control, return switch status both false', () => {
|
|
183
|
+
const oldControls = {};
|
|
184
|
+
const newControls = {};
|
|
185
|
+
assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldControls, newControls), {
|
|
186
|
+
isReturnToMain: false, isJoinToBreakout: false
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('if switch session from breakout to main, return isReturnToMain as true', () => {
|
|
191
|
+
const oldControls = {breakout: {sessionType: 'BREAKOUT'}};
|
|
192
|
+
const newControls = {breakout: {sessionType: 'MAIN'}};
|
|
193
|
+
assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldControls, newControls), {
|
|
194
|
+
isReturnToMain: true, isJoinToBreakout: false
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('if switch session from main to breakout, return isJoinToBreakout as true', () => {
|
|
199
|
+
const oldControls = {breakout: {sessionType: 'MAIN'}};
|
|
200
|
+
const newControls = {breakout: {sessionType: 'BREAKOUT'}};
|
|
201
|
+
assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldControls, newControls), {
|
|
202
|
+
isReturnToMain: false, isJoinToBreakout: true
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('#isMainSessionDTO', () => {
|
|
208
|
+
it('return false is sessionType is BREAKOUT', () => {
|
|
209
|
+
const locus = {
|
|
210
|
+
controls: {breakout: {sessionType: 'BREAKOUT'}}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
assert.equal(controlsUtils.isMainSessionDTO(locus), false);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('return true is sessionType is not BREAKOUT', () => {
|
|
217
|
+
const locus = {
|
|
218
|
+
controls: {breakout: {sessionType: 'MAIN'}}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
assert.equal(controlsUtils.isMainSessionDTO(locus), true);
|
|
222
|
+
|
|
223
|
+
assert.equal(controlsUtils.isMainSessionDTO({}), true);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
180
226
|
});
|
|
181
227
|
});
|