@webex/plugin-meetings 3.6.0-next.9 → 3.7.0-ipv6-multi-turn-urls.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.
Files changed (159) hide show
  1. package/README.md +2 -1
  2. package/dist/breakouts/breakout.js +1 -1
  3. package/dist/breakouts/index.js +1 -1
  4. package/dist/common/errors/{webinar-registration-error.js → join-webinar-error.js} +12 -12
  5. package/dist/common/errors/join-webinar-error.js.map +1 -0
  6. package/dist/config.js +3 -1
  7. package/dist/config.js.map +1 -1
  8. package/dist/constants.js +50 -7
  9. package/dist/constants.js.map +1 -1
  10. package/dist/controls-options-manager/enums.js +1 -0
  11. package/dist/controls-options-manager/enums.js.map +1 -1
  12. package/dist/controls-options-manager/index.js +10 -3
  13. package/dist/controls-options-manager/index.js.map +1 -1
  14. package/dist/controls-options-manager/types.js.map +1 -1
  15. package/dist/controls-options-manager/util.js +12 -0
  16. package/dist/controls-options-manager/util.js.map +1 -1
  17. package/dist/index.js +7 -7
  18. package/dist/index.js.map +1 -1
  19. package/dist/interpretation/index.js +1 -1
  20. package/dist/interpretation/siLanguage.js +1 -1
  21. package/dist/locus-info/controlsUtils.js +28 -4
  22. package/dist/locus-info/controlsUtils.js.map +1 -1
  23. package/dist/locus-info/fullState.js +2 -1
  24. package/dist/locus-info/fullState.js.map +1 -1
  25. package/dist/locus-info/index.js +61 -3
  26. package/dist/locus-info/index.js.map +1 -1
  27. package/dist/media/index.js +29 -1
  28. package/dist/media/index.js.map +1 -1
  29. package/dist/meeting/in-meeting-actions.js +29 -1
  30. package/dist/meeting/in-meeting-actions.js.map +1 -1
  31. package/dist/meeting/index.js +692 -472
  32. package/dist/meeting/index.js.map +1 -1
  33. package/dist/meeting/locusMediaRequest.js +2 -6
  34. package/dist/meeting/locusMediaRequest.js.map +1 -1
  35. package/dist/meeting/request.js +21 -29
  36. package/dist/meeting/request.js.map +1 -1
  37. package/dist/meeting/util.js +95 -59
  38. package/dist/meeting/util.js.map +1 -1
  39. package/dist/meeting-info/meeting-info-v2.js +29 -17
  40. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  41. package/dist/meetings/index.js +8 -3
  42. package/dist/meetings/index.js.map +1 -1
  43. package/dist/members/index.js +3 -2
  44. package/dist/members/index.js.map +1 -1
  45. package/dist/members/util.js +13 -7
  46. package/dist/members/util.js.map +1 -1
  47. package/dist/metrics/constants.js +3 -1
  48. package/dist/metrics/constants.js.map +1 -1
  49. package/dist/multistream/remoteMedia.js +30 -15
  50. package/dist/multistream/remoteMedia.js.map +1 -1
  51. package/dist/reachability/clusterReachability.js +12 -15
  52. package/dist/reachability/clusterReachability.js.map +1 -1
  53. package/dist/reachability/index.js +433 -136
  54. package/dist/reachability/index.js.map +1 -1
  55. package/dist/reachability/reachability.types.js +7 -0
  56. package/dist/reachability/reachability.types.js.map +1 -0
  57. package/dist/reachability/request.js +23 -9
  58. package/dist/reachability/request.js.map +1 -1
  59. package/dist/recording-controller/enums.js +8 -4
  60. package/dist/recording-controller/enums.js.map +1 -1
  61. package/dist/recording-controller/index.js +18 -9
  62. package/dist/recording-controller/index.js.map +1 -1
  63. package/dist/recording-controller/util.js +13 -9
  64. package/dist/recording-controller/util.js.map +1 -1
  65. package/dist/roap/index.js +5 -7
  66. package/dist/roap/index.js.map +1 -1
  67. package/dist/roap/request.js +45 -79
  68. package/dist/roap/request.js.map +1 -1
  69. package/dist/roap/turnDiscovery.js +3 -6
  70. package/dist/roap/turnDiscovery.js.map +1 -1
  71. package/dist/types/common/errors/{webinar-registration-error.d.ts → join-webinar-error.d.ts} +2 -2
  72. package/dist/types/config.d.ts +2 -0
  73. package/dist/types/constants.d.ts +38 -1
  74. package/dist/types/controls-options-manager/enums.d.ts +2 -1
  75. package/dist/types/controls-options-manager/index.d.ts +2 -1
  76. package/dist/types/controls-options-manager/types.d.ts +2 -0
  77. package/dist/types/index.d.ts +2 -2
  78. package/dist/types/locus-info/index.d.ts +9 -0
  79. package/dist/types/meeting/in-meeting-actions.d.ts +28 -0
  80. package/dist/types/meeting/index.d.ts +34 -3
  81. package/dist/types/meeting/locusMediaRequest.d.ts +2 -3
  82. package/dist/types/meeting/request.d.ts +2 -2
  83. package/dist/types/meeting/util.d.ts +2 -2
  84. package/dist/types/meeting-info/meeting-info-v2.d.ts +4 -4
  85. package/dist/types/meetings/index.d.ts +4 -1
  86. package/dist/types/members/index.d.ts +2 -1
  87. package/dist/types/members/util.d.ts +5 -1
  88. package/dist/types/metrics/constants.d.ts +3 -1
  89. package/dist/types/reachability/clusterReachability.d.ts +1 -10
  90. package/dist/types/reachability/index.d.ts +74 -35
  91. package/dist/types/reachability/reachability.types.d.ts +64 -0
  92. package/dist/types/reachability/request.d.ts +5 -1
  93. package/dist/types/recording-controller/enums.d.ts +5 -2
  94. package/dist/types/recording-controller/index.d.ts +1 -0
  95. package/dist/types/recording-controller/util.d.ts +2 -1
  96. package/dist/types/roap/request.d.ts +1 -13
  97. package/dist/webinar/index.js +382 -19
  98. package/dist/webinar/index.js.map +1 -1
  99. package/package.json +22 -22
  100. package/src/common/errors/join-webinar-error.ts +24 -0
  101. package/src/config.ts +2 -0
  102. package/src/constants.ts +49 -3
  103. package/src/controls-options-manager/enums.ts +1 -0
  104. package/src/controls-options-manager/index.ts +19 -2
  105. package/src/controls-options-manager/types.ts +2 -0
  106. package/src/controls-options-manager/util.ts +12 -0
  107. package/src/index.ts +2 -2
  108. package/src/locus-info/controlsUtils.ts +46 -2
  109. package/src/locus-info/fullState.ts +1 -0
  110. package/src/locus-info/index.ts +60 -0
  111. package/src/media/index.ts +15 -0
  112. package/src/meeting/in-meeting-actions.ts +58 -0
  113. package/src/meeting/index.ts +232 -25
  114. package/src/meeting/locusMediaRequest.ts +4 -8
  115. package/src/meeting/request.ts +4 -11
  116. package/src/meeting/util.ts +25 -4
  117. package/src/meeting-info/meeting-info-v2.ts +23 -11
  118. package/src/meetings/index.ts +54 -41
  119. package/src/members/index.ts +4 -2
  120. package/src/members/util.ts +4 -1
  121. package/src/metrics/constants.ts +3 -1
  122. package/src/multistream/remoteMedia.ts +28 -15
  123. package/src/reachability/clusterReachability.ts +5 -15
  124. package/src/reachability/index.ts +285 -77
  125. package/src/reachability/reachability.types.ts +85 -0
  126. package/src/reachability/request.ts +55 -30
  127. package/src/recording-controller/enums.ts +5 -2
  128. package/src/recording-controller/index.ts +17 -4
  129. package/src/recording-controller/util.ts +20 -5
  130. package/src/roap/index.ts +4 -5
  131. package/src/roap/request.ts +30 -44
  132. package/src/roap/turnDiscovery.ts +2 -4
  133. package/src/webinar/index.ts +223 -17
  134. package/test/unit/spec/controls-options-manager/index.js +56 -32
  135. package/test/unit/spec/controls-options-manager/util.js +44 -0
  136. package/test/unit/spec/locus-info/controlsUtils.js +80 -4
  137. package/test/unit/spec/locus-info/index.js +59 -2
  138. package/test/unit/spec/meeting/in-meeting-actions.ts +31 -1
  139. package/test/unit/spec/meeting/index.js +369 -103
  140. package/test/unit/spec/meeting/locusMediaRequest.ts +18 -11
  141. package/test/unit/spec/meeting/request.js +3 -26
  142. package/test/unit/spec/meeting/utils.js +55 -13
  143. package/test/unit/spec/meeting-info/meetinginfov2.js +9 -4
  144. package/test/unit/spec/meetings/index.js +25 -6
  145. package/test/unit/spec/members/index.js +25 -2
  146. package/test/unit/spec/members/request.js +37 -3
  147. package/test/unit/spec/members/utils.js +110 -1
  148. package/test/unit/spec/multistream/remoteMedia.ts +11 -7
  149. package/test/unit/spec/reachability/clusterReachability.ts +7 -0
  150. package/test/unit/spec/reachability/index.ts +265 -1
  151. package/test/unit/spec/reachability/request.js +56 -15
  152. package/test/unit/spec/recording-controller/index.js +61 -5
  153. package/test/unit/spec/recording-controller/util.js +39 -3
  154. package/test/unit/spec/roap/index.ts +1 -1
  155. package/test/unit/spec/roap/request.ts +51 -109
  156. package/test/unit/spec/roap/turnDiscovery.ts +202 -147
  157. package/test/unit/spec/webinar/index.ts +443 -14
  158. package/dist/common/errors/webinar-registration-error.js.map +0 -1
  159. package/src/common/errors/webinar-registration-error.ts +0 -27
@@ -5,6 +5,7 @@ import jwtDecode from 'jwt-decode';
5
5
  import {StatelessWebexPlugin} from '@webex/webex-core';
6
6
  // @ts-ignore - Types not available for @webex/common
7
7
  import {Defer} from '@webex/common';
8
+ import {safeSetTimeout, safeSetInterval} from '@webex/common-timers';
8
9
  import {
9
10
  ClientEvent,
10
11
  ClientEventLeaveReason,
@@ -121,6 +122,8 @@ import {
121
122
  MEETING_PERMISSION_TOKEN_REFRESH_REASON,
122
123
  ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT,
123
124
  NAMED_MEDIA_GROUP_TYPE_AUDIO,
125
+ WEBINAR_ERROR_WEBCAST,
126
+ WEBINAR_ERROR_REGISTRATIONID,
124
127
  } from '../constants';
125
128
  import BEHAVIORAL_METRICS from '../metrics/constants';
126
129
  import ParameterError from '../common/errors/parameter';
@@ -128,7 +131,7 @@ import {
128
131
  MeetingInfoV2PasswordError,
129
132
  MeetingInfoV2CaptchaError,
130
133
  MeetingInfoV2PolicyError,
131
- MeetingInfoV2WebinarRegistrationError,
134
+ MeetingInfoV2JoinWebinarError,
132
135
  } from '../meeting-info/meeting-info-v2';
133
136
  import {CSI, ReceiveSlotManager} from '../multistream/receiveSlotManager';
134
137
  import SendSlotManager from '../multistream/sendSlotManager';
@@ -157,7 +160,7 @@ import ControlsOptionsManager from '../controls-options-manager';
157
160
  import PermissionError from '../common/errors/permission';
158
161
  import {LocusMediaRequest} from './locusMediaRequest';
159
162
  import {ConnectionStateHandler, ConnectionStateEvent} from './connectionStateHandler';
160
- import WebinarRegistrationError from '../common/errors/webinar-registration-error';
163
+ import JoinWebinarError from '../common/errors/join-webinar-error';
161
164
 
162
165
  // default callback so we don't call an undefined function, but in practice it should never be used
163
166
  const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
@@ -702,6 +705,8 @@ export default class Meeting extends StatelessWebexPlugin {
702
705
  private iceCandidateErrors: Map<string, number>;
703
706
  private iceCandidatesCount: number;
704
707
  private rtcMetrics?: RtcMetrics;
708
+ private uploadLogsTimer?: ReturnType<typeof setTimeout>;
709
+ private logUploadIntervalIndex: number;
705
710
 
706
711
  /**
707
712
  * @param {Object} attrs
@@ -770,6 +775,8 @@ export default class Meeting extends StatelessWebexPlugin {
770
775
  );
771
776
  this.callStateForMetrics.correlationId = this.id;
772
777
  }
778
+ this.logUploadIntervalIndex = 0;
779
+
773
780
  /**
774
781
  * @instance
775
782
  * @type {String}
@@ -1762,15 +1769,20 @@ export default class Meeting extends StatelessWebexPlugin {
1762
1769
  this.meetingInfo = err.meetingInfo;
1763
1770
  }
1764
1771
  throw new PermissionError();
1765
- } else if (err instanceof MeetingInfoV2WebinarRegistrationError) {
1772
+ } else if (err instanceof MeetingInfoV2JoinWebinarError) {
1766
1773
  this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION;
1774
+ if (WEBINAR_ERROR_WEBCAST.includes(err.wbxAppApiCode)) {
1775
+ this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NEED_JOIN_WITH_WEBCAST;
1776
+ } else if (WEBINAR_ERROR_REGISTRATIONID.includes(err.wbxAppApiCode)) {
1777
+ this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_NEED_REGISTRATIONID;
1778
+ }
1767
1779
  this.meetingInfoFailureCode = err.wbxAppApiCode;
1768
1780
 
1769
1781
  if (err.meetingInfo) {
1770
1782
  this.meetingInfo = err.meetingInfo;
1771
1783
  }
1772
1784
 
1773
- throw new WebinarRegistrationError();
1785
+ throw new JoinWebinarError();
1774
1786
  } else if (err instanceof MeetingInfoV2PasswordError) {
1775
1787
  LoggerProxy.logger.info(
1776
1788
  // @ts-ignore
@@ -2014,6 +2026,7 @@ export default class Meeting extends StatelessWebexPlugin {
2014
2026
  this.setUpLocusInfoSelfListener();
2015
2027
  this.setUpLocusInfoMeetingListener();
2016
2028
  this.setUpLocusServicesListener();
2029
+ this.setUpLocusResourcesListener();
2017
2030
  // members update listeners
2018
2031
  this.setUpLocusFullStateListener();
2019
2032
  this.setUpLocusUrlListener();
@@ -2635,6 +2648,43 @@ export default class Meeting extends StatelessWebexPlugin {
2635
2648
  );
2636
2649
  });
2637
2650
 
2651
+ this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_WEBCAST_CHANGED, ({state}) => {
2652
+ Trigger.trigger(
2653
+ this,
2654
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
2655
+ EVENT_TRIGGERS.MEETING_CONTROLS_WEBCAST_UPDATED,
2656
+ {state}
2657
+ );
2658
+ });
2659
+
2660
+ this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_MEETING_FULL_CHANGED, ({state}) => {
2661
+ Trigger.trigger(
2662
+ this,
2663
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
2664
+ EVENT_TRIGGERS.MEETING_CONTROLS_MEETING_FULL_UPDATED,
2665
+ {state}
2666
+ );
2667
+ });
2668
+
2669
+ this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_PRACTICE_SESSION_STATUS_UPDATED, ({state}) => {
2670
+ this.webinar.updatePracticeSessionStatus(state);
2671
+ Trigger.trigger(
2672
+ this,
2673
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
2674
+ EVENT_TRIGGERS.MEETING_CONTROLS_PRACTICE_SESSION_STATUS_UPDATED,
2675
+ {state}
2676
+ );
2677
+ });
2678
+
2679
+ this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_STAGE_VIEW_UPDATED, ({state}) => {
2680
+ Trigger.trigger(
2681
+ this,
2682
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
2683
+ EVENT_TRIGGERS.MEETING_CONTROLS_STAGE_VIEW_UPDATED,
2684
+ {state}
2685
+ );
2686
+ });
2687
+
2638
2688
  this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_VIDEO_CHANGED, ({state}) => {
2639
2689
  Trigger.trigger(
2640
2690
  this,
@@ -2996,10 +3046,20 @@ export default class Meeting extends StatelessWebexPlugin {
2996
3046
  this.breakouts.breakoutServiceUrlUpdate(payload?.services?.breakout?.url);
2997
3047
  this.annotation.approvalUrlUpdate(payload?.services?.approval?.url);
2998
3048
  this.simultaneousInterpretation.approvalUrlUpdate(payload?.services?.approval?.url);
2999
- this.webinar.webcastUrlUpdate(payload?.services?.webcast?.url);
3000
- this.webinar.webinarAttendeesSearchingUrlUpdate(
3001
- payload?.services?.webinarAttendeesSearching?.url
3002
- );
3049
+ });
3050
+ }
3051
+
3052
+ /**
3053
+ * Set up the locus info resources link listener
3054
+ * update the locusInfo for webcast instance url
3055
+ * @param {Object} payload - The event payload
3056
+ * @returns {undefined}
3057
+ * @private
3058
+ * @memberof Meeting
3059
+ */
3060
+ private setUpLocusResourcesListener() {
3061
+ this.locusInfo.on(LOCUSINFO.EVENTS.LINKS_RESOURCES, (payload) => {
3062
+ this.webinar.updateWebcastUrl(payload);
3003
3063
  });
3004
3064
  }
3005
3065
 
@@ -3202,6 +3262,9 @@ export default class Meeting extends StatelessWebexPlugin {
3202
3262
  options: {meetingId: this.id},
3203
3263
  });
3204
3264
  }
3265
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.GUEST_ENTERED_LOBBY, {
3266
+ correlation_id: this.correlationId,
3267
+ });
3205
3268
  this.updateLLMConnection();
3206
3269
  });
3207
3270
  this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ADMITTED_GUEST, async (payload) => {
@@ -3225,6 +3288,9 @@ export default class Meeting extends StatelessWebexPlugin {
3225
3288
  name: 'client.lobby.exited',
3226
3289
  options: {meetingId: this.id},
3227
3290
  });
3291
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.GUEST_EXITED_LOBBY, {
3292
+ correlation_id: this.correlationId,
3293
+ });
3228
3294
  }
3229
3295
  this.rtcMetrics?.sendNextMetrics();
3230
3296
  this.updateLLMConnection();
@@ -3311,7 +3377,7 @@ export default class Meeting extends StatelessWebexPlugin {
3311
3377
  this.simultaneousInterpretation.updateCanManageInterpreters(
3312
3378
  payload.newRoles?.includes(SELF_ROLES.MODERATOR)
3313
3379
  );
3314
- this.webinar.updateCanManageWebcast(payload.newRoles?.includes(SELF_ROLES.MODERATOR));
3380
+ this.webinar.updateRoleChanged(payload);
3315
3381
  Trigger.trigger(
3316
3382
  this,
3317
3383
  {
@@ -3458,6 +3524,7 @@ export default class Meeting extends StatelessWebexPlugin {
3458
3524
  emailAddress: string;
3459
3525
  email: string;
3460
3526
  phoneNumber: string;
3527
+ roles: Array<string>;
3461
3528
  },
3462
3529
  alertIfActive = true
3463
3530
  ) {
@@ -3714,6 +3781,10 @@ export default class Meeting extends StatelessWebexPlugin {
3714
3781
  this.userDisplayHints,
3715
3782
  this.selfUserPolicies
3716
3783
  ),
3784
+ isPremiseRecordingEnabled: RecordingUtil.isPremiseRecordingEnabled(
3785
+ this.userDisplayHints,
3786
+ this.selfUserPolicies
3787
+ ),
3717
3788
  canRaiseHand: MeetingUtil.canUserRaiseHand(this.userDisplayHints),
3718
3789
  canLowerAllHands: MeetingUtil.canUserLowerAllHands(this.userDisplayHints),
3719
3790
  canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(this.userDisplayHints),
@@ -3805,6 +3876,22 @@ export default class Meeting extends StatelessWebexPlugin {
3805
3876
  requiredHints: [DISPLAY_HINTS.DISABLE_VIEW_THE_PARTICIPANT_LIST],
3806
3877
  displayHints: this.userDisplayHints,
3807
3878
  }),
3879
+ canEnableViewTheParticipantsListPanelist: ControlsOptionsUtil.hasHints({
3880
+ requiredHints: [DISPLAY_HINTS.ENABLE_VIEW_THE_PARTICIPANT_LIST_PANELIST],
3881
+ displayHints: this.userDisplayHints,
3882
+ }),
3883
+ canDisableViewTheParticipantsListPanelist: ControlsOptionsUtil.hasHints({
3884
+ requiredHints: [DISPLAY_HINTS.DISABLE_VIEW_THE_PARTICIPANT_LIST_PANELIST],
3885
+ displayHints: this.userDisplayHints,
3886
+ }),
3887
+ canEnableShowAttendeeCount: ControlsOptionsUtil.hasHints({
3888
+ requiredHints: [DISPLAY_HINTS.ENABLE_SHOW_ATTENDEE_COUNT],
3889
+ displayHints: this.userDisplayHints,
3890
+ }),
3891
+ canDisableShowAttendeeCount: ControlsOptionsUtil.hasHints({
3892
+ requiredHints: [DISPLAY_HINTS.DISABLE_SHOW_ATTENDEE_COUNT],
3893
+ displayHints: this.userDisplayHints,
3894
+ }),
3808
3895
  canEnableRaiseHand: ControlsOptionsUtil.hasHints({
3809
3896
  requiredHints: [DISPLAY_HINTS.ENABLE_RAISE_HAND],
3810
3897
  displayHints: this.userDisplayHints,
@@ -3821,6 +3908,42 @@ export default class Meeting extends StatelessWebexPlugin {
3821
3908
  requiredHints: [DISPLAY_HINTS.DISABLE_VIDEO],
3822
3909
  displayHints: this.userDisplayHints,
3823
3910
  }),
3911
+ canStartWebcast: ControlsOptionsUtil.hasHints({
3912
+ requiredHints: [DISPLAY_HINTS.WEBCAST_CONTROL_START],
3913
+ displayHints: this.userDisplayHints,
3914
+ }),
3915
+ canStopWebcast: ControlsOptionsUtil.hasHints({
3916
+ requiredHints: [DISPLAY_HINTS.WEBCAST_CONTROL_STOP],
3917
+ displayHints: this.userDisplayHints,
3918
+ }),
3919
+ canShowStageView: ControlsOptionsUtil.hasHints({
3920
+ requiredHints: [DISPLAY_HINTS.STAGE_VIEW_ACTIVE],
3921
+ displayHints: this.userDisplayHints,
3922
+ }),
3923
+ canEnableStageView: ControlsOptionsUtil.hasHints({
3924
+ requiredHints: [DISPLAY_HINTS.ENABLE_STAGE_VIEW],
3925
+ displayHints: this.userDisplayHints,
3926
+ }),
3927
+ canDisableStageView: ControlsOptionsUtil.hasHints({
3928
+ requiredHints: [DISPLAY_HINTS.DISABLE_STAGE_VIEW],
3929
+ displayHints: this.userDisplayHints,
3930
+ }),
3931
+ isPracticeSessionOn: ControlsOptionsUtil.hasHints({
3932
+ requiredHints: [DISPLAY_HINTS.PRACTICE_SESSION_ON],
3933
+ displayHints: this.userDisplayHints,
3934
+ }),
3935
+ isPracticeSessionOff: ControlsOptionsUtil.hasHints({
3936
+ requiredHints: [DISPLAY_HINTS.PRACTICE_SESSION_OFF],
3937
+ displayHints: this.userDisplayHints,
3938
+ }),
3939
+ canStartPracticeSession: ControlsOptionsUtil.hasHints({
3940
+ requiredHints: [DISPLAY_HINTS.SHOW_PRACTICE_SESSION_START],
3941
+ displayHints: this.userDisplayHints,
3942
+ }),
3943
+ canStopPracticeSession: ControlsOptionsUtil.hasHints({
3944
+ requiredHints: [DISPLAY_HINTS.SHOW_PRACTICE_SESSION_STOP],
3945
+ displayHints: this.userDisplayHints,
3946
+ }),
3824
3947
  canShareFile:
3825
3948
  (ControlsOptionsUtil.hasHints({
3826
3949
  requiredHints: [DISPLAY_HINTS.SHARE_FILE],
@@ -3977,6 +4100,66 @@ export default class Meeting extends StatelessWebexPlugin {
3977
4100
  Trigger.trigger(this, options, EVENTS.REQUEST_UPLOAD_LOGS, this);
3978
4101
  }
3979
4102
 
4103
+ /**
4104
+ * sets the timer for periodic log upload
4105
+ * @returns {void}
4106
+ */
4107
+ private setLogUploadTimer() {
4108
+ // start with short timeouts and increase them later on so in case users have very long multi-hour meetings we don't get too fragmented logs
4109
+ const LOG_UPLOAD_INTERVALS = [0.1, 15, 30, 60]; // in minutes
4110
+
4111
+ const delay =
4112
+ 1000 *
4113
+ 60 *
4114
+ // @ts-ignore - config coming from registerPlugin
4115
+ this.config.logUploadIntervalMultiplicationFactor *
4116
+ LOG_UPLOAD_INTERVALS[this.logUploadIntervalIndex];
4117
+
4118
+ if (this.logUploadIntervalIndex < LOG_UPLOAD_INTERVALS.length - 1) {
4119
+ this.logUploadIntervalIndex += 1;
4120
+ }
4121
+
4122
+ this.uploadLogsTimer = safeSetTimeout(() => {
4123
+ this.uploadLogsTimer = undefined;
4124
+
4125
+ this.uploadLogs();
4126
+
4127
+ // just as an extra precaution, to avoid uploading logs forever in case something goes wrong
4128
+ // and the page remains opened, we stop it if there is no media connection
4129
+ if (!this.mediaProperties.webrtcMediaConnection) {
4130
+ return;
4131
+ }
4132
+
4133
+ this.setLogUploadTimer();
4134
+ }, delay);
4135
+ }
4136
+
4137
+ /**
4138
+ * Starts a periodic upload of logs
4139
+ *
4140
+ * @returns {undefined}
4141
+ */
4142
+ public startPeriodicLogUpload() {
4143
+ // @ts-ignore - config coming from registerPlugin
4144
+ if (this.config.logUploadIntervalMultiplicationFactor && !this.uploadLogsTimer) {
4145
+ this.logUploadIntervalIndex = 0;
4146
+
4147
+ this.setLogUploadTimer();
4148
+ }
4149
+ }
4150
+
4151
+ /**
4152
+ * Stops the periodic upload of logs
4153
+ *
4154
+ * @returns {undefined}
4155
+ */
4156
+ public stopPeriodicLogUpload() {
4157
+ if (this.uploadLogsTimer) {
4158
+ clearTimeout(this.uploadLogsTimer);
4159
+ this.uploadLogsTimer = undefined;
4160
+ }
4161
+ }
4162
+
3980
4163
  /**
3981
4164
  * Removes remote audio, video and share streams from class instance's mediaProperties
3982
4165
  * @returns {undefined}
@@ -4688,8 +4871,6 @@ export default class Meeting extends StatelessWebexPlugin {
4688
4871
  if (!joinResponse) {
4689
4872
  // This is the 1st attempt or a retry after join request failed -> we need to do a join with TURN discovery
4690
4873
 
4691
- // @ts-ignore
4692
- joinOptions.reachability = await this.webex.meetings.reachability.getReachabilityResults();
4693
4874
  const turnDiscoveryRequest = await this.roap.generateTurnDiscoveryRequestMessage(
4694
4875
  this,
4695
4876
  true
@@ -4821,6 +5002,8 @@ export default class Meeting extends StatelessWebexPlugin {
4821
5002
  );
4822
5003
  }
4823
5004
 
5005
+ this.cleanUpBeforeReconnection();
5006
+
4824
5007
  return this.reconnectionManager
4825
5008
  .reconnect(options, async () => {
4826
5009
  await this.waitForRemoteSDPAnswer();
@@ -5198,16 +5381,19 @@ export default class Meeting extends StatelessWebexPlugin {
5198
5381
  this.meetingFiniteStateMachine.reset();
5199
5382
  }
5200
5383
 
5201
- // @ts-ignore
5202
- this.webex.internal.newMetrics.submitClientEvent({
5203
- name: 'client.call.initiated',
5204
- payload: {
5205
- trigger: this.callStateForMetrics.joinTrigger || 'user-interaction',
5206
- isRoapCallEnabled: true,
5207
- pstnAudioType: options?.pstnAudioType,
5208
- },
5209
- options: {meetingId: this.id},
5210
- });
5384
+ // send client.call.initiated unless told not to
5385
+ if (options.sendCallInitiated !== false) {
5386
+ // @ts-ignore
5387
+ this.webex.internal.newMetrics.submitClientEvent({
5388
+ name: 'client.call.initiated',
5389
+ payload: {
5390
+ trigger: this.callStateForMetrics.joinTrigger || 'user-interaction',
5391
+ isRoapCallEnabled: true,
5392
+ pstnAudioType: options?.pstnAudioType,
5393
+ },
5394
+ options: {meetingId: this.id},
5395
+ });
5396
+ }
5211
5397
 
5212
5398
  LoggerProxy.logger.log('Meeting:index#join --> Joining a meeting');
5213
5399
 
@@ -6238,7 +6424,7 @@ export default class Meeting extends StatelessWebexPlugin {
6238
6424
  this.mediaProperties.webrtcMediaConnection.on(
6239
6425
  MediaConnectionEventNames.ICE_CANDIDATE,
6240
6426
  (event) => {
6241
- if (event.candidate) {
6427
+ if (event.candidate && event.candidate.candidate && event.candidate.candidate.length > 0) {
6242
6428
  this.iceCandidatesCount += 1;
6243
6429
  }
6244
6430
  }
@@ -6949,6 +7135,23 @@ export default class Meeting extends StatelessWebexPlugin {
6949
7135
  }
6950
7136
  }
6951
7137
 
7138
+ private async cleanUpBeforeReconnection(): Promise<void> {
7139
+ try {
7140
+ // when media fails, we want to upload a webrtc dump to see whats going on
7141
+ // this function is async, but returns once the stats have been gathered
7142
+ await this.forceSendStatsReport({callFrom: 'cleanUpBeforeReconnection'});
7143
+
7144
+ if (this.statsAnalyzer) {
7145
+ await this.statsAnalyzer.stopAnalyzer();
7146
+ }
7147
+ } catch (error) {
7148
+ LoggerProxy.logger.error(
7149
+ 'Meeting:index#cleanUpBeforeReconnection --> Error during cleanup: ',
7150
+ error
7151
+ );
7152
+ }
7153
+ }
7154
+
6952
7155
  /**
6953
7156
  * Creates an instance of LocusMediaRequest for this meeting - it is needed for doing any calls
6954
7157
  * to Locus /media API (these are used for sending Roap messages and updating audio/video mute status).
@@ -7040,7 +7243,7 @@ export default class Meeting extends StatelessWebexPlugin {
7040
7243
  shareAudioEnabled = true,
7041
7244
  shareVideoEnabled = true,
7042
7245
  remoteMediaManagerConfig,
7043
- bundlePolicy,
7246
+ bundlePolicy = 'max-bundle',
7044
7247
  } = options;
7045
7248
 
7046
7249
  this.allowMediaInLobby = options?.allowMediaInLobby;
@@ -7145,6 +7348,7 @@ export default class Meeting extends StatelessWebexPlugin {
7145
7348
 
7146
7349
  // We can log ReceiveSlot SSRCs only after the SDP exchange, so doing it here:
7147
7350
  this.remoteMediaManager?.logAllReceiveSlots();
7351
+ this.startPeriodicLogUpload();
7148
7352
  } catch (error) {
7149
7353
  LoggerProxy.logger.error(`${LOG_HEADER} failed to establish media connection: `, error);
7150
7354
 
@@ -7927,18 +8131,21 @@ export default class Meeting extends StatelessWebexPlugin {
7927
8131
  * @param {boolean} mutedEnabled
7928
8132
  * @param {boolean} disallowUnmuteEnabled
7929
8133
  * @param {boolean} muteOnEntryEnabled
8134
+ * @param {array} roles
7930
8135
  * @public
7931
8136
  * @memberof Meeting
7932
8137
  */
7933
8138
  public setMuteAll(
7934
8139
  mutedEnabled: boolean,
7935
8140
  disallowUnmuteEnabled: boolean,
7936
- muteOnEntryEnabled: boolean
8141
+ muteOnEntryEnabled: boolean,
8142
+ roles: Array<string>
7937
8143
  ) {
7938
8144
  return this.controlsOptionsManager.setMuteAll(
7939
8145
  mutedEnabled,
7940
8146
  disallowUnmuteEnabled,
7941
- muteOnEntryEnabled
8147
+ muteOnEntryEnabled,
8148
+ roles
7942
8149
  );
7943
8150
  }
7944
8151
 
@@ -2,8 +2,9 @@
2
2
  import {defer} from 'lodash';
3
3
  import {Defer} from '@webex/common';
4
4
  import {WebexPlugin} from '@webex/webex-core';
5
- import {MEDIA, HTTP_VERBS, ROAP, IP_VERSION} from '../constants';
5
+ import {MEDIA, HTTP_VERBS, ROAP} from '../constants';
6
6
  import LoggerProxy from '../common/logs/logger-proxy';
7
+ import {ClientMediaPreferences} from '../reachability/reachability.types';
7
8
 
8
9
  export type MediaRequestType = 'RoapMessage' | 'LocalMute';
9
10
  export type RequestResult = any;
@@ -14,9 +15,8 @@ export type RoapRequest = {
14
15
  mediaId: string;
15
16
  roapMessage: any;
16
17
  reachability: any;
18
+ clientMediaPreferences: ClientMediaPreferences;
17
19
  sequence?: any;
18
- joinCookie: any; // any, because this is opaque to the client, we pass whatever object we got from one backend component (Orpheus) to the other (Locus)
19
- ipVersion?: IP_VERSION;
20
20
  };
21
21
 
22
22
  export type LocalMuteRequest = {
@@ -202,10 +202,6 @@ export class LocusMediaRequest extends WebexPlugin {
202
202
  const body: any = {
203
203
  device: this.config.device,
204
204
  correlationId: this.config.correlationId,
205
- clientMediaPreferences: {
206
- preferTranscoding: this.config.preferTranscoding,
207
- ipver: request.type === 'RoapMessage' ? request.ipVersion : undefined,
208
- },
209
205
  };
210
206
 
211
207
  const localMedias: any = {
@@ -223,7 +219,7 @@ export class LocusMediaRequest extends WebexPlugin {
223
219
  case 'RoapMessage':
224
220
  localMedias.roapMessage = request.roapMessage;
225
221
  localMedias.reachability = request.reachability;
226
- body.clientMediaPreferences.joinCookie = request.joinCookie;
222
+ body.clientMediaPreferences = request.clientMediaPreferences;
227
223
 
228
224
  // @ts-ignore
229
225
  this.webex.internal.newMetrics.submitClientEvent({
@@ -26,11 +26,11 @@ import {
26
26
  SEND_DTMF_ENDPOINT,
27
27
  _SLIDES_,
28
28
  ANNOTATION,
29
- IP_VERSION,
30
29
  } from '../constants';
31
30
  import {SendReactionOptions, ToggleReactionsOptions} from './request.type';
32
31
  import MeetingUtil from './util';
33
32
  import {AnnotationInfo} from '../annotation/annotation.types';
33
+ import {ClientMediaPreferences} from '../reachability/reachability.types';
34
34
 
35
35
  /**
36
36
  * @class MeetingRequest
@@ -128,8 +128,8 @@ export default class MeetingRequest extends StatelessWebexPlugin {
128
128
  locale?: string;
129
129
  deviceCapabilities?: Array<string>;
130
130
  liveAnnotationSupported: boolean;
131
- ipVersion?: IP_VERSION;
132
131
  alias?: string;
132
+ clientMediaPreferences: ClientMediaPreferences;
133
133
  }) {
134
134
  const {
135
135
  asResourceOccupant,
@@ -147,12 +147,11 @@ export default class MeetingRequest extends StatelessWebexPlugin {
147
147
  moveToResource,
148
148
  roapMessage,
149
149
  reachability,
150
- preferTranscoding,
151
150
  breakoutsSupported,
152
151
  locale,
153
152
  deviceCapabilities = [],
154
153
  liveAnnotationSupported,
155
- ipVersion,
154
+ clientMediaPreferences,
156
155
  alias,
157
156
  } = options;
158
157
 
@@ -160,8 +159,6 @@ export default class MeetingRequest extends StatelessWebexPlugin {
160
159
 
161
160
  let url = '';
162
161
 
163
- const joinCookie = await this.getJoinCookie();
164
-
165
162
  const body: any = {
166
163
  asResourceOccupant,
167
164
  device: {
@@ -176,11 +173,7 @@ export default class MeetingRequest extends StatelessWebexPlugin {
176
173
  allowMultiDevice: true,
177
174
  ensureConversation: ensureConversation || false,
178
175
  supportsNativeLobby: 1,
179
- clientMediaPreferences: {
180
- preferTranscoding: preferTranscoding ?? true,
181
- joinCookie,
182
- ipver: ipVersion,
183
- },
176
+ clientMediaPreferences,
184
177
  };
185
178
 
186
179
  if (alias) {
@@ -115,7 +115,7 @@ const MeetingUtil = {
115
115
  return IP_VERSION.unknown;
116
116
  },
117
117
 
118
- joinMeeting: (meeting, options) => {
118
+ joinMeeting: async (meeting, options) => {
119
119
  if (!meeting) {
120
120
  return Promise.reject(new ParameterError('You need a meeting object.'));
121
121
  }
@@ -127,6 +127,27 @@ const MeetingUtil = {
127
127
  options: {meetingId: meeting.id},
128
128
  });
129
129
 
130
+ let reachability;
131
+ let clientMediaPreferences = {
132
+ // bare minimum fallback value that should allow us to join
133
+ ipver: IP_VERSION.unknown,
134
+ joinCookie: undefined,
135
+ preferTranscoding: !meeting.isMultistream,
136
+ };
137
+
138
+ try {
139
+ clientMediaPreferences = await webex.meetings.reachability.getClientMediaPreferences(
140
+ meeting.isMultistream,
141
+ MeetingUtil.getIpVersion(webex)
142
+ );
143
+ reachability = await webex.meetings.reachability.getReachabilityReportToAttachToRoap();
144
+ } catch (e) {
145
+ LoggerProxy.logger.error(
146
+ 'Meeting:util#joinMeeting --> Error getting reachability or clientMediaPreferences:',
147
+ e
148
+ );
149
+ }
150
+
130
151
  // eslint-disable-next-line no-warning-comments
131
152
  // TODO: check if the meeting is in JOINING state
132
153
  // if Joining state termintate the request as user might click multiple times
@@ -138,20 +159,19 @@ const MeetingUtil = {
138
159
  locusUrl: meeting.locusUrl,
139
160
  locusClusterUrl: meeting.meetingInfo?.locusClusterUrl,
140
161
  correlationId: meeting.correlationId,
141
- reachability: options.reachability,
162
+ reachability,
142
163
  roapMessage: options.roapMessage,
143
164
  permissionToken: meeting.permissionToken,
144
165
  resourceId: options.resourceId || null,
145
166
  moderator: options.moderator,
146
167
  pin: options.pin,
147
168
  moveToResource: options.moveToResource,
148
- preferTranscoding: !meeting.isMultistream,
149
169
  asResourceOccupant: options.asResourceOccupant,
150
170
  breakoutsSupported: options.breakoutsSupported,
151
171
  locale: options.locale,
152
172
  deviceCapabilities: options.deviceCapabilities,
153
173
  liveAnnotationSupported: options.liveAnnotationSupported,
154
- ipVersion: MeetingUtil.getIpVersion(meeting.getWebexObject()),
174
+ clientMediaPreferences,
155
175
  })
156
176
  .then((res) => {
157
177
  const parsed = MeetingUtil.parseLocusJoin(res);
@@ -177,6 +197,7 @@ const MeetingUtil = {
177
197
 
178
198
  cleanUp: (meeting) => {
179
199
  meeting.getWebexObject().internal.device.meetingEnded();
200
+ meeting.stopPeriodicLogUpload();
180
201
 
181
202
  meeting.breakouts.cleanUp();
182
203
  meeting.simultaneousInterpretation.cleanUp();