@webex/plugin-meetings 2.60.0-next.1 → 2.60.0-next.10

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 (185) hide show
  1. package/dist/annotation/annotation.types.d.ts +42 -0
  2. package/dist/annotation/constants.d.ts +31 -0
  3. package/dist/annotation/index.d.ts +117 -0
  4. package/dist/breakouts/breakout.d.ts +8 -0
  5. package/dist/breakouts/breakout.js +1 -1
  6. package/dist/breakouts/collection.d.ts +5 -0
  7. package/dist/breakouts/edit-lock-error.d.ts +15 -0
  8. package/dist/breakouts/events.d.ts +8 -0
  9. package/dist/breakouts/index.d.ts +5 -0
  10. package/dist/breakouts/index.js +1 -1
  11. package/dist/breakouts/request.d.ts +22 -0
  12. package/dist/breakouts/utils.d.ts +15 -0
  13. package/dist/common/browser-detection.d.ts +9 -0
  14. package/dist/common/collection.d.ts +48 -0
  15. package/dist/common/config.d.ts +2 -0
  16. package/dist/common/errors/captcha-error.d.ts +15 -0
  17. package/dist/common/errors/intent-to-join.d.ts +16 -0
  18. package/dist/common/errors/join-meeting.d.ts +17 -0
  19. package/dist/common/errors/media.d.ts +15 -0
  20. package/dist/common/errors/no-meeting-info.d.ts +14 -0
  21. package/dist/common/errors/parameter.d.ts +15 -0
  22. package/dist/common/errors/password-error.d.ts +15 -0
  23. package/dist/common/errors/permission.d.ts +14 -0
  24. package/dist/common/errors/reclaim-host-role-errors.d.ts +60 -0
  25. package/dist/common/errors/reconnection-in-progress.d.ts +9 -0
  26. package/dist/common/errors/reconnection.d.ts +15 -0
  27. package/dist/common/errors/stats.d.ts +15 -0
  28. package/dist/common/errors/webex-errors.d.ts +93 -0
  29. package/dist/common/errors/webex-meetings-error.d.ts +20 -0
  30. package/dist/common/events/events-scope.d.ts +17 -0
  31. package/dist/common/events/events.d.ts +12 -0
  32. package/dist/common/events/trigger-proxy.d.ts +2 -0
  33. package/dist/common/events/util.d.ts +2 -0
  34. package/dist/common/logs/logger-config.d.ts +2 -0
  35. package/dist/common/logs/logger-proxy.d.ts +2 -0
  36. package/dist/common/logs/request.d.ts +36 -0
  37. package/dist/common/queue.d.ts +34 -0
  38. package/dist/config.d.ts +71 -0
  39. package/dist/constants.d.ts +1072 -0
  40. package/dist/constants.js +1 -1
  41. package/dist/constants.js.map +1 -1
  42. package/dist/controls-options-manager/constants.d.ts +4 -0
  43. package/dist/controls-options-manager/enums.d.ts +15 -0
  44. package/dist/controls-options-manager/enums.js +2 -1
  45. package/dist/controls-options-manager/enums.js.map +1 -1
  46. package/dist/controls-options-manager/index.d.ts +136 -0
  47. package/dist/controls-options-manager/types.d.ts +43 -0
  48. package/dist/controls-options-manager/util.d.ts +1 -0
  49. package/dist/index.d.ts +7 -0
  50. package/dist/interpretation/collection.d.ts +5 -0
  51. package/dist/interpretation/index.d.ts +5 -0
  52. package/dist/interpretation/index.js +1 -1
  53. package/dist/interpretation/siLanguage.d.ts +5 -0
  54. package/dist/interpretation/siLanguage.js +1 -1
  55. package/dist/locus-info/controlsUtils.d.ts +2 -0
  56. package/dist/locus-info/embeddedAppsUtils.d.ts +2 -0
  57. package/dist/locus-info/fullState.d.ts +2 -0
  58. package/dist/locus-info/hostUtils.d.ts +2 -0
  59. package/dist/locus-info/index.d.ts +322 -0
  60. package/dist/locus-info/infoUtils.d.ts +2 -0
  61. package/dist/locus-info/mediaSharesUtils.d.ts +2 -0
  62. package/dist/locus-info/parser.d.ts +272 -0
  63. package/dist/locus-info/parser.js +5 -5
  64. package/dist/locus-info/parser.js.map +1 -1
  65. package/dist/locus-info/selfUtils.d.ts +2 -0
  66. package/dist/media/index.d.ts +34 -0
  67. package/dist/media/index.js +6 -5
  68. package/dist/media/index.js.map +1 -1
  69. package/dist/media/properties.d.ts +93 -0
  70. package/dist/media/util.d.ts +2 -0
  71. package/dist/mediaQualityMetrics/config.d.ts +237 -0
  72. package/dist/mediaQualityMetrics/config.js +1 -202
  73. package/dist/mediaQualityMetrics/config.js.map +1 -1
  74. package/dist/meeting/in-meeting-actions.d.ts +167 -0
  75. package/dist/meeting/in-meeting-actions.js +4 -0
  76. package/dist/meeting/in-meeting-actions.js.map +1 -1
  77. package/dist/meeting/index.d.ts +1719 -0
  78. package/dist/meeting/index.js +288 -155
  79. package/dist/meeting/index.js.map +1 -1
  80. package/dist/meeting/locusMediaRequest.d.ts +74 -0
  81. package/dist/meeting/muteState.d.ts +184 -0
  82. package/dist/meeting/request.d.ts +290 -0
  83. package/dist/meeting/request.type.d.ts +11 -0
  84. package/dist/meeting/state.d.ts +9 -0
  85. package/dist/meeting/util.d.ts +103 -0
  86. package/dist/meeting-info/collection.d.ts +20 -0
  87. package/dist/meeting-info/index.d.ts +69 -0
  88. package/dist/meeting-info/meeting-info-v2.d.ts +123 -0
  89. package/dist/meeting-info/meeting-info-v2.js +3 -0
  90. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  91. package/dist/meeting-info/request.d.ts +22 -0
  92. package/dist/meeting-info/util.d.ts +2 -0
  93. package/dist/meeting-info/utilv2.d.ts +2 -0
  94. package/dist/meeting-info/utilv2.js +14 -29
  95. package/dist/meeting-info/utilv2.js.map +1 -1
  96. package/dist/meetings/collection.d.ts +40 -0
  97. package/dist/meetings/collection.js +17 -0
  98. package/dist/meetings/collection.js.map +1 -1
  99. package/dist/meetings/index.d.ts +378 -0
  100. package/dist/meetings/index.js +30 -9
  101. package/dist/meetings/index.js.map +1 -1
  102. package/dist/meetings/meetings.types.d.ts +4 -0
  103. package/dist/meetings/request.d.ts +27 -0
  104. package/dist/meetings/util.d.ts +18 -0
  105. package/dist/member/index.d.ts +160 -0
  106. package/dist/member/member.types.d.ts +11 -0
  107. package/dist/member/types.d.ts +32 -0
  108. package/dist/member/util.d.ts +2 -0
  109. package/dist/members/collection.d.ts +29 -0
  110. package/dist/members/index.d.ts +353 -0
  111. package/dist/members/request.d.ts +114 -0
  112. package/dist/members/types.d.ts +25 -0
  113. package/dist/members/util.d.ts +215 -0
  114. package/dist/metrics/constants.d.ts +68 -0
  115. package/dist/metrics/constants.js +3 -0
  116. package/dist/metrics/constants.js.map +1 -1
  117. package/dist/metrics/index.d.ts +45 -0
  118. package/dist/multistream/mediaRequestManager.d.ts +118 -0
  119. package/dist/multistream/receiveSlot.d.ts +68 -0
  120. package/dist/multistream/receiveSlotManager.d.ts +56 -0
  121. package/dist/multistream/remoteMedia.d.ts +72 -0
  122. package/dist/multistream/remoteMediaGroup.d.ts +47 -0
  123. package/dist/multistream/remoteMediaManager.d.ts +285 -0
  124. package/dist/multistream/sendSlotManager.d.ts +61 -0
  125. package/dist/networkQualityMonitor/index.d.ts +70 -0
  126. package/dist/personal-meeting-room/index.d.ts +47 -0
  127. package/dist/personal-meeting-room/request.d.ts +14 -0
  128. package/dist/personal-meeting-room/util.d.ts +2 -0
  129. package/dist/reachability/index.d.ts +194 -0
  130. package/dist/reachability/request.d.ts +39 -0
  131. package/dist/reactions/constants.d.ts +3 -0
  132. package/dist/reactions/reactions.d.ts +4 -0
  133. package/dist/reactions/reactions.type.d.ts +52 -0
  134. package/dist/reconnection-manager/index.d.ts +136 -0
  135. package/dist/reconnection-manager/index.js +27 -28
  136. package/dist/reconnection-manager/index.js.map +1 -1
  137. package/dist/recording-controller/enums.d.ts +7 -0
  138. package/dist/recording-controller/index.d.ts +207 -0
  139. package/dist/recording-controller/util.d.ts +14 -0
  140. package/dist/roap/index.d.ts +78 -0
  141. package/dist/roap/request.d.ts +41 -0
  142. package/dist/roap/turnDiscovery.d.ts +92 -0
  143. package/dist/rtcMetrics/constants.d.ts +4 -0
  144. package/dist/rtcMetrics/index.d.ts +54 -0
  145. package/dist/rtcMetrics/index.js +25 -0
  146. package/dist/rtcMetrics/index.js.map +1 -1
  147. package/dist/statsAnalyzer/global.d.ts +36 -0
  148. package/dist/statsAnalyzer/index.d.ts +191 -0
  149. package/dist/statsAnalyzer/index.js +53 -146
  150. package/dist/statsAnalyzer/index.js.map +1 -1
  151. package/dist/statsAnalyzer/mqaUtil.d.ts +24 -0
  152. package/dist/statsAnalyzer/mqaUtil.js +11 -12
  153. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  154. package/dist/transcription/index.d.ts +64 -0
  155. package/dist/webinar/collection.d.ts +16 -0
  156. package/dist/webinar/index.d.ts +5 -0
  157. package/dist/webinar/index.js +1 -1
  158. package/package.json +26 -27
  159. package/src/constants.ts +10 -4
  160. package/src/controls-options-manager/enums.ts +2 -0
  161. package/src/locus-info/parser.ts +6 -6
  162. package/src/media/index.ts +5 -5
  163. package/src/mediaQualityMetrics/config.ts +0 -135
  164. package/src/meeting/in-meeting-actions.ts +8 -0
  165. package/src/meeting/index.ts +263 -125
  166. package/src/meeting-info/meeting-info-v2.ts +4 -0
  167. package/src/meeting-info/utilv2.ts +6 -19
  168. package/src/meetings/collection.ts +13 -0
  169. package/src/meetings/index.ts +28 -10
  170. package/src/metrics/constants.ts +3 -0
  171. package/src/reconnection-manager/index.ts +63 -68
  172. package/src/rtcMetrics/index.ts +24 -0
  173. package/src/statsAnalyzer/index.ts +68 -216
  174. package/src/statsAnalyzer/mqaUtil.ts +17 -22
  175. package/test/unit/spec/media/index.ts +20 -4
  176. package/test/unit/spec/meeting/in-meeting-actions.ts +4 -0
  177. package/test/unit/spec/meeting/index.js +1376 -189
  178. package/test/unit/spec/meeting/muteState.js +2 -1
  179. package/test/unit/spec/meeting-info/meetinginfov2.js +28 -0
  180. package/test/unit/spec/meetings/collection.js +12 -0
  181. package/test/unit/spec/meetings/index.js +382 -118
  182. package/test/unit/spec/member/util.js +0 -31
  183. package/test/unit/spec/reconnection-manager/index.js +42 -12
  184. package/test/unit/spec/rtcMetrics/index.ts +20 -0
  185. package/test/unit/spec/stats-analyzer/index.js +12 -2
@@ -9,7 +9,6 @@ import {
9
9
  ClientEvent,
10
10
  ClientEventLeaveReason,
11
11
  CallDiagnosticUtils,
12
- CALL_DIAGNOSTIC_CONFIG,
13
12
  } from '@webex/internal-plugin-metrics';
14
13
  import {
15
14
  ConnectionState,
@@ -105,6 +104,7 @@ import {
105
104
  MEETING_PERMISSION_TOKEN_REFRESH_THRESHOLD_IN_SEC,
106
105
  MEETING_PERMISSION_TOKEN_REFRESH_REASON,
107
106
  ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT,
107
+ RECONNECTION,
108
108
  } from '../constants';
109
109
  import BEHAVIORAL_METRICS from '../metrics/constants';
110
110
  import ParameterError from '../common/errors/parameter';
@@ -178,6 +178,12 @@ export type AddMediaOptions = {
178
178
  allowMediaInLobby?: boolean; // allows adding media when in the lobby
179
179
  };
180
180
 
181
+ export type CallStateForMetrics = {
182
+ correlationId?: string;
183
+ joinTrigger?: string;
184
+ loginType?: string;
185
+ };
186
+
181
187
  export const MEDIA_UPDATE_TYPE = {
182
188
  TRANSCODED_MEDIA_CONNECTION: 'TRANSCODED_MEDIA_CONNECTION',
183
189
  SHARE_FLOOR_REQUEST: 'SHARE_FLOOR_REQUEST',
@@ -470,7 +476,7 @@ export default class Meeting extends StatelessWebexPlugin {
470
476
  annotation: any;
471
477
  webinar: any;
472
478
  conversationUrl: string;
473
- correlationId: string;
479
+ callStateForMetrics: CallStateForMetrics;
474
480
  destination: string;
475
481
  destinationType: string;
476
482
  deviceUrl: string;
@@ -528,7 +534,7 @@ export default class Meeting extends StatelessWebexPlugin {
528
534
  meetingInfoFailureCode?: number;
529
535
  meetingInfoExtraParams?: Record<string, any>;
530
536
  networkQualityMonitor: NetworkQualityMonitor;
531
- networkStatus: string;
537
+ networkStatus?: NETWORK_STATUS;
532
538
  passwordStatus: string;
533
539
  queuedMediaUpdates: any[];
534
540
  recording: any;
@@ -538,6 +544,7 @@ export default class Meeting extends StatelessWebexPlugin {
538
544
  requiredCaptcha: any;
539
545
  receiveSlotManager: ReceiveSlotManager;
540
546
  selfUserPolicies: any;
547
+ enforceVBGImagesURL: string;
541
548
  shareStatus: string;
542
549
  screenShareFloorState: ScreenShareFloorStatus;
543
550
  statsAnalyzer: StatsAnalyzer;
@@ -560,6 +567,7 @@ export default class Meeting extends StatelessWebexPlugin {
560
567
  meetingState: any;
561
568
  permissionToken: string;
562
569
  permissionTokenPayload: any;
570
+ permissionTokenReceivedLocalTime: number;
563
571
  resourceId: any;
564
572
  resourceUrl: string;
565
573
  selfId: string;
@@ -577,6 +585,7 @@ export default class Meeting extends StatelessWebexPlugin {
577
585
  private sendSlotManager: SendSlotManager = new SendSlotManager(LoggerProxy);
578
586
  private deferSDPAnswer?: Defer; // used for waiting for a response
579
587
  private sdpResponseTimer?: ReturnType<typeof setTimeout>;
588
+ private hasMediaConnectionConnectedAtLeastOnce: boolean;
580
589
 
581
590
  /**
582
591
  * @param {Object} attrs
@@ -611,20 +620,22 @@ export default class Meeting extends StatelessWebexPlugin {
611
620
  */
612
621
  this.id = uuid.v4();
613
622
  /**
614
- * Correlation ID used for network tracking of meeting
623
+ * Call state used for metrics
615
624
  * @instance
616
- * @type {String}
625
+ * @type {CallStateForMetrics}
617
626
  * @readonly
618
627
  * @public
619
628
  * @memberof Meeting
620
629
  */
621
- if (attrs.correlationId) {
630
+ this.callStateForMetrics = attrs.callStateForMetrics || {};
631
+ const correlationId = attrs.correlationId || attrs.callStateForMetrics?.correlationId;
632
+ if (correlationId) {
622
633
  LoggerProxy.logger.log(
623
- `Meetings:index#constructor --> Initializing the meeting object with correlation id from app ${this.correlationId}`
634
+ `Meetings:index#constructor --> Initializing the meeting object with correlation id from app ${correlationId}`
624
635
  );
625
- this.correlationId = attrs.correlationId;
636
+ this.callStateForMetrics.correlationId = correlationId;
626
637
  } else {
627
- this.correlationId = this.id;
638
+ this.callStateForMetrics.correlationId = this.id;
628
639
  }
629
640
  /**
630
641
  * @instance
@@ -1096,13 +1107,14 @@ export default class Meeting extends StatelessWebexPlugin {
1096
1107
  */
1097
1108
  this.networkQualityMonitor = null;
1098
1109
  /**
1110
+ * Indicates network status of the webrtc media connection
1099
1111
  * @instance
1100
1112
  * @type {String}
1101
1113
  * @readonly
1102
1114
  * @public
1103
1115
  * @memberof Meeting
1104
1116
  */
1105
- this.networkStatus = null;
1117
+ this.networkStatus = undefined;
1106
1118
  /**
1107
1119
  * Passing only info as we send basic info for meeting added event
1108
1120
  * @instance
@@ -1318,6 +1330,15 @@ export default class Meeting extends StatelessWebexPlugin {
1318
1330
  * @memberof Meeting
1319
1331
  */
1320
1332
  this.retriedWithTurnServer = false;
1333
+
1334
+ /**
1335
+ * Whether or not the media connection has ever successfully connected.
1336
+ * @instance
1337
+ * @type {boolean}
1338
+ * @private
1339
+ * @memberof Meeting
1340
+ */
1341
+ this.hasMediaConnectionConnectedAtLeastOnce = false;
1321
1342
  }
1322
1343
 
1323
1344
  /**
@@ -1350,6 +1371,22 @@ export default class Meeting extends StatelessWebexPlugin {
1350
1371
  return this.type === 'CALL';
1351
1372
  }
1352
1373
 
1374
+ /**
1375
+ * Getter - Returns callStateForMetrics.correlationId
1376
+ * @returns {string}
1377
+ */
1378
+ get correlationId() {
1379
+ return this.callStateForMetrics.correlationId;
1380
+ }
1381
+
1382
+ /**
1383
+ * Setter - sets callStateForMetrics.correlationId
1384
+ * @param {string} correlationId
1385
+ */
1386
+ set correlationId(correlationId: string) {
1387
+ this.callStateForMetrics.correlationId = correlationId;
1388
+ }
1389
+
1353
1390
  /**
1354
1391
  * Internal method for fetching meeting info
1355
1392
  *
@@ -1491,15 +1528,21 @@ export default class Meeting extends StatelessWebexPlugin {
1491
1528
  : this.destination;
1492
1529
  const destinationType = isStartingSpaceInstantV2Meeting ? _MEETING_LINK_ : this.destinationType;
1493
1530
 
1494
- const timeLeft = this.getPermissionTokenTimeLeftInSec();
1531
+ const permissionTokenExpiryInfo = this.getPermissionTokenExpiryInfo();
1532
+
1533
+ const timeLeft = permissionTokenExpiryInfo?.timeLeft;
1534
+ const expiryTime = permissionTokenExpiryInfo?.expiryTime;
1535
+ const currentTime = permissionTokenExpiryInfo?.currentTime;
1495
1536
 
1496
1537
  LoggerProxy.logger.info(
1497
- `Meeting:index#refreshPermissionToken --> refreshing permission token, destinationType=${destinationType}, timeLeft=${timeLeft}, reason=${reason}`
1538
+ `Meeting:index#refreshPermissionToken --> refreshing permission token, destinationType=${destinationType}, timeLeft=${timeLeft}, permissionTokenExpiry=${expiryTime}, currentTimestamp=${currentTime},reason=${reason}`
1498
1539
  );
1499
1540
 
1500
1541
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.PERMISSION_TOKEN_REFRESH, {
1501
1542
  correlationId: this.correlationId,
1502
1543
  timeLeft,
1544
+ expiryTime,
1545
+ currentTime,
1503
1546
  reason,
1504
1547
  destinationType,
1505
1548
  });
@@ -1928,12 +1971,12 @@ export default class Meeting extends StatelessWebexPlugin {
1928
1971
 
1929
1972
  /**
1930
1973
  * sets the network status on meeting object
1931
- * @param {String} networkStatus
1974
+ * @param {NETWORK_STATUS} networkStatus
1932
1975
  * @private
1933
1976
  * @returns {undefined}
1934
1977
  * @memberof Meeting
1935
1978
  */
1936
- private setNetworkStatus(networkStatus: string) {
1979
+ private setNetworkStatus(networkStatus?: NETWORK_STATUS) {
1937
1980
  if (networkStatus === NETWORK_STATUS.DISCONNECTED) {
1938
1981
  Trigger.trigger(
1939
1982
  this,
@@ -3275,6 +3318,11 @@ export default class Meeting extends StatelessWebexPlugin {
3275
3318
  }) &&
3276
3319
  this.meetingInfo?.video?.supportHDV) ||
3277
3320
  !this.arePolicyRestrictionsSupported(),
3321
+ enforceVirtualBackground:
3322
+ ControlsOptionsUtil.hasPolicies({
3323
+ requiredPolicies: [SELF_POLICY.ENFORCE_VIRTUAL_BACKGROUND],
3324
+ policies: this.selfUserPolicies,
3325
+ }) && this.arePolicyRestrictionsSupported(),
3278
3326
  supportHQV:
3279
3327
  (ControlsOptionsUtil.hasPolicies({
3280
3328
  requiredPolicies: [SELF_POLICY.SUPPORT_HQV],
@@ -3428,6 +3476,10 @@ export default class Meeting extends StatelessWebexPlugin {
3428
3476
  requiredPolicies: [SELF_POLICY.SUPPORT_FILE_TRANSFER],
3429
3477
  policies: this.selfUserPolicies,
3430
3478
  }),
3479
+ canChat: ControlsOptionsUtil.hasPolicies({
3480
+ requiredPolicies: [SELF_POLICY.SUPPORT_CHAT],
3481
+ policies: this.selfUserPolicies,
3482
+ }),
3431
3483
  canShareApplication:
3432
3484
  (ControlsOptionsUtil.hasHints({
3433
3485
  requiredHints: [DISPLAY_HINTS.SHARE_APPLICATION],
@@ -3487,6 +3539,7 @@ export default class Meeting extends StatelessWebexPlugin {
3487
3539
  */
3488
3540
  setSelfUserPolicies() {
3489
3541
  this.selfUserPolicies = this.permissionTokenPayload?.permission?.userPolicies;
3542
+ this.enforceVBGImagesURL = this.permissionTokenPayload?.permission?.enforceVBGImagesURL;
3490
3543
  }
3491
3544
 
3492
3545
  /**
@@ -3497,6 +3550,7 @@ export default class Meeting extends StatelessWebexPlugin {
3497
3550
  */
3498
3551
  public setPermissionTokenPayload(permissionToken: string) {
3499
3552
  this.permissionTokenPayload = decode(permissionToken);
3553
+ this.permissionTokenReceivedLocalTime = new Date().getTime();
3500
3554
  }
3501
3555
 
3502
3556
  /**
@@ -3590,8 +3644,7 @@ export default class Meeting extends StatelessWebexPlugin {
3590
3644
  * @memberof Meeting
3591
3645
  */
3592
3646
  closeRemoteStreams() {
3593
- const {remoteAudioStream, remoteVideoStream, remoteShareStream, shareAudioStream} =
3594
- this.mediaProperties;
3647
+ const {remoteAudioStream, remoteVideoStream, remoteShareStream} = this.mediaProperties;
3595
3648
 
3596
3649
  /**
3597
3650
  * Triggers an event to the developer
@@ -3632,7 +3685,6 @@ export default class Meeting extends StatelessWebexPlugin {
3632
3685
  stopStream(remoteAudioStream, EVENT_TYPES.REMOTE_AUDIO),
3633
3686
  stopStream(remoteVideoStream, EVENT_TYPES.REMOTE_VIDEO),
3634
3687
  stopStream(remoteShareStream, EVENT_TYPES.REMOTE_SHARE),
3635
- stopStream(shareAudioStream, EVENT_TYPES.REMOTE_SHARE_AUDIO),
3636
3688
  ]);
3637
3689
  }
3638
3690
 
@@ -3703,11 +3755,16 @@ export default class Meeting extends StatelessWebexPlugin {
3703
3755
  private async setLocalShareVideoStream(localDisplayStream?: LocalDisplayStream) {
3704
3756
  const oldStream = this.mediaProperties.shareVideoStream;
3705
3757
 
3758
+ oldStream?.off(StreamEventNames.MuteStateChange, this.handleShareVideoStreamMuteStateChange);
3706
3759
  oldStream?.off(StreamEventNames.Ended, this.handleShareVideoStreamEnded);
3707
3760
  oldStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
3708
3761
 
3709
3762
  this.mediaProperties.setLocalShareVideoStream(localDisplayStream);
3710
3763
 
3764
+ localDisplayStream?.on(
3765
+ StreamEventNames.MuteStateChange,
3766
+ this.handleShareVideoStreamMuteStateChange
3767
+ );
3711
3768
  localDisplayStream?.on(StreamEventNames.Ended, this.handleShareVideoStreamEnded);
3712
3769
  localDisplayStream?.on(
3713
3770
  LocalStreamEventNames.OutputTrackChange,
@@ -3763,7 +3820,7 @@ export default class Meeting extends StatelessWebexPlugin {
3763
3820
  functionName: string;
3764
3821
  isPublished: boolean;
3765
3822
  mediaType: MediaType;
3766
- stream: MediaStream;
3823
+ stream: LocalStream;
3767
3824
  }) {
3768
3825
  const {functionName, isPublished, mediaType, stream} = options;
3769
3826
  Trigger.trigger(
@@ -3797,12 +3854,16 @@ export default class Meeting extends StatelessWebexPlugin {
3797
3854
  videoStream?.off(StreamEventNames.MuteStateChange, this.localVideoStreamMuteStateHandler);
3798
3855
  videoStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
3799
3856
 
3800
- shareAudioStream?.off(StreamEventNames.MuteStateChange, this.handleShareAudioStreamEnded);
3857
+ shareAudioStream?.off(StreamEventNames.Ended, this.handleShareAudioStreamEnded);
3801
3858
  shareAudioStream?.off(
3802
3859
  LocalStreamEventNames.OutputTrackChange,
3803
3860
  this.localOutputTrackChangeHandler
3804
3861
  );
3805
- shareVideoStream?.off(StreamEventNames.MuteStateChange, this.handleShareVideoStreamEnded);
3862
+ shareVideoStream?.off(
3863
+ StreamEventNames.MuteStateChange,
3864
+ this.handleShareVideoStreamMuteStateChange
3865
+ );
3866
+ shareVideoStream?.off(StreamEventNames.Ended, this.handleShareVideoStreamEnded);
3806
3867
  shareVideoStream?.off(
3807
3868
  LocalStreamEventNames.OutputTrackChange,
3808
3869
  this.localOutputTrackChangeHandler
@@ -3916,6 +3977,7 @@ export default class Meeting extends StatelessWebexPlugin {
3916
3977
  this.receiveSlotManager.reset();
3917
3978
  this.mediaProperties.webrtcMediaConnection.close();
3918
3979
  this.sendSlotManager.reset();
3980
+ this.setNetworkStatus(undefined);
3919
3981
  }
3920
3982
 
3921
3983
  this.audio = null;
@@ -3943,14 +4005,25 @@ export default class Meeting extends StatelessWebexPlugin {
3943
4005
  }
3944
4006
 
3945
4007
  /**
3946
- * Convenience method to set the correlation id for the Meeting
3947
- * @param {String} id correlation id to set on the class
4008
+ * Convenience method to set the correlation id for the callStateForMetrics
4009
+ * @param {String} id correlation id to set on the callStateForMetrics
3948
4010
  * @returns {undefined}
3949
4011
  * @public
3950
4012
  * @memberof Meeting
3951
4013
  */
3952
4014
  public setCorrelationId(id: string) {
3953
- this.correlationId = id;
4015
+ this.callStateForMetrics.correlationId = id;
4016
+ }
4017
+
4018
+ /**
4019
+ * Update the callStateForMetrics
4020
+ * @param {CallStateForMetrics} callStateForMetrics updated values for callStateForMetrics
4021
+ * @returns {undefined}
4022
+ * @public
4023
+ * @memberof Meeting
4024
+ */
4025
+ public updateCallStateForMetrics(callStateForMetrics: CallStateForMetrics) {
4026
+ this.callStateForMetrics = {...this.callStateForMetrics, ...callStateForMetrics};
3954
4027
  }
3955
4028
 
3956
4029
  /**
@@ -4268,6 +4341,8 @@ export default class Meeting extends StatelessWebexPlugin {
4268
4341
 
4269
4342
  return this.reconnectionManager
4270
4343
  .reconnect(options)
4344
+ .then(() => this.waitForRemoteSDPAnswer())
4345
+ .then(() => this.waitForMediaConnectionConnected())
4271
4346
  .then(() => {
4272
4347
  Trigger.trigger(
4273
4348
  this,
@@ -4278,6 +4353,18 @@ export default class Meeting extends StatelessWebexPlugin {
4278
4353
  EVENT_TRIGGERS.MEETING_RECONNECTION_SUCCESS
4279
4354
  );
4280
4355
  LoggerProxy.logger.log('Meeting:index#reconnect --> Meeting reconnect success');
4356
+
4357
+ // @ts-ignore
4358
+ this.webex.internal.newMetrics.submitClientEvent({
4359
+ name: 'client.media.recovered',
4360
+ payload: {
4361
+ recoveredBy: 'new',
4362
+ },
4363
+ options: {
4364
+ meetingId: this.id,
4365
+ },
4366
+ });
4367
+ this.reconnectionManager.setStatus(RECONNECTION.STATE.COMPLETE);
4281
4368
  })
4282
4369
  .catch((error) => {
4283
4370
  Trigger.trigger(
@@ -4587,7 +4674,11 @@ export default class Meeting extends StatelessWebexPlugin {
4587
4674
  // @ts-ignore
4588
4675
  this.webex.internal.newMetrics.submitClientEvent({
4589
4676
  name: 'client.call.initiated',
4590
- payload: {trigger: 'user-interaction', isRoapCallEnabled: true},
4677
+ payload: {
4678
+ trigger: this.callStateForMetrics.joinTrigger || 'user-interaction',
4679
+ isRoapCallEnabled: true,
4680
+ pstnAudioType: options?.pstnAudioType,
4681
+ },
4591
4682
  options: {meetingId: this.id},
4592
4683
  });
4593
4684
 
@@ -4753,7 +4844,7 @@ export default class Meeting extends StatelessWebexPlugin {
4753
4844
  .then((join) => {
4754
4845
  if (isBrowser) {
4755
4846
  // @ts-ignore - config coming from registerPlugin
4756
- if (this.config.receiveTranscription || this.receiveTranscription) {
4847
+ if (this.config.receiveTranscription || options.receiveTranscription) {
4757
4848
  if (this.isTranscriptionSupported()) {
4758
4849
  LoggerProxy.logger.info(
4759
4850
  'Meeting:index#join --> Attempting to enabled to receive transcription!'
@@ -5398,40 +5489,25 @@ export default class Meeting extends StatelessWebexPlugin {
5398
5489
 
5399
5490
  this.mediaProperties.webrtcMediaConnection.on(Event.CONNECTION_STATE_CHANGED, (event) => {
5400
5491
  const connectionFailed = () => {
5401
- // we know the media connection failed and browser will not attempt to recover it any more
5402
- // so reset the timer as it's not needed anymore, we want to reconnect immediately
5403
- this.reconnectionManager.resetReconnectionTimer();
5404
-
5405
- this.reconnect({networkDisconnect: true});
5406
- // @ts-ignore
5407
- this.webex.internal.newMetrics.submitClientEvent({
5408
- name: 'client.ice.end',
5409
- payload: {
5410
- canProceed: false,
5411
- icePhase: 'IN_MEETING',
5412
- errors: [
5413
- // @ts-ignore
5414
- this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode(
5415
- {
5416
- clientErrorCode: CALL_DIAGNOSTIC_CONFIG.ICE_FAILURE_CLIENT_CODE,
5417
- }
5418
- ),
5419
- ],
5420
- },
5421
- options: {
5422
- meetingId: this.id,
5423
- },
5424
- });
5425
-
5426
- this.uploadLogs({
5427
- file: 'peer-connection-manager/index',
5428
- function: 'connectionFailed',
5429
- });
5430
-
5431
5492
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.CONNECTION_FAILURE, {
5432
5493
  correlation_id: this.correlationId,
5433
5494
  locus_id: this.locusId,
5495
+ networkStatus: this.networkStatus,
5496
+ hasMediaConnectionConnectedAtLeastOnce: this.hasMediaConnectionConnectedAtLeastOnce,
5434
5497
  });
5498
+
5499
+ if (this.hasMediaConnectionConnectedAtLeastOnce) {
5500
+ // we know the media connection failed and browser will not attempt to recover it any more
5501
+ // so reset the timer as it's not needed anymore, we want to reconnect immediately
5502
+ this.reconnectionManager.resetReconnectionTimer();
5503
+
5504
+ this.reconnect({networkDisconnect: true});
5505
+
5506
+ this.uploadLogs({
5507
+ file: 'peer-connection-manager/index',
5508
+ function: 'connectionFailed',
5509
+ });
5510
+ }
5435
5511
  };
5436
5512
 
5437
5513
  LoggerProxy.logger.info(
@@ -5443,22 +5519,32 @@ export default class Meeting extends StatelessWebexPlugin {
5443
5519
 
5444
5520
  switch (event.state) {
5445
5521
  case ConnectionState.Connecting:
5446
- // @ts-ignore
5447
- this.webex.internal.newMetrics.submitClientEvent({
5448
- name: 'client.ice.start',
5449
- options: {
5450
- meetingId: this.id,
5451
- },
5452
- });
5522
+ if (!this.hasMediaConnectionConnectedAtLeastOnce) {
5523
+ // Only send CA event for join flow if we haven't successfully connected media yet
5524
+ // @ts-ignore
5525
+ this.webex.internal.newMetrics.submitClientEvent({
5526
+ name: 'client.ice.start',
5527
+ options: {
5528
+ meetingId: this.id,
5529
+ },
5530
+ });
5531
+ }
5453
5532
  break;
5454
5533
  case ConnectionState.Connected:
5455
- // @ts-ignore
5456
- this.webex.internal.newMetrics.submitClientEvent({
5457
- name: 'client.ice.end',
5458
- options: {
5459
- meetingId: this.id,
5460
- },
5461
- });
5534
+ if (!this.hasMediaConnectionConnectedAtLeastOnce) {
5535
+ // Only send CA event for join flow if we haven't successfully connected media yet
5536
+ // @ts-ignore
5537
+ this.webex.internal.newMetrics.submitClientEvent({
5538
+ name: 'client.ice.end',
5539
+ payload: {
5540
+ canProceed: true,
5541
+ icePhase: 'JOIN_MEETING_FINAL',
5542
+ },
5543
+ options: {
5544
+ meetingId: this.id,
5545
+ },
5546
+ });
5547
+ }
5462
5548
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.CONNECTION_SUCCESS, {
5463
5549
  correlation_id: this.correlationId,
5464
5550
  locus_id: this.locusId,
@@ -5467,6 +5553,7 @@ export default class Meeting extends StatelessWebexPlugin {
5467
5553
  this.setNetworkStatus(NETWORK_STATUS.CONNECTED);
5468
5554
  this.reconnectionManager.iceReconnected();
5469
5555
  this.statsAnalyzer.startAnalyzer(this.mediaProperties.webrtcMediaConnection);
5556
+ this.hasMediaConnectionConnectedAtLeastOnce = true;
5470
5557
  break;
5471
5558
  case ConnectionState.Disconnected:
5472
5559
  this.setNetworkStatus(NETWORK_STATUS.DISCONNECTED);
@@ -5771,36 +5858,42 @@ export default class Meeting extends StatelessWebexPlugin {
5771
5858
  try {
5772
5859
  await this.mediaProperties.waitForMediaConnectionConnected();
5773
5860
  } catch (error) {
5774
- // @ts-ignore
5775
- this.webex.internal.newMetrics.submitClientEvent({
5776
- name: 'client.ice.end',
5777
- payload: {
5778
- canProceed: !this.turnServerUsed, // If we haven't done turn tls retry yet we will proceed with join attempt
5779
- icePhase: this.turnServerUsed ? 'JOIN_MEETING_FINAL' : 'JOIN_MEETING_RETRY',
5780
- errors: [
5781
- // @ts-ignore
5782
- this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode({
5783
- clientErrorCode: CallDiagnosticUtils.generateClientErrorCodeForIceFailure({
5784
- signalingState:
5785
- this.mediaProperties.webrtcMediaConnection?.multistreamConnection?.pc?.pc
5786
- ?.signalingState ||
5787
- this.mediaProperties.webrtcMediaConnection?.mediaConnection?.pc?.signalingState ||
5788
- 'unknown',
5789
- iceConnectionState:
5790
- this.mediaProperties.webrtcMediaConnection?.multistreamConnection?.pc?.pc
5791
- ?.iceConnectionState ||
5792
- this.mediaProperties.webrtcMediaConnection?.mediaConnection?.pc
5793
- ?.iceConnectionState ||
5794
- 'unknown',
5795
- turnServerUsed: this.turnServerUsed,
5796
- }),
5797
- }),
5798
- ],
5799
- },
5800
- options: {
5801
- meetingId: this.id,
5802
- },
5803
- });
5861
+ if (!this.hasMediaConnectionConnectedAtLeastOnce) {
5862
+ // Only send CA event for join flow if we haven't successfully connected media yet
5863
+ // @ts-ignore
5864
+ this.webex.internal.newMetrics.submitClientEvent({
5865
+ name: 'client.ice.end',
5866
+ payload: {
5867
+ canProceed: !this.turnServerUsed, // If we haven't done turn tls retry yet we will proceed with join attempt
5868
+ icePhase: this.turnServerUsed ? 'JOIN_MEETING_FINAL' : 'JOIN_MEETING_RETRY',
5869
+ errors: [
5870
+ // @ts-ignore
5871
+ this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode(
5872
+ {
5873
+ clientErrorCode: CallDiagnosticUtils.generateClientErrorCodeForIceFailure({
5874
+ signalingState:
5875
+ this.mediaProperties.webrtcMediaConnection?.multistreamConnection?.pc?.pc
5876
+ ?.signalingState ||
5877
+ this.mediaProperties.webrtcMediaConnection?.mediaConnection?.pc
5878
+ ?.signalingState ||
5879
+ 'unknown',
5880
+ iceConnectionState:
5881
+ this.mediaProperties.webrtcMediaConnection?.multistreamConnection?.pc?.pc
5882
+ ?.iceConnectionState ||
5883
+ this.mediaProperties.webrtcMediaConnection?.mediaConnection?.pc
5884
+ ?.iceConnectionState ||
5885
+ 'unknown',
5886
+ turnServerUsed: this.turnServerUsed,
5887
+ }),
5888
+ }
5889
+ ),
5890
+ ],
5891
+ },
5892
+ options: {
5893
+ meetingId: this.id,
5894
+ },
5895
+ });
5896
+ }
5804
5897
  throw new Error(
5805
5898
  `Timed out waiting for media connection to be connected, correlationId=${this.correlationId}`
5806
5899
  );
@@ -5924,9 +6017,24 @@ export default class Meeting extends StatelessWebexPlugin {
5924
6017
  bundlePolicy?: BundlePolicy
5925
6018
  ): Promise<void> {
5926
6019
  this.retriedWithTurnServer = true;
6020
+ const LOG_HEADER = 'Meeting:index#addMedia():retryWithForcedTurnDiscovery -->';
5927
6021
 
5928
6022
  await this.cleanUpBeforeRetryWithTurnServer();
5929
6023
 
6024
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_MEDIA_RETRY, {
6025
+ correlation_id: this.correlationId,
6026
+ state: this.state,
6027
+ meetingState: this.meetingState,
6028
+ reason: 'forcingTurnTls',
6029
+ });
6030
+
6031
+ if (this.state === MEETING_STATE.STATES.LEFT) {
6032
+ LoggerProxy.logger.info(
6033
+ `${LOG_HEADER} meeting state was LEFT after first attempt to establish media connection. Attempting to rejoin. `
6034
+ );
6035
+ await this.join({rejoin: true});
6036
+ }
6037
+
5930
6038
  await this.retryEstablishMediaConnectionWithForcedTurnDiscovery(
5931
6039
  remoteMediaManagerConfig,
5932
6040
  bundlePolicy
@@ -6130,10 +6238,11 @@ export default class Meeting extends StatelessWebexPlugin {
6130
6238
  */
6131
6239
  async addMedia(options: AddMediaOptions = {}): Promise<void> {
6132
6240
  this.retriedWithTurnServer = false;
6241
+ this.hasMediaConnectionConnectedAtLeastOnce = false;
6133
6242
  const LOG_HEADER = 'Meeting:index#addMedia -->';
6134
6243
  LoggerProxy.logger.info(`${LOG_HEADER} called with: ${JSON.stringify(options)}`);
6135
6244
 
6136
- if (this.meetingState !== FULL_STATE.ACTIVE) {
6245
+ if (options.allowMediaInLobby !== true && this.meetingState !== FULL_STATE.ACTIVE) {
6137
6246
  throw new MeetingNotActiveError();
6138
6247
  }
6139
6248
 
@@ -6807,17 +6916,13 @@ export default class Meeting extends StatelessWebexPlugin {
6807
6916
  .catch((error) => {
6808
6917
  LoggerProxy.logger.error('Meeting:index#stopWhiteboardShare --> Error ', error);
6809
6918
 
6810
- Metrics.sendBehavioralMetric(
6811
- // @ts-ignore - check if STOP_WHITEBOARD_SHARE_FAILURE exists
6812
- BEHAVIORAL_METRICS.STOP_WHITEBOARD_SHARE_FAILURE,
6813
- {
6814
- correlation_id: this.correlationId,
6815
- locus_id: this.locusUrl.split('/').pop(),
6816
- reason: error.message,
6817
- stack: error.stack,
6818
- board: {channelUrl},
6819
- }
6820
- );
6919
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_STOP_WHITEBOARD_SHARE_FAILURE, {
6920
+ correlation_id: this.correlationId,
6921
+ locus_id: this.locusUrl.split('/').pop(),
6922
+ reason: error.message,
6923
+ stack: error.stack,
6924
+ board: {channelUrl},
6925
+ });
6821
6926
 
6822
6927
  return Promise.reject(error);
6823
6928
  })
@@ -6872,6 +6977,11 @@ export default class Meeting extends StatelessWebexPlugin {
6872
6977
  .then(() => {
6873
6978
  this.screenShareFloorState = ScreenShareFloorStatus.GRANTED;
6874
6979
 
6980
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_SHARE_SUCCESS, {
6981
+ correlation_id: this.correlationId,
6982
+ locus_id: this.locusUrl.split('/').pop(),
6983
+ });
6984
+
6875
6985
  return Promise.resolve();
6876
6986
  })
6877
6987
  .catch((error) => {
@@ -7293,6 +7403,23 @@ export default class Meeting extends StatelessWebexPlugin {
7293
7403
  }
7294
7404
  };
7295
7405
 
7406
+ /**
7407
+ * Functionality for when a share video is muted or unmuted.
7408
+ * @private
7409
+ * @memberof Meeting
7410
+ * @param {boolean} muted
7411
+ * @returns {undefined}
7412
+ */
7413
+ private handleShareVideoStreamMuteStateChange = (muted: boolean) => {
7414
+ LoggerProxy.logger.log(
7415
+ `Meeting:index#handleShareVideoStreamMuteStateChange --> Share video stream mute state changed to muted ${muted}`
7416
+ );
7417
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE, {
7418
+ correlationId: this.correlationId,
7419
+ muted,
7420
+ });
7421
+ };
7422
+
7296
7423
  /**
7297
7424
  * Functionality for when a share video is ended.
7298
7425
  * @private
@@ -7659,10 +7786,12 @@ export default class Meeting extends StatelessWebexPlugin {
7659
7786
  .update({
7660
7787
  // TODO: RoapMediaConnection is not ready to use stream classes yet, so we pass the raw MediaStreamTrack for now
7661
7788
  localTracks: {
7662
- audio: this.mediaProperties.audioStream?.outputTrack || null,
7663
- video: this.mediaProperties.videoStream?.outputTrack || null,
7664
- screenShareVideo: this.mediaProperties.shareVideoStream?.outputTrack || null,
7665
- screenShareAudio: this.mediaProperties.shareAudioStream?.outputTrack || null,
7789
+ audio: this.mediaProperties.audioStream?.outputStream?.getTracks()[0] || null,
7790
+ video: this.mediaProperties.videoStream?.outputStream?.getTracks()[0] || null,
7791
+ screenShareVideo:
7792
+ this.mediaProperties.shareVideoStream?.outputStream?.getTracks()[0] || null,
7793
+ screenShareAudio:
7794
+ this.mediaProperties.shareAudioStream?.outputStream?.getTracks()[0] || null,
7666
7795
  },
7667
7796
  direction: {
7668
7797
  audio: Media.getDirection(
@@ -7851,25 +7980,34 @@ export default class Meeting extends StatelessWebexPlugin {
7851
7980
  }
7852
7981
 
7853
7982
  /**
7854
- * Gets the time left in seconds till the permission token expires
7983
+ * Gets permission token expiry information including timeLeft, expiryTime, currentTime
7855
7984
  * (from the time the function has been fired)
7856
7985
  *
7857
- * @returns {number} time left in seconds
7986
+ * @returns {object} permissionTokenExpiryInfo
7987
+ * @returns {number} permissionTokenExpiryInfo.timeLeft The time left for token to expire
7988
+ * @returns {number} permissionTokenExpiryInfo.expiryTime The expiry time of permission token from the server
7989
+ * @returns {number} permissionTokenExpiryInfo.currentTime The current time of the local machine
7858
7990
  */
7859
- public getPermissionTokenTimeLeftInSec(): number | undefined {
7991
+ public getPermissionTokenExpiryInfo() {
7860
7992
  if (!this.permissionTokenPayload) {
7861
7993
  return undefined;
7862
7994
  }
7863
7995
 
7864
- const permissionTokenExpValue = Number(this.permissionTokenPayload.exp);
7996
+ const permissionTokenExpiryFromServer = Number(this.permissionTokenPayload.exp);
7997
+ const permissionTokenIssuedTimeFromServer = Number(this.permissionTokenPayload.iat);
7998
+
7999
+ const shiftInTime = this.permissionTokenReceivedLocalTime - permissionTokenIssuedTimeFromServer;
7865
8000
 
7866
8001
  // using new Date instead of Date.now() to allow for accurate unit testing
7867
8002
  // https://github.com/sinonjs/fake-timers/issues/321
7868
- const now = new Date().getTime();
8003
+ const currentTime = new Date().getTime();
8004
+
8005
+ // adjusted time is calculated in case your machine time is wrong
8006
+ const adjustedCurrentTime = currentTime - shiftInTime;
8007
+
8008
+ const timeLeft = (permissionTokenExpiryFromServer - adjustedCurrentTime) / 1000;
7869
8009
 
7870
- // substract current time from the permissionTokenExp
7871
- // (permissionTokenExp is a epoch timestamp, not a time to live duration)
7872
- return (permissionTokenExpValue - now) / 1000;
8010
+ return {timeLeft, expiryTime: permissionTokenExpiryFromServer, currentTime};
7873
8011
  }
7874
8012
 
7875
8013
  /**
@@ -7881,9 +8019,9 @@ export default class Meeting extends StatelessWebexPlugin {
7881
8019
  * @returns {Promise<void>}
7882
8020
  */
7883
8021
  public checkAndRefreshPermissionToken(threshold: number, reason: string): Promise<void> {
7884
- const permissionTokenTimeLeft = this.getPermissionTokenTimeLeftInSec();
8022
+ const timeLeft = this.getPermissionTokenExpiryInfo()?.timeLeft;
7885
8023
 
7886
- if (permissionTokenTimeLeft !== undefined && permissionTokenTimeLeft <= threshold) {
8024
+ if (timeLeft !== undefined && timeLeft <= threshold) {
7887
8025
  return this.refreshPermissionToken(reason);
7888
8026
  }
7889
8027