@webex/plugin-meetings 1.149.2 → 1.151.1

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.
@@ -51,6 +51,7 @@ import {
51
51
  MEETING_STATE_MACHINE,
52
52
  MEETING_STATE,
53
53
  MEETINGS,
54
+ METRICS_JOIN_TIMES_MAX_DURATION,
54
55
  METRICS_OPERATIONAL_MEASURES,
55
56
  MQA_STATS,
56
57
  NETWORK_STATUS,
@@ -1065,10 +1066,45 @@ export default class Meeting extends StatelessWebexPlugin {
1065
1066
  };
1066
1067
  }
1067
1068
 
1069
+ const localSDPGenRemoteSDPRecv = this.getLocalSDPGenRemoteSDPRecvDelay();
1070
+
1071
+ if (localSDPGenRemoteSDPRecv) {
1072
+ options.joinTimes = {
1073
+ ...options.joinTimes,
1074
+ localSDPGenRemoteSDPRecv
1075
+ };
1076
+ }
1077
+
1078
+ const callInitiateJoinReq = this.getCallInitiateJoinReq();
1079
+
1080
+ if (callInitiateJoinReq) {
1081
+ options.joinTimes = {
1082
+ ...options.joinTimes,
1083
+ callInitiateJoinReq
1084
+ };
1085
+ }
1086
+
1087
+ const joinReqResp = this.getJoinReqResp();
1088
+
1089
+ if (joinReqResp) {
1090
+ options.joinTimes = {
1091
+ ...options.joinTimes,
1092
+ joinReqResp
1093
+ };
1094
+ }
1095
+
1096
+ const getTotalJmt = this.getTotalJmt();
1097
+
1098
+ if (getTotalJmt) {
1099
+ options.joinTimes = {
1100
+ ...options.joinTimes,
1101
+ getTotalJmt
1102
+ };
1103
+ }
1104
+
1068
1105
  if (options.type === MQA_STATS.CA_TYPE) {
1069
1106
  payload = Metrics.initMediaPayload(options.event, identifiers, options);
1070
1107
  }
1071
-
1072
1108
  else {
1073
1109
  payload = Metrics.initPayload(options.event, identifiers, options);
1074
1110
  }
@@ -1310,7 +1346,7 @@ export default class Meeting extends StatelessWebexPlugin {
1310
1346
 
1311
1347
  /**
1312
1348
  * Set up the locus info media shares listener
1313
- * update content sharing id value for members, and updates the member
1349
+ * update content and whiteboard sharing id value for members, and updates the member
1314
1350
  * notifies consumer with members:content:update {activeContentSharingId, endedContentSharingId}
1315
1351
  * @returns {undefined}
1316
1352
  * @private
@@ -1319,11 +1355,16 @@ export default class Meeting extends StatelessWebexPlugin {
1319
1355
  setUpLocusMediaSharesListener() {
1320
1356
  // Will get triggered on local and remote share
1321
1357
  this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_MEDIA_SHARES, (payload) => {
1322
- const {contentId, disposition} = payload.current;
1358
+ const {content: contentShare, whiteboard: whiteboardShare} = payload.current;
1359
+ const previousContentShare = payload.previous?.content;
1360
+ const previousWhiteboardShare = payload.previous?.whiteboard;
1323
1361
 
1324
1362
  if (
1325
- contentId === payload.previous?.contentId &&
1326
- disposition === payload.previous?.disposition
1363
+ (contentShare.beneficiaryId === previousContentShare?.beneficiaryId &&
1364
+ contentShare.disposition === previousContentShare?.disposition) &&
1365
+ (whiteboardShare.beneficiaryId === previousWhiteboardShare?.beneficiaryId &&
1366
+ whiteboardShare.disposition === previousWhiteboardShare?.disposition &&
1367
+ whiteboardShare.resourceUrl === previousWhiteboardShare?.resourceUrl)
1327
1368
  ) {
1328
1369
  // nothing changed, so ignore
1329
1370
  // (this happens when we steal presentation from remote)
@@ -1334,15 +1375,16 @@ export default class Meeting extends StatelessWebexPlugin {
1334
1375
 
1335
1376
  // REMOTE - check if remote started sharing
1336
1377
  if (
1337
- this.selfId !== contentId &&
1338
- disposition === FLOOR_ACTION.GRANTED
1378
+ this.selfId !== contentShare.beneficiaryId &&
1379
+ contentShare.disposition === FLOOR_ACTION.GRANTED
1339
1380
  ) {
1381
+ // CONTENT - sharing content remote
1340
1382
  newShareStatus = SHARE_STATUS.REMOTE_SHARE_ACTIVE;
1341
1383
  }
1342
- // LOCAL - check if we started sharing
1384
+ // LOCAL - check if we started sharing content
1343
1385
  else if (
1344
- this.selfId === contentId &&
1345
- disposition === FLOOR_ACTION.GRANTED
1386
+ this.selfId === contentShare.beneficiaryId &&
1387
+ contentShare.disposition === FLOOR_ACTION.GRANTED
1346
1388
  ) {
1347
1389
  if (this.mediaProperties.shareTrack?.readyState === 'ended') {
1348
1390
  this.stopShare({
@@ -1353,13 +1395,23 @@ export default class Meeting extends StatelessWebexPlugin {
1353
1395
  });
1354
1396
  }
1355
1397
  else {
1398
+ // CONTENT - sharing content local
1356
1399
  newShareStatus = SHARE_STATUS.LOCAL_SHARE_ACTIVE;
1357
1400
  }
1358
1401
  }
1359
- // or if sharing has been stopped
1402
+ // If we did not hit the cases above, no one is sharng content, so we check if we are sharing whiteboard
1403
+ // There is no concept of local/remote share for whiteboard
1404
+ // It does not matter who requested to share the whiteboard, everyone gets the same view
1405
+ else if (whiteboardShare.disposition === FLOOR_ACTION.GRANTED) {
1406
+ // WHITEBOARD - sharing whiteboard
1407
+ newShareStatus = SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
1408
+ }
1409
+ // or if content share is either released or null and whiteboard share is either released or null, no one is sharing
1360
1410
  else if (
1361
- payload.previous &&
1362
- disposition === FLOOR_ACTION.RELEASED
1411
+ (previousContentShare &&
1412
+ (contentShare.disposition === FLOOR_ACTION.RELEASED) || (contentShare.disposition === null)) &&
1413
+ (previousWhiteboardShare &&
1414
+ (whiteboardShare.disposition === FLOOR_ACTION.RELEASED) || (whiteboardShare.disposition === null))
1363
1415
  ) {
1364
1416
  newShareStatus = SHARE_STATUS.NO_SHARE;
1365
1417
  }
@@ -1397,6 +1449,17 @@ export default class Meeting extends StatelessWebexPlugin {
1397
1449
  );
1398
1450
  break;
1399
1451
 
1452
+ case SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE:
1453
+ Trigger.trigger(
1454
+ this,
1455
+ {
1456
+ file: 'meeting/index',
1457
+ function: 'stopWhiteboardShare'
1458
+ },
1459
+ EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD
1460
+ );
1461
+ break;
1462
+
1400
1463
  case SHARE_STATUS.NO_SHARE:
1401
1464
  // nothing to do
1402
1465
  break;
@@ -1417,13 +1480,16 @@ export default class Meeting extends StatelessWebexPlugin {
1417
1480
  },
1418
1481
  EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
1419
1482
  {
1420
- memberId: contentId
1483
+ memberId: contentShare.beneficiaryId
1421
1484
  }
1422
1485
  );
1423
1486
  };
1424
1487
 
1425
1488
  // if a remote participant is stealing the presentation from us
1426
- if (this.mediaProperties.mediaDirection?.sendShare) {
1489
+ if (!this.mediaProperties.mediaDirection?.sendShare || oldShareStatus === SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE) {
1490
+ sendStartedSharingRemote();
1491
+ }
1492
+ else {
1427
1493
  this.updateShare({
1428
1494
  sendShare: false,
1429
1495
  receiveShare: this.mediaProperties.mediaDirection.receiveShare
@@ -1432,9 +1498,6 @@ export default class Meeting extends StatelessWebexPlugin {
1432
1498
  sendStartedSharingRemote();
1433
1499
  });
1434
1500
  }
1435
- else {
1436
- sendStartedSharingRemote();
1437
- }
1438
1501
  break;
1439
1502
  }
1440
1503
 
@@ -1450,6 +1513,22 @@ export default class Meeting extends StatelessWebexPlugin {
1450
1513
  Metrics.postEvent({event: eventType.LOCAL_SHARE_FLOOR_GRANTED, meeting: this});
1451
1514
  break;
1452
1515
 
1516
+ case SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE:
1517
+ Trigger.trigger(
1518
+ this,
1519
+ {
1520
+ file: 'meeting/index',
1521
+ function: 'startWhiteboardShare'
1522
+ },
1523
+ EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
1524
+ {
1525
+ resourceUrl: whiteboardShare.resourceUrl,
1526
+ memberId: whiteboardShare.beneficiaryId
1527
+ }
1528
+ );
1529
+ Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_FLOOR_GRANTED, meeting: this});
1530
+ break;
1531
+
1453
1532
  case SHARE_STATUS.NO_SHARE:
1454
1533
  // nothing to do
1455
1534
  break;
@@ -1471,9 +1550,27 @@ export default class Meeting extends StatelessWebexPlugin {
1471
1550
  },
1472
1551
  EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
1473
1552
  {
1474
- memberId: contentId
1553
+ memberId: contentShare.beneficiaryId
1554
+ }
1555
+ );
1556
+ this.members.locusMediaSharesUpdate(payload);
1557
+ }
1558
+ else if (newShareStatus === SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE) {
1559
+ // if we got here, then some remote participant has stolen
1560
+ // the presentation from another remote participant
1561
+ Trigger.trigger(
1562
+ this,
1563
+ {
1564
+ file: 'meeting/index',
1565
+ function: 'startWhiteboardShare'
1566
+ },
1567
+ EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
1568
+ {
1569
+ resourceUrl: whiteboardShare.resourceUrl,
1570
+ memberId: whiteboardShare.beneficiaryId
1475
1571
  }
1476
1572
  );
1573
+ Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_FLOOR_GRANTED, meeting: this});
1477
1574
  this.members.locusMediaSharesUpdate(payload);
1478
1575
  }
1479
1576
  });
@@ -3311,12 +3408,13 @@ export default class Meeting extends StatelessWebexPlugin {
3311
3408
  }
3312
3409
  }
3313
3410
 
3314
- return MeetingUtil.joinMeetingOptions(this, options).then((join) => {
3315
- this.meetingFiniteStateMachine.join();
3316
- LoggerProxy.logger.log('Meeting:index#join --> Success');
3411
+ return MeetingUtil.joinMeetingOptions(this, options)
3412
+ .then((join) => {
3413
+ this.meetingFiniteStateMachine.join();
3414
+ LoggerProxy.logger.log('Meeting:index#join --> Success');
3317
3415
 
3318
- return join;
3319
- })
3416
+ return join;
3417
+ })
3320
3418
  .then((join) => {
3321
3419
  joinSuccess(join);
3322
3420
  this.deferJoin = undefined;
@@ -3790,7 +3888,8 @@ export default class Meeting extends StatelessWebexPlugin {
3790
3888
  meetingId: this.id,
3791
3889
  remoteQualityLevel: this.mediaProperties.remoteQualityLevel,
3792
3890
  enableRtx: this.config.enableRtx,
3793
- enableExtmap: this.config.enableExtmap
3891
+ enableExtmap: this.config.enableExtmap,
3892
+ setStartLocalSDPGenRemoteSDPRecvDelay: this.setStartLocalSDPGenRemoteSDPRecvDelay.bind(this)
3794
3893
  })
3795
3894
  .then((peerConnection) => this.getDevices().then((devices) => {
3796
3895
  MeetingUtil.handleDeviceLogging(devices);
@@ -4491,6 +4590,105 @@ export default class Meeting extends StatelessWebexPlugin {
4491
4590
  });
4492
4591
  }
4493
4592
 
4593
+ /**
4594
+ * Start sharing whiteboard given channelUrl
4595
+ * @param {string} channelUrl whiteboard url
4596
+ * @param {String} resourceToken token created by authorize media injector
4597
+ * @returns {Promise}
4598
+ * @public
4599
+ * @memberof Meeting
4600
+ */
4601
+ startWhiteboardShare(channelUrl, resourceToken) {
4602
+ const whiteboard = this.locusInfo.mediaShares.find((element) => element.name === 'whiteboard');
4603
+
4604
+ if (!channelUrl) {
4605
+ return Promise.reject(new ParameterError('Cannot share without channelUrl.'));
4606
+ }
4607
+
4608
+ if (whiteboard) {
4609
+ Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_INITIATED, meeting: this});
4610
+
4611
+ const body = {
4612
+ disposition: FLOOR_ACTION.GRANTED,
4613
+ personUrl: this.locusInfo.self.url,
4614
+ deviceUrl: this.deviceUrl,
4615
+ uri: whiteboard.url,
4616
+ resourceUrl: channelUrl
4617
+ };
4618
+
4619
+ if (resourceToken) {
4620
+ body.resourceToken = resourceToken;
4621
+ }
4622
+
4623
+ return this.meetingRequest.changeMeetingFloor(body)
4624
+ .then(() => {
4625
+ this.isSharing = false;
4626
+
4627
+ return Promise.resolve();
4628
+ })
4629
+ .catch((error) => {
4630
+ LoggerProxy.logger.error('Meeting:index#startWhiteboardShare --> Error ', error);
4631
+
4632
+ Metrics.sendOperationalMetric(
4633
+ METRICS_OPERATIONAL_MEASURES.MEETING_START_WHITEBOARD_SHARE_FAILURE,
4634
+ {
4635
+ correlation_id: this.correlationId,
4636
+ locus_id: this.locusUrl.split('/').pop(),
4637
+ reason: error.message,
4638
+ stack: error.stack,
4639
+ board: {channelUrl}
4640
+ }
4641
+ );
4642
+
4643
+ return Promise.reject(error);
4644
+ });
4645
+ }
4646
+
4647
+ return Promise.reject(new ParameterError('Cannot share without whiteboard.'));
4648
+ }
4649
+
4650
+ /**
4651
+ * Stop sharing whiteboard given channelUrl
4652
+ * @param {string} channelUrl whiteboard url
4653
+ * @returns {Promise}
4654
+ * @public
4655
+ * @memberof Meeting
4656
+ */
4657
+ stopWhiteboardShare(channelUrl) {
4658
+ const whiteboard = this.locusInfo.mediaShares.find((element) => element.name === 'whiteboard');
4659
+
4660
+ if (whiteboard) {
4661
+ Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_STOPPED, meeting: this});
4662
+
4663
+ return this.meetingRequest.changeMeetingFloor({
4664
+ disposition: FLOOR_ACTION.RELEASED,
4665
+ personUrl: this.locusInfo.self.url,
4666
+ deviceUrl: this.deviceUrl,
4667
+ uri: whiteboard.url
4668
+ })
4669
+ .catch((error) => {
4670
+ LoggerProxy.logger.error('Meeting:index#stopWhiteboardShare --> Error ', error);
4671
+
4672
+ Metrics.sendOperationalMetric(
4673
+ METRICS_OPERATIONAL_MEASURES.STOP_WHITEBOARD_SHARE_FAILURE,
4674
+ {
4675
+ correlation_id: this.correlationId,
4676
+ locus_id: this.locusUrl.split('/').pop(),
4677
+ reason: error.message,
4678
+ stack: error.stack,
4679
+ board: {channelUrl}
4680
+ }
4681
+ );
4682
+
4683
+ return Promise.reject(error);
4684
+ })
4685
+ .finally(() => {
4686
+ });
4687
+ }
4688
+
4689
+ return Promise.reject(new ParameterError('Cannot stop share without whiteboard.'));
4690
+ }
4691
+
4494
4692
  /**
4495
4693
  * Start sharing content with server
4496
4694
  * @returns {Promise} see #meetingRequest.changeMeetingFloor
@@ -4564,7 +4762,7 @@ export default class Meeting extends StatelessWebexPlugin {
4564
4762
  Metrics.postEvent({event: eventType.SHARE_STOPPED, meeting: this});
4565
4763
  Media.stopTracks(this.mediaProperties.shareTrack);
4566
4764
 
4567
- if (this.contentId !== this.selfId) {
4765
+ if (content.floor.beneficiary.id !== this.selfId) {
4568
4766
  // remote participant started sharing and caused our sharing to stop, we don't want to send any floor action request in that case
4569
4767
  this.isSharing = false;
4570
4768
 
@@ -5078,9 +5276,9 @@ export default class Meeting extends StatelessWebexPlugin {
5078
5276
  }
5079
5277
 
5080
5278
  /**
5081
- * @param {string} typeMedia 'audio' or 'video'
5082
- * @returns {undefined}
5083
- */
5279
+ * @param {string} typeMedia 'audio' or 'video'
5280
+ * @returns {undefined}
5281
+ */
5084
5282
  setEndSendingMediaDelay(typeMedia) {
5085
5283
  this[`endSendingMediaDelay${typeMedia}`] = performance.now();
5086
5284
  }
@@ -5095,4 +5293,127 @@ export default class Meeting extends StatelessWebexPlugin {
5095
5293
 
5096
5294
  return (start && end) ? end - start : undefined;
5097
5295
  }
5296
+
5297
+ /**
5298
+ *
5299
+ * @returns {undefined}
5300
+ */
5301
+ setStartLocalSDPGenRemoteSDPRecvDelay() {
5302
+ if (!this.startLocalSDPGenRemoteSDPRecvDelay) {
5303
+ this.startLocalSDPGenRemoteSDPRecvDelay = performance.now();
5304
+ this.endLocalSDPGenRemoteSDPRecvDelay = undefined;
5305
+ }
5306
+ }
5307
+
5308
+ /**
5309
+ *
5310
+ * @returns {undefined}
5311
+ */
5312
+ setEndLocalSDPGenRemoteSDPRecvDelay() {
5313
+ if (!this.endLocalSDPGenRemoteSDPRecvDelay) {
5314
+ this.endLocalSDPGenRemoteSDPRecvDelay = performance.now();
5315
+ }
5316
+ }
5317
+
5318
+ /**
5319
+ *
5320
+ * @returns {string} duration between local SDP generation and remote SDP reception
5321
+ */
5322
+ getLocalSDPGenRemoteSDPRecvDelay() {
5323
+ const start = this.startLocalSDPGenRemoteSDPRecvDelay;
5324
+ const end = this.endLocalSDPGenRemoteSDPRecvDelay;
5325
+
5326
+ if (start && end) {
5327
+ const calculatedDelay = end - start;
5328
+
5329
+ return calculatedDelay > METRICS_JOIN_TIMES_MAX_DURATION ?
5330
+ undefined :
5331
+ calculatedDelay;
5332
+ }
5333
+
5334
+ return undefined;
5335
+ }
5336
+
5337
+ /**
5338
+ *
5339
+ * @returns {undefined}
5340
+ */
5341
+ setStartCallInitiateJoinReq() {
5342
+ this.startCallInitiateJoinReq = performance.now();
5343
+ this.endCallInitiateJoinReq = undefined;
5344
+ }
5345
+
5346
+ /**
5347
+ *
5348
+ * @returns {undefined}
5349
+ */
5350
+ setEndCallInitiateJoinReq() {
5351
+ this.endCallInitiateJoinReq = performance.now();
5352
+ }
5353
+
5354
+ /**
5355
+ *
5356
+ * @returns {string} duration between call initiate and sending join request to locus
5357
+ */
5358
+ getCallInitiateJoinReq() {
5359
+ const start = this.startCallInitiateJoinReq;
5360
+ const end = this.endCallInitiateJoinReq;
5361
+
5362
+ if (start && end) {
5363
+ const calculatedDelay = end - start;
5364
+
5365
+ return calculatedDelay > METRICS_JOIN_TIMES_MAX_DURATION ?
5366
+ undefined :
5367
+ calculatedDelay;
5368
+ }
5369
+
5370
+ return undefined;
5371
+ }
5372
+
5373
+ /**
5374
+ *
5375
+ * @returns {undefined}
5376
+ */
5377
+ setStartJoinReqResp() {
5378
+ this.startJoinReqResp = performance.now();
5379
+ this.endJoinReqResp = undefined;
5380
+ }
5381
+
5382
+ /**
5383
+ *
5384
+ * @returns {undefined}
5385
+ */
5386
+ setEndJoinReqResp() {
5387
+ this.endJoinReqResp = performance.now();
5388
+ }
5389
+
5390
+ /**
5391
+ *
5392
+ * @returns {string} duration between sending locus join request and receiving join response
5393
+ */
5394
+ getJoinReqResp() {
5395
+ const start = this.startJoinReqResp;
5396
+ const end = this.endJoinReqResp;
5397
+
5398
+ if (start && end) {
5399
+ const calculatedDelay = end - start;
5400
+
5401
+ return calculatedDelay > METRICS_JOIN_TIMES_MAX_DURATION ?
5402
+ undefined :
5403
+ calculatedDelay;
5404
+ }
5405
+
5406
+ return undefined;
5407
+ }
5408
+
5409
+ /**
5410
+ *
5411
+ * @returns {string} duration between call initiate and successful locus join (even if it is in lobby)
5412
+ */
5413
+ getTotalJmt() {
5414
+ const start = this.startCallInitiateJoinReq;
5415
+ const end = this.endJoinReqResp;
5416
+
5417
+ return (start && end) ? end - start : undefined;
5418
+ }
5098
5419
  }
@@ -523,13 +523,19 @@ export default class MeetingRequest extends StatelessWebexPlugin {
523
523
  };
524
524
  }
525
525
 
526
+ const body = {
527
+ floor: floorReq,
528
+ resourceUrl: options.resourceUrl
529
+ };
530
+
531
+ if (options?.resourceToken) {
532
+ body.resourceToken = options?.resourceToken;
533
+ }
534
+
526
535
  return this.request({
527
536
  uri: options.uri,
528
537
  method: HTTP_VERBS.PUT,
529
- body: {
530
- floor: floorReq,
531
- resourceUrl: options.resourceUrl
532
- }
538
+ body
533
539
  });
534
540
  }
535
541
 
@@ -214,6 +214,9 @@ MeetingInfoUtil.getRequestBody = (options) => {
214
214
  if (destination.info?.webExMeetingId) {
215
215
  body.meetingKey = destination.info.webExMeetingId;
216
216
  }
217
+ else if (destination.info?.sipUri) {
218
+ body.sipUrl = destination.info.sipUri;
219
+ }
217
220
  break;
218
221
  case _MEETING_LINK_:
219
222
  body.meetingUrl = destination;
@@ -4,7 +4,7 @@
4
4
  import {isEmpty} from 'lodash';
5
5
  import {StatelessWebexPlugin} from '@webex/webex-core';
6
6
 
7
- import {MEETINGS, EVENT_TRIGGERS, FLOOR_ACTION, CONTENT} from '../constants';
7
+ import {MEETINGS, EVENT_TRIGGERS, FLOOR_ACTION, CONTENT, WHITEBOARD} from '../constants';
8
8
  import Trigger from '../common/events/trigger-proxy';
9
9
  import Member from '../member';
10
10
  import LoggerProxy from '../common/logs/logger-proxy';
@@ -132,6 +132,14 @@ export default class Members extends StatelessWebexPlugin {
132
132
  * @memberof Members
133
133
  */
134
134
  this.mediaShareContentId = null;
135
+ /**
136
+ * The current mediaShareWhiteboardId for the meeting
137
+ * @instance
138
+ * @type {String}
139
+ * @private
140
+ * @memberof Members
141
+ */
142
+ this.mediaShareWhiteboardId = null;
135
143
  /**
136
144
  * The current recordingId for the meeting, if it exists
137
145
  * @instance
@@ -281,20 +289,49 @@ export default class Members extends StatelessWebexPlugin {
281
289
  * @memberof Members
282
290
  */
283
291
  locusMediaSharesUpdate(payload) {
284
- const currentContent = payload.current;
285
- const previousContent = payload.previous;
292
+ const currentContent = payload.current?.content;
293
+ const previousContent = payload.previous?.content;
294
+ const currentWhiteboard = payload.current?.whiteboard;
295
+ const previousWhiteboard = payload.previous?.whiteboard;
286
296
  let whoSharing = null;
287
297
  let whoStopped = null;
288
298
 
289
- if (currentContent && currentContent.contentId) {
299
+ if (currentContent?.beneficiaryId) {
290
300
  if (currentContent.disposition === FLOOR_ACTION.GRANTED) {
291
- whoSharing = currentContent.contentId;
292
- whoStopped = previousContent && previousContent.contentId;
301
+ whoSharing = currentContent.beneficiaryId;
302
+ this.mediaShareWhiteboardId = null;
303
+ this.mediaShareContentId = whoSharing;
304
+ }
305
+
306
+ if (previousContent?.disposition === FLOOR_ACTION.GRANTED) {
307
+ if (currentContent.disposition === FLOOR_ACTION.RELEASED) {
308
+ whoStopped = currentContent.beneficiaryId;
309
+ this.mediaShareContentId = null;
310
+ }
311
+ else if (currentContent.disposition === FLOOR_ACTION.GRANTED && currentContent.beneficiaryId !== previousContent.beneficiaryId) {
312
+ whoStopped = previousContent.beneficiaryId;
313
+ }
293
314
  }
294
- else if (currentContent.disposition === FLOOR_ACTION.RELEASED) {
295
- whoStopped = currentContent.contentId;
315
+ }
316
+
317
+ if (currentWhiteboard?.beneficiaryId) {
318
+ if (currentWhiteboard.disposition === FLOOR_ACTION.GRANTED) {
319
+ whoSharing = currentWhiteboard.beneficiaryId;
320
+ this.mediaShareContentId = null;
321
+ this.mediaShareWhiteboardId = whoSharing;
322
+ }
323
+
324
+ if (previousWhiteboard?.disposition === FLOOR_ACTION.GRANTED) {
325
+ if (currentWhiteboard.disposition === FLOOR_ACTION.RELEASED) {
326
+ whoStopped = currentWhiteboard.beneficiaryId;
327
+ this.mediaShareWhiteboardId = null;
328
+ }
329
+ else if (currentWhiteboard.disposition === FLOOR_ACTION.GRANTED && currentWhiteboard.beneficiaryId !== previousWhiteboard.beneficiaryId) {
330
+ whoStopped = previousWhiteboard.beneficiaryId;
331
+ }
296
332
  }
297
333
  }
334
+
298
335
  if (whoSharing) {
299
336
  const shareMember = this.membersCollection.get(whoSharing);
300
337
 
@@ -309,7 +346,7 @@ export default class Members extends StatelessWebexPlugin {
309
346
  stopMember.setIsContentSharing(false);
310
347
  }
311
348
  }
312
- this.mediaShareContentId = whoSharing;
349
+
313
350
  Trigger.trigger(
314
351
  this,
315
352
  {
@@ -318,8 +355,8 @@ export default class Members extends StatelessWebexPlugin {
318
355
  },
319
356
  EVENT_TRIGGERS.MEMBERS_CONTENT_UPDATE,
320
357
  {
321
- activeContentSharingId: whoSharing,
322
- endedContentSharingId: whoStopped
358
+ activeSharingId: whoSharing,
359
+ endedSharingId: whoStopped
323
360
  }
324
361
  );
325
362
  }
@@ -524,6 +561,36 @@ export default class Members extends StatelessWebexPlugin {
524
561
  }
525
562
  }
526
563
 
564
+ /**
565
+ * Update the media share whiteboard id
566
+ * @param {Object} locus
567
+ * @param {String} [whiteboardId] optional, takes precedence
568
+ * @throws {Error}
569
+ * @returns {undefined}
570
+ * @memberof Members
571
+ */
572
+ setMediaShareWhiteboardId(locus, whiteboardId) {
573
+ if (whiteboardId) {
574
+ this.mediaShareWhiteboardId = whiteboardId;
575
+ }
576
+ else if (locus) {
577
+ const whiteboardMediaShare =
578
+ locus.mediaShares &&
579
+ locus.mediaShares.length &&
580
+ locus.mediaShares.find((mediaShare) => mediaShare.name === WHITEBOARD);
581
+
582
+ this.mediaShareWhiteboardId =
583
+ (whiteboardMediaShare &&
584
+ whiteboardMediaShare.floor &&
585
+ whiteboardMediaShare.floor.beneficiary &&
586
+ whiteboardMediaShare.floor.beneficiary.id) ||
587
+ null;
588
+ }
589
+ else {
590
+ throw new ParameterError('Setting hostid for the Members module should be done with a locus object or hostId');
591
+ }
592
+ }
593
+
527
594
  /**
528
595
  * Find all the updates, and added members
529
596
  * Removed/left members will end up in updates
@@ -552,6 +619,7 @@ export default class Members extends StatelessWebexPlugin {
552
619
  selfId: this.selfId,
553
620
  hostId: this.hostId,
554
621
  contentSharingId: this.mediaShareContentId,
622
+ whiteboardSharingId: this.mediaShareWhiteboardId,
555
623
  type: this.type
556
624
  })
557
625
  );
@@ -563,6 +631,7 @@ export default class Members extends StatelessWebexPlugin {
563
631
  selfId: this.selfId,
564
632
  hostId: this.hostId,
565
633
  contentSharingId: this.mediaShareContentId,
634
+ whiteboardSharingId: this.mediaShareWhiteboardId,
566
635
  type: this.type
567
636
  })
568
637
  );