@webex/plugin-meetings 2.60.1-next.6 → 2.60.1-next.8

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.
Files changed (54) hide show
  1. package/README.md +12 -0
  2. package/dist/breakouts/breakout.js +1 -1
  3. package/dist/breakouts/index.js +1 -1
  4. package/dist/constants.d.ts +14 -2
  5. package/dist/constants.js +18 -6
  6. package/dist/constants.js.map +1 -1
  7. package/dist/interpretation/index.js +1 -1
  8. package/dist/interpretation/siLanguage.js +1 -1
  9. package/dist/locus-info/index.d.ts +1 -1
  10. package/dist/locus-info/index.js +8 -8
  11. package/dist/locus-info/index.js.map +1 -1
  12. package/dist/meeting/index.d.ts +63 -19
  13. package/dist/meeting/index.js +703 -579
  14. package/dist/meeting/index.js.map +1 -1
  15. package/dist/meeting/request.js +25 -18
  16. package/dist/meeting/request.js.map +1 -1
  17. package/dist/meeting/util.d.ts +16 -0
  18. package/dist/meeting/util.js +71 -0
  19. package/dist/meeting/util.js.map +1 -1
  20. package/dist/meetings/index.d.ts +25 -3
  21. package/dist/meetings/index.js +83 -32
  22. package/dist/meetings/index.js.map +1 -1
  23. package/dist/reachability/index.js +11 -6
  24. package/dist/reachability/index.js.map +1 -1
  25. package/dist/reconnection-manager/index.js +3 -1
  26. package/dist/reconnection-manager/index.js.map +1 -1
  27. package/dist/roap/index.js +50 -54
  28. package/dist/roap/index.js.map +1 -1
  29. package/dist/statsAnalyzer/index.js +1 -1
  30. package/dist/statsAnalyzer/index.js.map +1 -1
  31. package/dist/statsAnalyzer/mqaUtil.js +13 -10
  32. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  33. package/dist/webinar/index.js +1 -1
  34. package/package.json +22 -22
  35. package/src/constants.ts +15 -2
  36. package/src/locus-info/index.ts +13 -12
  37. package/src/meeting/index.ts +256 -130
  38. package/src/meeting/request.ts +7 -0
  39. package/src/meeting/util.ts +97 -0
  40. package/src/meetings/index.ts +59 -18
  41. package/src/reachability/index.ts +7 -4
  42. package/src/reconnection-manager/index.ts +1 -1
  43. package/src/roap/index.ts +49 -51
  44. package/src/statsAnalyzer/index.ts +2 -2
  45. package/src/statsAnalyzer/mqaUtil.ts +15 -14
  46. package/test/unit/spec/locus-info/index.js +53 -5
  47. package/test/unit/spec/meeting/index.js +1792 -1139
  48. package/test/unit/spec/meeting/request.js +22 -12
  49. package/test/unit/spec/meeting/utils.js +93 -0
  50. package/test/unit/spec/meetings/index.js +180 -21
  51. package/test/unit/spec/reachability/index.ts +2 -1
  52. package/test/unit/spec/reconnection-manager/index.js +1 -0
  53. package/test/unit/spec/roap/index.ts +28 -42
  54. package/test/unit/spec/stats-analyzer/index.js +415 -30
@@ -233,6 +233,13 @@ export enum ScreenShareFloorStatus {
233
233
  RELEASED = 'floor_released',
234
234
  }
235
235
 
236
+ type FetchMeetingInfoParams = {
237
+ password?: string;
238
+ captchaCode?: string;
239
+ extraParams?: Record<string, any>;
240
+ sendCAevents?: boolean;
241
+ };
242
+
236
243
  /**
237
244
  * MediaDirection
238
245
  * @typedef {Object} MediaDirection
@@ -615,6 +622,8 @@ export default class Meeting extends StatelessWebexPlugin {
615
622
  environment: string;
616
623
  namespace = MEETINGS;
617
624
  allowMediaInLobby: boolean;
625
+ localShareInstanceId: string;
626
+ remoteShareInstanceId: string;
618
627
  turnDiscoverySkippedReason: string;
619
628
  turnServerUsed: boolean;
620
629
  areVoiceaEventsSetup = false;
@@ -1323,6 +1332,24 @@ export default class Meeting extends StatelessWebexPlugin {
1323
1332
  */
1324
1333
  this.keepAliveTimerId = null;
1325
1334
 
1335
+ /**
1336
+ * id for tracking Local Share instances in Call Analyzer
1337
+ * @instance
1338
+ * @type {String}
1339
+ * @private
1340
+ * @memberof Meeting
1341
+ */
1342
+ this.localShareInstanceId = null;
1343
+
1344
+ /**
1345
+ * id for tracking Remote Share instances in Call Analyzer
1346
+ * @instance
1347
+ * @type {String}
1348
+ * @private
1349
+ * @memberof Meeting
1350
+ */
1351
+ this.remoteShareInstanceId = null;
1352
+
1326
1353
  /**
1327
1354
  * The class that helps to control recording functions: start, stop, pause, resume, etc
1328
1355
  * @instance
@@ -1477,6 +1504,97 @@ export default class Meeting extends StatelessWebexPlugin {
1477
1504
  this.callStateForMetrics.correlationId = correlationId;
1478
1505
  }
1479
1506
 
1507
+ /**
1508
+ * Set meeting info and trigger `MEETING_INFO_AVAILABLE` event
1509
+ * @param {any} info
1510
+ * @param {string} [meetingLookupUrl] Lookup url, defined when the meeting info fetched
1511
+ * @returns {void}
1512
+ */
1513
+ private setMeetingInfo(info, meetingLookupUrl) {
1514
+ this.meetingInfo = info ? {...info, meetingLookupUrl} : null;
1515
+ this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NONE;
1516
+
1517
+ this.requiredCaptcha = null;
1518
+ if (
1519
+ this.passwordStatus === PASSWORD_STATUS.REQUIRED ||
1520
+ this.passwordStatus === PASSWORD_STATUS.VERIFIED
1521
+ ) {
1522
+ this.passwordStatus = PASSWORD_STATUS.VERIFIED;
1523
+ } else {
1524
+ this.passwordStatus = PASSWORD_STATUS.NOT_REQUIRED;
1525
+ }
1526
+
1527
+ Trigger.trigger(
1528
+ this,
1529
+ {
1530
+ file: 'meetings',
1531
+ function: 'fetchMeetingInfo',
1532
+ },
1533
+ EVENT_TRIGGERS.MEETING_INFO_AVAILABLE
1534
+ );
1535
+
1536
+ this.updateMeetingActions();
1537
+ }
1538
+
1539
+ /**
1540
+ * Add pre-fetched meeting info
1541
+ *
1542
+ * The passed meeting info should be be complete, e.g.: fetched after password or captcha provided
1543
+ *
1544
+ * @param {Object} meetingInfo - Complete meeting info
1545
+ * @param {FetchMeetingInfoParams} fetchParams - Fetch parameters for validation
1546
+ * @param {String|undefined} meetingLookupUrl - Lookup url, defined when the meeting info fetched
1547
+ * @returns {Promise<void>}
1548
+ */
1549
+ public async injectMeetingInfo(
1550
+ meetingInfo: any,
1551
+ fetchParams: FetchMeetingInfoParams,
1552
+ meetingLookupUrl: string | undefined
1553
+ ): Promise<void> {
1554
+ await this.prepForFetchMeetingInfo(fetchParams, 'injectMeetingInfo');
1555
+
1556
+ this.parseMeetingInfo(meetingInfo, this.destination);
1557
+ this.setMeetingInfo(meetingInfo, meetingLookupUrl);
1558
+ }
1559
+
1560
+ /**
1561
+ * Validate fetch parameters and clear the fetchMeetingInfoTimeout timeout
1562
+ *
1563
+ * @param {FetchMeetingInfoParams} fetchParams - fetch parameters for validation
1564
+ * @param {String} caller - Name of the caller for logging
1565
+ *
1566
+ * @returns {Promise<void>}
1567
+ * @private
1568
+ */
1569
+ private prepForFetchMeetingInfo(
1570
+ {password = null, captchaCode = null, extraParams = {}}: FetchMeetingInfoParams,
1571
+ caller: string
1572
+ ): Promise<void> {
1573
+ // when fetch meeting info is called directly by the client, we want to clear out the random timer for sdk to do it
1574
+ if (this.fetchMeetingInfoTimeoutId) {
1575
+ clearTimeout(this.fetchMeetingInfoTimeoutId);
1576
+ this.fetchMeetingInfoTimeoutId = undefined;
1577
+ }
1578
+ if (captchaCode && !this.requiredCaptcha) {
1579
+ return Promise.reject(
1580
+ new Error(`${caller}() called with captchaCode when captcha was not required`)
1581
+ );
1582
+ }
1583
+ if (
1584
+ password &&
1585
+ this.passwordStatus !== PASSWORD_STATUS.REQUIRED &&
1586
+ this.passwordStatus !== PASSWORD_STATUS.UNKNOWN
1587
+ ) {
1588
+ return Promise.reject(
1589
+ new Error(`${caller}() called with password when password was not required`)
1590
+ );
1591
+ }
1592
+
1593
+ this.meetingInfoExtraParams = cloneDeep(extraParams);
1594
+
1595
+ return Promise.resolve();
1596
+ }
1597
+
1480
1598
  /**
1481
1599
  * Internal method for fetching meeting info
1482
1600
  *
@@ -1507,29 +1625,8 @@ export default class Meeting extends StatelessWebexPlugin {
1507
1625
  {meetingId: this.id, sendCAevents}
1508
1626
  );
1509
1627
 
1510
- this.parseMeetingInfo(info, this.destination);
1511
- this.meetingInfo = info ? {...info.body, meetingLookupUrl: info?.url} : null;
1512
- this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NONE;
1513
- this.requiredCaptcha = null;
1514
- if (
1515
- this.passwordStatus === PASSWORD_STATUS.REQUIRED ||
1516
- this.passwordStatus === PASSWORD_STATUS.VERIFIED
1517
- ) {
1518
- this.passwordStatus = PASSWORD_STATUS.VERIFIED;
1519
- } else {
1520
- this.passwordStatus = PASSWORD_STATUS.NOT_REQUIRED;
1521
- }
1522
-
1523
- Trigger.trigger(
1524
- this,
1525
- {
1526
- file: 'meetings',
1527
- function: 'fetchMeetingInfo',
1528
- },
1529
- EVENT_TRIGGERS.MEETING_INFO_AVAILABLE
1530
- );
1531
-
1532
- this.updateMeetingActions();
1628
+ this.parseMeetingInfo(info?.body, this.destination, info?.errors);
1629
+ this.setMeetingInfo(info?.body, info?.url);
1533
1630
 
1534
1631
  return Promise.resolve();
1535
1632
  } catch (err) {
@@ -1673,46 +1770,13 @@ export default class Meeting extends StatelessWebexPlugin {
1673
1770
  * @memberof Meeting
1674
1771
  * @returns {Promise}
1675
1772
  */
1676
- public async fetchMeetingInfo({
1677
- password = null,
1678
- captchaCode = null,
1679
- extraParams = {},
1680
- sendCAevents = false,
1681
- }: {
1682
- password?: string;
1683
- captchaCode?: string;
1684
- extraParams?: Record<string, any>;
1685
- sendCAevents?: boolean;
1686
- }) {
1687
- // when fetch meeting info is called directly by the client, we want to clear out the random timer for sdk to do it
1688
- if (this.fetchMeetingInfoTimeoutId) {
1689
- clearTimeout(this.fetchMeetingInfoTimeoutId);
1690
- this.fetchMeetingInfoTimeoutId = undefined;
1691
- }
1692
- if (captchaCode && !this.requiredCaptcha) {
1693
- return Promise.reject(
1694
- new Error('fetchMeetingInfo() called with captchaCode when captcha was not required')
1695
- );
1696
- }
1697
- if (
1698
- password &&
1699
- this.passwordStatus !== PASSWORD_STATUS.REQUIRED &&
1700
- this.passwordStatus !== PASSWORD_STATUS.UNKNOWN
1701
- ) {
1702
- return Promise.reject(
1703
- new Error('fetchMeetingInfo() called with password when password was not required')
1704
- );
1705
- }
1706
-
1707
- this.meetingInfoExtraParams = cloneDeep(extraParams);
1773
+ public async fetchMeetingInfo(options: FetchMeetingInfoParams) {
1774
+ await this.prepForFetchMeetingInfo(options, 'fetchMeetingInfo');
1708
1775
 
1709
1776
  return this.fetchMeetingInfoInternal({
1710
1777
  destination: this.destination,
1711
1778
  destinationType: this.destinationType,
1712
- password,
1713
- captchaCode,
1714
- extraParams,
1715
- sendCAevents,
1779
+ ...options,
1716
1780
  });
1717
1781
  }
1718
1782
 
@@ -1965,6 +2029,9 @@ export default class Meeting extends StatelessWebexPlugin {
1965
2029
  * @memberof Meeting
1966
2030
  */
1967
2031
  private setUpVoiceaListeners() {
2032
+ // @ts-ignore
2033
+ this.webex.internal.voicea.listenToEvents();
2034
+
1968
2035
  // @ts-ignore
1969
2036
  this.webex.internal.voicea.on(
1970
2037
  VOICEAEVENTS.VOICEA_ANNOUNCEMENT,
@@ -1994,6 +2061,8 @@ export default class Meeting extends StatelessWebexPlugin {
1994
2061
  VOICEAEVENTS.HIGHLIGHT_CREATED,
1995
2062
  this.voiceaListenerCallbacks[VOICEAEVENTS.HIGHLIGHT_CREATED]
1996
2063
  );
2064
+
2065
+ this.areVoiceaEventsSetup = true;
1997
2066
  }
1998
2067
 
1999
2068
  /**
@@ -2362,16 +2431,6 @@ export default class Meeting extends StatelessWebexPlugin {
2362
2431
  }
2363
2432
  );
2364
2433
 
2365
- this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_JOIN_BREAKOUT_FROM_MAIN, ({mainLocusUrl}) => {
2366
- this.meetingRequest.getLocusStatusByUrl(mainLocusUrl).catch((error) => {
2367
- // clear main session cache when attendee join into breakout and forbidden to get locus from main locus url,
2368
- // which means main session is not active for the attendee
2369
- if (error?.statusCode === 403) {
2370
- this.locusInfo.clearMainSessionLocusCache();
2371
- }
2372
- });
2373
- });
2374
-
2375
2434
  this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_ENTRY_EXIT_TONE_UPDATED, ({entryExitTone}) => {
2376
2435
  Trigger.trigger(
2377
2436
  this,
@@ -2606,6 +2665,8 @@ export default class Meeting extends StatelessWebexPlugin {
2606
2665
  switch (newShareStatus) {
2607
2666
  case SHARE_STATUS.REMOTE_SHARE_ACTIVE: {
2608
2667
  const sendStartedSharingRemote = () => {
2668
+ this.remoteShareInstanceId = contentShare.shareInstanceId;
2669
+
2609
2670
  Trigger.trigger(
2610
2671
  this,
2611
2672
  {
@@ -2616,7 +2677,7 @@ export default class Meeting extends StatelessWebexPlugin {
2616
2677
  {
2617
2678
  memberId: contentShare.beneficiaryId,
2618
2679
  url: contentShare.url,
2619
- shareInstanceId: contentShare.shareInstanceId,
2680
+ shareInstanceId: this.remoteShareInstanceId,
2620
2681
  annotationInfo: contentShare.annotation,
2621
2682
  }
2622
2683
  );
@@ -2653,6 +2714,7 @@ export default class Meeting extends StatelessWebexPlugin {
2653
2714
  name: 'client.share.floor-granted.local',
2654
2715
  payload: {
2655
2716
  mediaType: 'share',
2717
+ shareInstanceId: this.localShareInstanceId,
2656
2718
  },
2657
2719
  options: {meetingId: this.id},
2658
2720
  });
@@ -2695,6 +2757,8 @@ export default class Meeting extends StatelessWebexPlugin {
2695
2757
  } else if (newShareStatus === SHARE_STATUS.REMOTE_SHARE_ACTIVE) {
2696
2758
  // if we got here, then some remote participant has stolen
2697
2759
  // the presentation from another remote participant
2760
+ this.remoteShareInstanceId = contentShare.shareInstanceId;
2761
+
2698
2762
  Trigger.trigger(
2699
2763
  this,
2700
2764
  {
@@ -2705,7 +2769,7 @@ export default class Meeting extends StatelessWebexPlugin {
2705
2769
  {
2706
2770
  memberId: contentShare.beneficiaryId,
2707
2771
  url: contentShare.url,
2708
- shareInstanceId: contentShare.shareInstanceId,
2772
+ shareInstanceId: this.remoteShareInstanceId,
2709
2773
  annotationInfo: contentShare.annotation,
2710
2774
  }
2711
2775
  );
@@ -3323,30 +3387,40 @@ export default class Meeting extends StatelessWebexPlugin {
3323
3387
  /**
3324
3388
  * Sets the meeting info on the class instance
3325
3389
  * @param {Object} meetingInfo
3326
- * @param {Object} meetingInfo.body
3327
- * @param {String} meetingInfo.body.conversationUrl
3328
- * @param {String} meetingInfo.body.locusUrl
3329
- * @param {String} meetingInfo.body.sipUri
3330
- * @param {Object} meetingInfo.body.owner
3390
+ * @param {String} meetingInfo.conversationUrl
3391
+ * @param {String} meetingInfo.locusUrl
3392
+ * @param {String} meetingInfo.sipUri
3393
+ * @param {String} [meetingInfo.sipUrl]
3394
+ * @param {String} [meetingInfo.sipMeetingUri]
3395
+ * @param {String} [meetingInfo.meetingNumber]
3396
+ * @param {String} [meetingInfo.meetingJoinUrl]
3397
+ * @param {String} [meetingInfo.hostId]
3398
+ * @param {String} [meetingInfo.permissionToken]
3399
+ * @param {String} [meetingInfo.channel]
3400
+ * @param {Object} meetingInfo.owner
3331
3401
  * @param {Object | String} destination locus object with meeting data or destination string (sip url, meeting link, etc)
3402
+ * @param {Object | String} errors Meeting info request error
3332
3403
  * @returns {undefined}
3333
3404
  * @private
3334
3405
  * @memberof Meeting
3335
3406
  */
3336
3407
  parseMeetingInfo(
3337
- meetingInfo:
3338
- | {
3339
- body: {
3340
- conversationUrl: string;
3341
- locusUrl: string;
3342
- sipUri: string;
3343
- owner: object;
3344
- };
3345
- }
3346
- | any,
3347
- destination: object | string | null = null
3408
+ meetingInfo: {
3409
+ conversationUrl: string;
3410
+ locusUrl: string;
3411
+ sipUri: string;
3412
+ owner: object;
3413
+ sipUrl?: string;
3414
+ sipMeetingUri?: string;
3415
+ meetingNumber?: string;
3416
+ meetingJoinUrl?: string;
3417
+ hostId?: string;
3418
+ permissionToken?: string;
3419
+ channel?: string;
3420
+ },
3421
+ destination: object | string | null = null,
3422
+ errors: any = undefined
3348
3423
  ) {
3349
- const webexMeetingInfo = meetingInfo?.body;
3350
3424
  // We try to use as much info from Locus meeting object, stored in destination
3351
3425
 
3352
3426
  let locusMeetingObject;
@@ -3356,40 +3430,31 @@ export default class Meeting extends StatelessWebexPlugin {
3356
3430
  }
3357
3431
 
3358
3432
  // MeetingInfo will be undefined for 1:1 calls
3359
- if (
3360
- locusMeetingObject ||
3361
- (webexMeetingInfo && !(meetingInfo?.errors && meetingInfo?.errors.length > 0))
3362
- ) {
3433
+ if (locusMeetingObject || (meetingInfo && !(errors?.length > 0))) {
3363
3434
  this.conversationUrl =
3364
- locusMeetingObject?.conversationUrl ||
3365
- webexMeetingInfo?.conversationUrl ||
3366
- this.conversationUrl;
3367
- this.locusUrl = locusMeetingObject?.url || webexMeetingInfo?.locusUrl || this.locusUrl;
3435
+ locusMeetingObject?.conversationUrl || meetingInfo?.conversationUrl || this.conversationUrl;
3436
+ this.locusUrl = locusMeetingObject?.url || meetingInfo?.locusUrl || this.locusUrl;
3368
3437
  // @ts-ignore - config coming from registerPlugin
3369
3438
  this.setSipUri(
3370
3439
  // @ts-ignore
3371
3440
  this.config.experimental.enableUnifiedMeetings
3372
- ? locusMeetingObject?.info.sipUri || webexMeetingInfo?.sipUrl
3373
- : locusMeetingObject?.info.sipUri || webexMeetingInfo?.sipMeetingUri || this.sipUri
3441
+ ? locusMeetingObject?.info.sipUri || meetingInfo?.sipUrl
3442
+ : locusMeetingObject?.info.sipUri || meetingInfo?.sipMeetingUri || this.sipUri
3374
3443
  );
3375
3444
  // @ts-ignore - config coming from registerPlugin
3376
3445
  if (this.config.experimental.enableUnifiedMeetings) {
3377
- this.meetingNumber =
3378
- locusMeetingObject?.info.webExMeetingId || webexMeetingInfo?.meetingNumber;
3379
- this.meetingJoinUrl = webexMeetingInfo?.meetingJoinUrl;
3446
+ this.meetingNumber = locusMeetingObject?.info.webExMeetingId || meetingInfo?.meetingNumber;
3447
+ this.meetingJoinUrl = meetingInfo?.meetingJoinUrl;
3380
3448
  }
3381
3449
  this.owner =
3382
- locusMeetingObject?.info.owner ||
3383
- webexMeetingInfo?.owner ||
3384
- webexMeetingInfo?.hostId ||
3385
- this.owner;
3386
- this.permissionToken = webexMeetingInfo?.permissionToken;
3387
- this.setPermissionTokenPayload(webexMeetingInfo?.permissionToken);
3450
+ locusMeetingObject?.info.owner || meetingInfo?.owner || meetingInfo?.hostId || this.owner;
3451
+ this.permissionToken = meetingInfo?.permissionToken;
3452
+ this.setPermissionTokenPayload(meetingInfo?.permissionToken);
3388
3453
  this.setSelfUserPolicies();
3389
3454
  // Need to populate environment when sending CA event
3390
- this.environment = locusMeetingObject?.info.channel || webexMeetingInfo?.channel;
3455
+ this.environment = locusMeetingObject?.info.channel || meetingInfo?.channel;
3391
3456
  }
3392
- MeetingUtil.parseInterpretationInfo(this, webexMeetingInfo);
3457
+ MeetingUtil.parseInterpretationInfo(this, meetingInfo);
3393
3458
  }
3394
3459
 
3395
3460
  /**
@@ -4587,13 +4652,11 @@ export default class Meeting extends StatelessWebexPlugin {
4587
4652
  const {statusCode} = payload;
4588
4653
 
4589
4654
  if (statusCode === 200) {
4590
- const currentCaptionLanguage =
4591
- this.transcription.languageOptions.requestedCaptionLanguage ?? LANGUAGE_ENGLISH;
4592
4655
  this.transcription.languageOptions = {
4593
4656
  ...this.transcription.languageOptions,
4594
- currentCaptionLanguage,
4657
+ currentCaptionLanguage: language,
4595
4658
  };
4596
- resolve(currentCaptionLanguage);
4659
+ resolve(language);
4597
4660
  } else {
4598
4661
  reject(payload);
4599
4662
  }
@@ -4677,10 +4740,12 @@ export default class Meeting extends StatelessWebexPlugin {
4677
4740
  try {
4678
4741
  if (!this.areVoiceaEventsSetup) {
4679
4742
  this.setUpVoiceaListeners();
4680
- this.areVoiceaEventsSetup = true;
4681
4743
  }
4682
- // @ts-ignore
4683
- await this.webex.internal.voicea.toggleTranscribing(true, options?.spokenLanguage);
4744
+
4745
+ if (this.getCurUserType() === 'host') {
4746
+ // @ts-ignore
4747
+ await this.webex.internal.voicea.toggleTranscribing(true, options?.spokenLanguage);
4748
+ }
4684
4749
  } catch (error) {
4685
4750
  LoggerProxy.logger.error(`Meeting:index#startTranscription --> ${error}`);
4686
4751
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.RECEIVE_TRANSCRIPTION_FAILURE, {
@@ -4769,6 +4834,7 @@ export default class Meeting extends StatelessWebexPlugin {
4769
4834
  VOICEAEVENTS.HIGHLIGHT_CREATED,
4770
4835
  this.voiceaListenerCallbacks[VOICEAEVENTS.HIGHLIGHT_CREATED]
4771
4836
  );
4837
+
4772
4838
  this.areVoiceaEventsSetup = false;
4773
4839
  this.triggerStopReceivingTranscriptionEvent();
4774
4840
  }
@@ -5014,15 +5080,33 @@ export default class Meeting extends StatelessWebexPlugin {
5014
5080
  .then((join) => {
5015
5081
  // @ts-ignore - config coming from registerPlugin
5016
5082
  if (this.config.enableAutomaticLLM) {
5017
- this.updateLLMConnection().catch((error) => {
5018
- LoggerProxy.logger.error('Meeting:index#join --> Update LLM Connection Failed', error);
5083
+ this.updateLLMConnection()
5084
+ .catch((error) => {
5085
+ LoggerProxy.logger.error(
5086
+ 'Meeting:index#join --> Transcription Socket Connection Failed',
5087
+ error
5088
+ );
5019
5089
 
5020
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.LLM_CONNECTION_AFTER_JOIN_FAILURE, {
5021
- correlation_id: this.correlationId,
5022
- reason: error?.message,
5023
- stack: error.stack,
5090
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.LLM_CONNECTION_AFTER_JOIN_FAILURE, {
5091
+ correlation_id: this.correlationId,
5092
+ reason: error?.message,
5093
+ stack: error.stack,
5094
+ });
5095
+ })
5096
+ .then(() => {
5097
+ LoggerProxy.logger.info(
5098
+ 'Meeting:index#join --> Transcription Socket Connection Success'
5099
+ );
5100
+ Trigger.trigger(
5101
+ this,
5102
+ {
5103
+ file: 'meeting/index',
5104
+ function: 'join',
5105
+ },
5106
+ EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED,
5107
+ undefined
5108
+ );
5024
5109
  });
5025
- });
5026
5110
  }
5027
5111
 
5028
5112
  return join;
@@ -5510,7 +5594,6 @@ export default class Meeting extends StatelessWebexPlugin {
5510
5594
  seq: event.roapMessage.seq,
5511
5595
  tieBreaker: event.roapMessage.tieBreaker,
5512
5596
  meeting: this, // or can pass meeting ID
5513
- reconnect: this.reconnectionManager.isReconnectInProgress(),
5514
5597
  })
5515
5598
  .then(({roapAnswer}) => {
5516
5599
  if (roapAnswer) {
@@ -5827,7 +5910,10 @@ export default class Meeting extends StatelessWebexPlugin {
5827
5910
  // @ts-ignore
5828
5911
  this.webex.internal.newMetrics.submitClientEvent({
5829
5912
  name: 'client.media.tx.start',
5830
- payload: {mediaType: data.type},
5913
+ payload: {
5914
+ mediaType: data.type,
5915
+ shareInstanceId: data.type === 'share' ? this.localShareInstanceId : undefined,
5916
+ },
5831
5917
  options: {
5832
5918
  meetingId: this.id,
5833
5919
  },
@@ -5837,7 +5923,10 @@ export default class Meeting extends StatelessWebexPlugin {
5837
5923
  // @ts-ignore
5838
5924
  this.webex.internal.newMetrics.submitClientEvent({
5839
5925
  name: 'client.media.tx.stop',
5840
- payload: {mediaType: data.type},
5926
+ payload: {
5927
+ mediaType: data.type,
5928
+ shareInstanceId: data.type === 'share' ? this.localShareInstanceId : undefined,
5929
+ },
5841
5930
  options: {
5842
5931
  meetingId: this.id,
5843
5932
  },
@@ -5856,7 +5945,10 @@ export default class Meeting extends StatelessWebexPlugin {
5856
5945
  // @ts-ignore
5857
5946
  this.webex.internal.newMetrics.submitClientEvent({
5858
5947
  name: 'client.media.rx.start',
5859
- payload: {mediaType: data.type},
5948
+ payload: {
5949
+ mediaType: data.type,
5950
+ shareInstanceId: data.type === 'share' ? this.remoteShareInstanceId : undefined,
5951
+ },
5860
5952
  options: {
5861
5953
  meetingId: this.id,
5862
5954
  },
@@ -5866,7 +5958,10 @@ export default class Meeting extends StatelessWebexPlugin {
5866
5958
  // @ts-ignore
5867
5959
  this.webex.internal.newMetrics.submitClientEvent({
5868
5960
  name: 'client.media.rx.stop',
5869
- payload: {mediaType: data.type},
5961
+ payload: {
5962
+ mediaType: data.type,
5963
+ shareInstanceId: data.type === 'share' ? this.remoteShareInstanceId : undefined,
5964
+ },
5870
5965
  options: {
5871
5966
  meetingId: this.id,
5872
5967
  },
@@ -7112,11 +7207,14 @@ export default class Meeting extends StatelessWebexPlugin {
7112
7207
  if (content && this.shareStatus !== SHARE_STATUS.LOCAL_SHARE_ACTIVE) {
7113
7208
  // @ts-ignore
7114
7209
  this.webex.internal.newMetrics.submitClientEvent({
7115
- name: 'client.share.initiated',
7210
+ name: 'client.share.floor-grant.request',
7116
7211
  payload: {
7117
7212
  mediaType: 'share',
7213
+ shareInstanceId: this.localShareInstanceId,
7214
+ },
7215
+ options: {
7216
+ meetingId: this.id,
7118
7217
  },
7119
- options: {meetingId: this.id},
7120
7218
  });
7121
7219
 
7122
7220
  return this.meetingRequest
@@ -7147,6 +7245,19 @@ export default class Meeting extends StatelessWebexPlugin {
7147
7245
  stack: error.stack,
7148
7246
  });
7149
7247
 
7248
+ // @ts-ignore
7249
+ this.webex.internal.newMetrics.submitClientEvent({
7250
+ name: 'client.share.floor-granted.local',
7251
+ payload: {
7252
+ mediaType: 'share',
7253
+ errors: MeetingUtil.getChangeMeetingFloorErrorPayload(error.message),
7254
+ shareInstanceId: this.localShareInstanceId,
7255
+ },
7256
+ options: {
7257
+ meetingId: this.id,
7258
+ },
7259
+ });
7260
+
7150
7261
  this.screenShareFloorState = ScreenShareFloorStatus.RELEASED;
7151
7262
 
7152
7263
  return Promise.reject(error);
@@ -7198,6 +7309,7 @@ export default class Meeting extends StatelessWebexPlugin {
7198
7309
  name: 'client.share.stopped',
7199
7310
  payload: {
7200
7311
  mediaType: 'share',
7312
+ shareInstanceId: this.localShareInstanceId,
7201
7313
  },
7202
7314
  options: {meetingId: this.id},
7203
7315
  });
@@ -7673,6 +7785,9 @@ export default class Meeting extends StatelessWebexPlugin {
7673
7785
  if (roles.includes(SELF_ROLES.COHOST)) {
7674
7786
  return 'cohost';
7675
7787
  }
7788
+ if (roles.includes(SELF_ROLES.PRESENTER)) {
7789
+ return 'presenter';
7790
+ }
7676
7791
  if (roles.includes(SELF_ROLES.ATTENDEE)) {
7677
7792
  return 'attendee';
7678
7793
  }
@@ -8077,6 +8192,17 @@ export default class Meeting extends StatelessWebexPlugin {
8077
8192
  }
8078
8193
 
8079
8194
  if (floorRequestNeeded) {
8195
+ this.localShareInstanceId = uuid.v4();
8196
+
8197
+ // @ts-ignore
8198
+ this.webex.internal.newMetrics.submitClientEvent({
8199
+ name: 'client.share.initiated',
8200
+ payload: {
8201
+ mediaType: 'share',
8202
+ shareInstanceId: this.localShareInstanceId,
8203
+ },
8204
+ options: {meetingId: this.id},
8205
+ });
8080
8206
  // we're sending the http request to Locus to request the screen share floor
8081
8207
  // only after the SDP update, because that's how it's always been done for transcoded meetings
8082
8208
  // and also if sharing from the start, we need confluence to have been created
@@ -185,6 +185,13 @@ export default class MeetingRequest extends StatelessWebexPlugin {
185
185
  deviceCapabilities.push(ANNOTATION.ANNOTATION_ON_SHARE_SUPPORTED);
186
186
  }
187
187
 
188
+ // append installationId to device config if it exists
189
+ // @ts-ignore
190
+ if (this.webex.internal.device.config.installationId) {
191
+ // @ts-ignore
192
+ body.device.installationId = this.webex.internal.device.config.installationId;
193
+ }
194
+
188
195
  if (locale) {
189
196
  body.locale = locale;
190
197
  }