@webex/plugin-meetings 3.7.0-next.2 → 3.7.0-next.21

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 (87) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/common/errors/{webinar-registration-error.js → join-webinar-error.js} +12 -12
  4. package/dist/common/errors/join-webinar-error.js.map +1 -0
  5. package/dist/config.js +1 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/constants.js +29 -6
  8. package/dist/constants.js.map +1 -1
  9. package/dist/index.js +8 -15
  10. package/dist/index.js.map +1 -1
  11. package/dist/interpretation/index.js +1 -1
  12. package/dist/interpretation/siLanguage.js +1 -1
  13. package/dist/locus-info/index.js +5 -2
  14. package/dist/locus-info/index.js.map +1 -1
  15. package/dist/meeting/in-meeting-actions.js +11 -1
  16. package/dist/meeting/in-meeting-actions.js.map +1 -1
  17. package/dist/meeting/index.js +115 -150
  18. package/dist/meeting/index.js.map +1 -1
  19. package/dist/meeting/util.js +3 -8
  20. package/dist/meeting/util.js.map +1 -1
  21. package/dist/meeting-info/meeting-info-v2.js +29 -17
  22. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  23. package/dist/meetings/index.js +6 -3
  24. package/dist/meetings/index.js.map +1 -1
  25. package/dist/members/util.js +4 -2
  26. package/dist/members/util.js.map +1 -1
  27. package/dist/metrics/constants.js +3 -1
  28. package/dist/metrics/constants.js.map +1 -1
  29. package/dist/multistream/remoteMedia.js +30 -15
  30. package/dist/multistream/remoteMedia.js.map +1 -1
  31. package/dist/reachability/clusterReachability.js +12 -11
  32. package/dist/reachability/clusterReachability.js.map +1 -1
  33. package/dist/recording-controller/enums.js +8 -4
  34. package/dist/recording-controller/enums.js.map +1 -1
  35. package/dist/recording-controller/index.js +18 -9
  36. package/dist/recording-controller/index.js.map +1 -1
  37. package/dist/recording-controller/util.js +13 -9
  38. package/dist/recording-controller/util.js.map +1 -1
  39. package/dist/types/common/errors/{webinar-registration-error.d.ts → join-webinar-error.d.ts} +2 -2
  40. package/dist/types/constants.d.ts +21 -1
  41. package/dist/types/index.d.ts +3 -3
  42. package/dist/types/locus-info/index.d.ts +2 -1
  43. package/dist/types/meeting/in-meeting-actions.d.ts +10 -0
  44. package/dist/types/meeting/index.d.ts +1 -10
  45. package/dist/types/meeting/util.d.ts +1 -1
  46. package/dist/types/meeting-info/meeting-info-v2.d.ts +4 -4
  47. package/dist/types/meetings/index.d.ts +3 -0
  48. package/dist/types/members/util.d.ts +2 -0
  49. package/dist/types/metrics/constants.d.ts +3 -1
  50. package/dist/types/recording-controller/enums.d.ts +5 -2
  51. package/dist/types/recording-controller/index.d.ts +1 -0
  52. package/dist/types/recording-controller/util.d.ts +2 -1
  53. package/dist/webinar/index.js +390 -7
  54. package/dist/webinar/index.js.map +1 -1
  55. package/package.json +22 -22
  56. package/src/common/errors/join-webinar-error.ts +24 -0
  57. package/src/config.ts +1 -1
  58. package/src/constants.ts +26 -3
  59. package/src/index.ts +2 -3
  60. package/src/locus-info/index.ts +4 -2
  61. package/src/meeting/in-meeting-actions.ts +21 -0
  62. package/src/meeting/index.ts +86 -54
  63. package/src/meeting/util.ts +3 -9
  64. package/src/meeting-info/meeting-info-v2.ts +23 -11
  65. package/src/meetings/index.ts +8 -2
  66. package/src/members/util.ts +1 -0
  67. package/src/metrics/constants.ts +3 -1
  68. package/src/multistream/remoteMedia.ts +28 -15
  69. package/src/reachability/clusterReachability.ts +4 -1
  70. package/src/recording-controller/enums.ts +5 -2
  71. package/src/recording-controller/index.ts +17 -4
  72. package/src/recording-controller/util.ts +20 -5
  73. package/src/webinar/index.ts +235 -9
  74. package/test/unit/spec/locus-info/index.js +129 -0
  75. package/test/unit/spec/meeting/in-meeting-actions.ts +13 -1
  76. package/test/unit/spec/meeting/index.js +179 -81
  77. package/test/unit/spec/meeting/utils.js +11 -19
  78. package/test/unit/spec/meeting-info/meetinginfov2.js +9 -4
  79. package/test/unit/spec/meetings/index.js +9 -5
  80. package/test/unit/spec/members/utils.js +95 -0
  81. package/test/unit/spec/multistream/remoteMedia.ts +11 -7
  82. package/test/unit/spec/reachability/clusterReachability.ts +7 -0
  83. package/test/unit/spec/recording-controller/index.js +61 -5
  84. package/test/unit/spec/recording-controller/util.js +39 -3
  85. package/test/unit/spec/webinar/index.ts +504 -0
  86. package/dist/common/errors/webinar-registration-error.js.map +0 -1
  87. package/src/common/errors/webinar-registration-error.ts +0 -27
package/src/constants.ts CHANGED
@@ -198,6 +198,8 @@ export const RETRY_TIMEOUT = 3000;
198
198
 
199
199
  export const ICE_AND_DTLS_CONNECTION_TIMEOUT = 20000;
200
200
  export const ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT = 35000;
201
+ export const WEBINAR_ERROR_WEBCAST = [403026];
202
+ export const WEBINAR_ERROR_REGISTRATIONID = [403037, 403137];
201
203
 
202
204
  // ******************** REGEX **********************
203
205
  // Please alphabetize
@@ -325,6 +327,7 @@ export const EVENT_TRIGGERS = {
325
327
  MEETING_RECONNECTION_FAILURE: 'meeting:reconnectionFailure',
326
328
  MEETING_UNLOCKED: 'meeting:unlocked',
327
329
  MEETING_LOCKED: 'meeting:locked',
330
+ MEETING_RESOURCE_LINKS_UPDATE: 'meeting:resourceLinks:update',
328
331
  MEETING_INFO_AVAILABLE: 'meeting:meetingInfoAvailable',
329
332
  MEETING_INFO_UPDATED: 'meeting:meetingInfoUpdated',
330
333
  MEETING_LOG_UPLOAD_SUCCESS: 'meeting:logUpload:success',
@@ -383,6 +386,13 @@ export const EVENT_TYPES = {
383
386
  ERROR: 'error',
384
387
  };
385
388
 
389
+ export const HEADERS = {
390
+ CONTENT_TYPE: 'Content-Type',
391
+ CONTENT_TYPE_VALUE: {
392
+ APPLICATION_JSON: 'application/json',
393
+ },
394
+ };
395
+
386
396
  // Handles the reason when meeting gets destroyed
387
397
  // host removed you from the meeting
388
398
  // You are the host and you left the meeting
@@ -529,9 +539,9 @@ export const ERROR_DICTIONARY = {
529
539
  'Reconnection was not started, because there is one already in progress or reconnections are disabled in config.',
530
540
  CODE: 15,
531
541
  },
532
- WebinarRegistrationError: {
533
- NAME: 'WebinarRegistrationError',
534
- MESSAGE: 'An error occurred while the webinar required registration.',
542
+ JoinWebinarError: {
543
+ NAME: 'JoinWebinarError',
544
+ MESSAGE: 'An error occurred while the join webinar.',
535
545
  CODE: 16,
536
546
  },
537
547
  };
@@ -904,6 +914,10 @@ export const DISPLAY_HINTS = {
904
914
  RECORDING_CONTROL_PAUSE: 'RECORDING_CONTROL_PAUSE',
905
915
  RECORDING_CONTROL_STOP: 'RECORDING_CONTROL_STOP',
906
916
  RECORDING_CONTROL_RESUME: 'RECORDING_CONTROL_RESUME',
917
+ PREMISE_RECORDING_CONTROL_START: 'PREMISE_RECORDING_CONTROL_START',
918
+ PREMISE_RECORDING_CONTROL_PAUSE: 'PREMISE_RECORDING_CONTROL_PAUSE',
919
+ PREMISE_RECORDING_CONTROL_STOP: 'PREMISE_RECORDING_CONTROL_STOP',
920
+ PREMISE_RECORDING_CONTROL_RESUME: 'PREMISE_RECORDING_CONTROL_RESUME',
907
921
  LOCK_CONTROL_UNLOCK: 'LOCK_CONTROL_UNLOCK',
908
922
  LOCK_CONTROL_LOCK: 'LOCK_CONTROL_LOCK',
909
923
  LOCK_STATUS_LOCKED: 'LOCK_STATUS_LOCKED',
@@ -950,6 +964,7 @@ export const DISPLAY_HINTS = {
950
964
  DISABLE_ASK_FOR_HELP: 'DISABLE_ASK_FOR_HELP',
951
965
  DISABLE_BREAKOUT_PREASSIGNMENTS: 'DISABLE_BREAKOUT_PREASSIGNMENTS',
952
966
  DISABLE_LOBBY_TO_BREAKOUT: 'DISABLE_LOBBY_TO_BREAKOUT',
967
+ DISABLE_BREAKOUT_START: 'DISABLE_BREAKOUT_START',
953
968
 
954
969
  // participants list
955
970
  DISABLE_VIEW_THE_PARTICIPANT_LIST: 'DISABLE_VIEW_THE_PARTICIPANT_LIST',
@@ -988,6 +1003,12 @@ export const DISPLAY_HINTS = {
988
1003
  STAGE_VIEW_INACTIVE: 'STAGE_VIEW_INACTIVE',
989
1004
  ENABLE_STAGE_VIEW: 'ENABLE_STAGE_VIEW',
990
1005
  DISABLE_STAGE_VIEW: 'DISABLE_STAGE_VIEW',
1006
+
1007
+ // Practice Session
1008
+ PRACTICE_SESSION_ON: 'PRACTICE_SESSION_ON',
1009
+ PRACTICE_SESSION_OFF: 'PRACTICE_SESSION_OFF',
1010
+ SHOW_PRACTICE_SESSION_START: 'SHOW_PRACTICE_SESSION_START',
1011
+ SHOW_PRACTICE_SESSION_STOP: 'SHOW_PRACTICE_SESSION_STOP',
991
1012
  };
992
1013
 
993
1014
  export const INTERSTITIAL_DISPLAY_HINTS = [DISPLAY_HINTS.VOIP_IS_ENABLED];
@@ -1300,6 +1321,8 @@ export const MEETING_INFO_FAILURE_REASON = {
1300
1321
  WRONG_CAPTCHA: 'WRONG_CAPTCHA', // wbxappapi requires a captcha code or a wrong captcha code was provided
1301
1322
  POLICY: 'POLICY', // meeting info request violates some meeting policy
1302
1323
  WEBINAR_REGISTRATION: 'WEBINAR_REGISTRATION', // webinar need registration
1324
+ NEED_JOIN_WITH_WEBCAST: 'NEED_JOIN_WITH_WEBCAST', // webinar need using webcast join
1325
+ WEBINAR_NEED_REGISTRATIONID: 'WEBINAR_NEED_REGISTRATIONID', // webinar need registrationID
1303
1326
  OTHER: 'OTHER', // any other error (network, etc)
1304
1327
  };
1305
1328
 
package/src/index.ts CHANGED
@@ -8,7 +8,7 @@ import CaptchaError from './common/errors/captcha-error';
8
8
  import IntentToJoinError from './common/errors/intent-to-join';
9
9
  import PasswordError from './common/errors/password-error';
10
10
  import PermissionError from './common/errors/permission';
11
- import WebinarRegistrationError from './common/errors/webinar-registration-error';
11
+ import JoinWebinarError from './common/errors/join-webinar-error';
12
12
  import {
13
13
  ReclaimHostEmptyWrongKeyError,
14
14
  ReclaimHostIsHostAlreadyError,
@@ -27,7 +27,6 @@ registerPlugin('meetings', Meetings, {
27
27
  });
28
28
 
29
29
  export {
30
- getDevices,
31
30
  LocalStream,
32
31
  LocalDisplayStream,
33
32
  LocalSystemAudioStream,
@@ -69,7 +68,7 @@ export {
69
68
  ReclaimHostEmptyWrongKeyError,
70
69
  Meeting,
71
70
  MeetingInfoUtil,
72
- WebinarRegistrationError,
71
+ JoinWebinarError,
73
72
  };
74
73
 
75
74
  export {RemoteMedia} from './multistream/remoteMedia';
@@ -1283,12 +1283,13 @@ export default class LocusInfo extends EventsScope {
1283
1283
  /**
1284
1284
  * handles when the locus.mediaShares is updated
1285
1285
  * @param {Object} mediaShares the locus.mediaShares property
1286
+ * @param {boolean} forceUpdate force to update the mediaShares
1286
1287
  * @returns {undefined}
1287
1288
  * @memberof LocusInfo
1288
1289
  * emits internal event locus_info_update_media_shares
1289
1290
  */
1290
- updateMediaShares(mediaShares: object) {
1291
- if (mediaShares && !isEqual(this.mediaShares, mediaShares)) {
1291
+ updateMediaShares(mediaShares: object, forceUpdate = false) {
1292
+ if (mediaShares && (!isEqual(this.mediaShares, mediaShares) || forceUpdate)) {
1292
1293
  const parsedMediaShares = MediaSharesUtils.getMediaShares(this.mediaShares, mediaShares);
1293
1294
 
1294
1295
  this.updateMeeting(parsedMediaShares.current);
@@ -1303,6 +1304,7 @@ export default class LocusInfo extends EventsScope {
1303
1304
  {
1304
1305
  current: parsedMediaShares.current,
1305
1306
  previous: parsedMediaShares.previous,
1307
+ forceUpdate,
1306
1308
  }
1307
1309
  );
1308
1310
  }
@@ -26,6 +26,7 @@ interface IInMeetingActions {
26
26
  canStartRecording?: boolean;
27
27
  canPauseRecording?: boolean;
28
28
  canResumeRecording?: boolean;
29
+ isPremiseRecordingEnabled?: boolean;
29
30
  canStopRecording?: boolean;
30
31
  canRaiseHand?: boolean;
31
32
  canLowerAllHands?: boolean;
@@ -93,6 +94,10 @@ interface IInMeetingActions {
93
94
  canShowStageView?: boolean;
94
95
  canEnableStageView?: boolean;
95
96
  canDisableStageView?: boolean;
97
+ isPracticeSessionOn?: boolean;
98
+ isPracticeSessionOff?: boolean;
99
+ canStartPracticeSession?: boolean;
100
+ canStopPracticeSession?: boolean;
96
101
  }
97
102
 
98
103
  /**
@@ -117,6 +122,8 @@ export default class InMeetingActions implements IInMeetingActions {
117
122
 
118
123
  canResumeRecording = null;
119
124
 
125
+ isPremiseRecordingEnabled = null;
126
+
120
127
  canStopRecording = null;
121
128
 
122
129
  canSetMuteOnEntry = null;
@@ -266,6 +273,15 @@ export default class InMeetingActions implements IInMeetingActions {
266
273
  canEnableStageView = null;
267
274
 
268
275
  canDisableStageView = null;
276
+
277
+ isPracticeSessionOn = null;
278
+
279
+ isPracticeSessionOff = null;
280
+
281
+ canStartPracticeSession = null;
282
+
283
+ canStopPracticeSession = null;
284
+
269
285
  /**
270
286
  * Returns all meeting action options
271
287
  * @returns {Object}
@@ -288,6 +304,7 @@ export default class InMeetingActions implements IInMeetingActions {
288
304
  canPauseRecording: this.canPauseRecording,
289
305
  canResumeRecording: this.canResumeRecording,
290
306
  canStopRecording: this.canStopRecording,
307
+ isPremiseRecordingEnabled: this.isPremiseRecordingEnabled,
291
308
  canRaiseHand: this.canRaiseHand,
292
309
  canLowerAllHands: this.canLowerAllHands,
293
310
  canLowerSomeoneElsesHand: this.canLowerSomeoneElsesHand,
@@ -354,6 +371,10 @@ export default class InMeetingActions implements IInMeetingActions {
354
371
  canShowStageView: this.canShowStageView,
355
372
  canEnableStageView: this.canEnableStageView,
356
373
  canDisableStageView: this.canDisableStageView,
374
+ isPracticeSessionOn: this.isPracticeSessionOn,
375
+ isPracticeSessionOff: this.isPracticeSessionOff,
376
+ canStartPracticeSession: this.canStartPracticeSession,
377
+ canStopPracticeSession: this.canStopPracticeSession,
357
378
  });
358
379
 
359
380
  /**
@@ -31,7 +31,6 @@ import {
31
31
  } from '@webex/internal-media-core';
32
32
 
33
33
  import {
34
- getDevices,
35
34
  LocalStream,
36
35
  LocalCameraStream,
37
36
  LocalDisplayStream,
@@ -122,6 +121,8 @@ import {
122
121
  MEETING_PERMISSION_TOKEN_REFRESH_REASON,
123
122
  ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT,
124
123
  NAMED_MEDIA_GROUP_TYPE_AUDIO,
124
+ WEBINAR_ERROR_WEBCAST,
125
+ WEBINAR_ERROR_REGISTRATIONID,
125
126
  } from '../constants';
126
127
  import BEHAVIORAL_METRICS from '../metrics/constants';
127
128
  import ParameterError from '../common/errors/parameter';
@@ -129,7 +130,7 @@ import {
129
130
  MeetingInfoV2PasswordError,
130
131
  MeetingInfoV2CaptchaError,
131
132
  MeetingInfoV2PolicyError,
132
- MeetingInfoV2WebinarRegistrationError,
133
+ MeetingInfoV2JoinWebinarError,
133
134
  } from '../meeting-info/meeting-info-v2';
134
135
  import {CSI, ReceiveSlotManager} from '../multistream/receiveSlotManager';
135
136
  import SendSlotManager from '../multistream/sendSlotManager';
@@ -158,7 +159,7 @@ import ControlsOptionsManager from '../controls-options-manager';
158
159
  import PermissionError from '../common/errors/permission';
159
160
  import {LocusMediaRequest} from './locusMediaRequest';
160
161
  import {ConnectionStateHandler, ConnectionStateEvent} from './connectionStateHandler';
161
- import WebinarRegistrationError from '../common/errors/webinar-registration-error';
162
+ import JoinWebinarError from '../common/errors/join-webinar-error';
162
163
 
163
164
  // default callback so we don't call an undefined function, but in practice it should never be used
164
165
  const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
@@ -848,7 +849,7 @@ export default class Meeting extends StatelessWebexPlugin {
848
849
  * @memberof Meeting
849
850
  */
850
851
  // @ts-ignore
851
- this.webinar = new Webinar({}, {parent: this.webex});
852
+ this.webinar = new Webinar({meetingId: this.id}, {parent: this.webex});
852
853
  /**
853
854
  * helper class for managing receive slots (for multistream media connections)
854
855
  */
@@ -1767,15 +1768,20 @@ export default class Meeting extends StatelessWebexPlugin {
1767
1768
  this.meetingInfo = err.meetingInfo;
1768
1769
  }
1769
1770
  throw new PermissionError();
1770
- } else if (err instanceof MeetingInfoV2WebinarRegistrationError) {
1771
+ } else if (err instanceof MeetingInfoV2JoinWebinarError) {
1771
1772
  this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION;
1773
+ if (WEBINAR_ERROR_WEBCAST.includes(err.wbxAppApiCode)) {
1774
+ this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NEED_JOIN_WITH_WEBCAST;
1775
+ } else if (WEBINAR_ERROR_REGISTRATIONID.includes(err.wbxAppApiCode)) {
1776
+ this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_NEED_REGISTRATIONID;
1777
+ }
1772
1778
  this.meetingInfoFailureCode = err.wbxAppApiCode;
1773
1779
 
1774
1780
  if (err.meetingInfo) {
1775
1781
  this.meetingInfo = err.meetingInfo;
1776
1782
  }
1777
1783
 
1778
- throw new WebinarRegistrationError();
1784
+ throw new JoinWebinarError();
1779
1785
  } else if (err instanceof MeetingInfoV2PasswordError) {
1780
1786
  LoggerProxy.logger.info(
1781
1787
  // @ts-ignore
@@ -2660,6 +2666,7 @@ export default class Meeting extends StatelessWebexPlugin {
2660
2666
  });
2661
2667
 
2662
2668
  this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_PRACTICE_SESSION_STATUS_UPDATED, ({state}) => {
2669
+ this.webinar.updatePracticeSessionStatus(state);
2663
2670
  Trigger.trigger(
2664
2671
  this,
2665
2672
  {file: 'meeting/index', function: 'setupLocusControlsListener'},
@@ -2733,6 +2740,7 @@ export default class Meeting extends StatelessWebexPlugin {
2733
2740
  this.triggerAnnotationInfoEvent(contentShare, previousContentShare);
2734
2741
 
2735
2742
  if (
2743
+ !payload.forceUpdate &&
2736
2744
  contentShare.beneficiaryId === previousContentShare?.beneficiaryId &&
2737
2745
  contentShare.disposition === previousContentShare?.disposition &&
2738
2746
  contentShare.deviceUrlSharing === previousContentShare.deviceUrlSharing &&
@@ -2779,7 +2787,11 @@ export default class Meeting extends StatelessWebexPlugin {
2779
2787
  // It does not matter who requested to share the whiteboard, everyone gets the same view
2780
2788
  else if (whiteboardShare.disposition === FLOOR_ACTION.GRANTED) {
2781
2789
  // WHITEBOARD - sharing whiteboard
2782
- newShareStatus = SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
2790
+ // Webinar attendee should receive whiteboard as remote share
2791
+ newShareStatus =
2792
+ this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee
2793
+ ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
2794
+ : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
2783
2795
  }
2784
2796
  // or if content share is either released or null and whiteboard share is either released or null, no one is sharing
2785
2797
  else if (
@@ -2794,6 +2806,7 @@ export default class Meeting extends StatelessWebexPlugin {
2794
2806
  LoggerProxy.logger.info(
2795
2807
  `Meeting:index#setUpLocusInfoMediaInactiveListener --> this.shareStatus=${this.shareStatus} newShareStatus=${newShareStatus}`
2796
2808
  );
2809
+
2797
2810
  if (newShareStatus !== this.shareStatus) {
2798
2811
  const oldShareStatus = this.shareStatus;
2799
2812
 
@@ -3051,7 +3064,20 @@ export default class Meeting extends StatelessWebexPlugin {
3051
3064
  */
3052
3065
  private setUpLocusResourcesListener() {
3053
3066
  this.locusInfo.on(LOCUSINFO.EVENTS.LINKS_RESOURCES, (payload) => {
3054
- this.webinar.updateWebcastUrl(payload);
3067
+ if (payload) {
3068
+ this.webinar.updateWebcastUrl(payload);
3069
+ Trigger.trigger(
3070
+ this,
3071
+ {
3072
+ file: 'meeting/index',
3073
+ function: 'setUpLocusInfoMeetingInfoListener',
3074
+ },
3075
+ EVENT_TRIGGERS.MEETING_RESOURCE_LINKS_UPDATE,
3076
+ {
3077
+ payload,
3078
+ }
3079
+ );
3080
+ }
3055
3081
  });
3056
3082
  }
3057
3083
 
@@ -3254,6 +3280,9 @@ export default class Meeting extends StatelessWebexPlugin {
3254
3280
  options: {meetingId: this.id},
3255
3281
  });
3256
3282
  }
3283
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.GUEST_ENTERED_LOBBY, {
3284
+ correlation_id: this.correlationId,
3285
+ });
3257
3286
  this.updateLLMConnection();
3258
3287
  });
3259
3288
  this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ADMITTED_GUEST, async (payload) => {
@@ -3277,6 +3306,9 @@ export default class Meeting extends StatelessWebexPlugin {
3277
3306
  name: 'client.lobby.exited',
3278
3307
  options: {meetingId: this.id},
3279
3308
  });
3309
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.GUEST_EXITED_LOBBY, {
3310
+ correlation_id: this.correlationId,
3311
+ });
3280
3312
  }
3281
3313
  this.rtcMetrics?.sendNextMetrics();
3282
3314
  this.updateLLMConnection();
@@ -3364,6 +3396,7 @@ export default class Meeting extends StatelessWebexPlugin {
3364
3396
  payload.newRoles?.includes(SELF_ROLES.MODERATOR)
3365
3397
  );
3366
3398
  this.webinar.updateRoleChanged(payload);
3399
+
3367
3400
  Trigger.trigger(
3368
3401
  this,
3369
3402
  {
@@ -3510,6 +3543,7 @@ export default class Meeting extends StatelessWebexPlugin {
3510
3543
  emailAddress: string;
3511
3544
  email: string;
3512
3545
  phoneNumber: string;
3546
+ roles: Array<string>;
3513
3547
  },
3514
3548
  alertIfActive = true
3515
3549
  ) {
@@ -3766,6 +3800,10 @@ export default class Meeting extends StatelessWebexPlugin {
3766
3800
  this.userDisplayHints,
3767
3801
  this.selfUserPolicies
3768
3802
  ),
3803
+ isPremiseRecordingEnabled: RecordingUtil.isPremiseRecordingEnabled(
3804
+ this.userDisplayHints,
3805
+ this.selfUserPolicies
3806
+ ),
3769
3807
  canRaiseHand: MeetingUtil.canUserRaiseHand(this.userDisplayHints),
3770
3808
  canLowerAllHands: MeetingUtil.canUserLowerAllHands(this.userDisplayHints),
3771
3809
  canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(this.userDisplayHints),
@@ -3909,6 +3947,22 @@ export default class Meeting extends StatelessWebexPlugin {
3909
3947
  requiredHints: [DISPLAY_HINTS.DISABLE_STAGE_VIEW],
3910
3948
  displayHints: this.userDisplayHints,
3911
3949
  }),
3950
+ isPracticeSessionOn: ControlsOptionsUtil.hasHints({
3951
+ requiredHints: [DISPLAY_HINTS.PRACTICE_SESSION_ON],
3952
+ displayHints: this.userDisplayHints,
3953
+ }),
3954
+ isPracticeSessionOff: ControlsOptionsUtil.hasHints({
3955
+ requiredHints: [DISPLAY_HINTS.PRACTICE_SESSION_OFF],
3956
+ displayHints: this.userDisplayHints,
3957
+ }),
3958
+ canStartPracticeSession: ControlsOptionsUtil.hasHints({
3959
+ requiredHints: [DISPLAY_HINTS.SHOW_PRACTICE_SESSION_START],
3960
+ displayHints: this.userDisplayHints,
3961
+ }),
3962
+ canStopPracticeSession: ControlsOptionsUtil.hasHints({
3963
+ requiredHints: [DISPLAY_HINTS.SHOW_PRACTICE_SESSION_STOP],
3964
+ displayHints: this.userDisplayHints,
3965
+ }),
3912
3966
  canShareFile:
3913
3967
  (ControlsOptionsUtil.hasHints({
3914
3968
  requiredHints: [DISPLAY_HINTS.SHARE_FILE],
@@ -4071,10 +4125,11 @@ export default class Meeting extends StatelessWebexPlugin {
4071
4125
  */
4072
4126
  private setLogUploadTimer() {
4073
4127
  // 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
4074
- const LOG_UPLOAD_INTERVALS = [0.1, 1, 15, 15, 30, 30, 30, 60];
4128
+ const LOG_UPLOAD_INTERVALS = [0.1, 15, 30, 60]; // in minutes
4075
4129
 
4076
4130
  const delay =
4077
4131
  1000 *
4132
+ 60 *
4078
4133
  // @ts-ignore - config coming from registerPlugin
4079
4134
  this.config.logUploadIntervalMultiplicationFactor *
4080
4135
  LOG_UPLOAD_INTERVALS[this.logUploadIntervalIndex];
@@ -5345,16 +5400,19 @@ export default class Meeting extends StatelessWebexPlugin {
5345
5400
  this.meetingFiniteStateMachine.reset();
5346
5401
  }
5347
5402
 
5348
- // @ts-ignore
5349
- this.webex.internal.newMetrics.submitClientEvent({
5350
- name: 'client.call.initiated',
5351
- payload: {
5352
- trigger: this.callStateForMetrics.joinTrigger || 'user-interaction',
5353
- isRoapCallEnabled: true,
5354
- pstnAudioType: options?.pstnAudioType,
5355
- },
5356
- options: {meetingId: this.id},
5357
- });
5403
+ // send client.call.initiated unless told not to
5404
+ if (options.sendCallInitiated !== false) {
5405
+ // @ts-ignore
5406
+ this.webex.internal.newMetrics.submitClientEvent({
5407
+ name: 'client.call.initiated',
5408
+ payload: {
5409
+ trigger: this.callStateForMetrics.joinTrigger || 'user-interaction',
5410
+ isRoapCallEnabled: true,
5411
+ pstnAudioType: options?.pstnAudioType,
5412
+ },
5413
+ options: {meetingId: this.id},
5414
+ });
5415
+ }
5358
5416
 
5359
5417
  LoggerProxy.logger.log('Meeting:index#join --> Joining a meeting');
5360
5418
 
@@ -5542,17 +5600,23 @@ export default class Meeting extends StatelessWebexPlugin {
5542
5600
  */
5543
5601
  async updateLLMConnection() {
5544
5602
  // @ts-ignore - Fix type
5545
- const {url, info: {datachannelUrl} = {}} = this.locusInfo;
5603
+ const {url, info: {datachannelUrl, practiceSessionDatachannelUrl} = {}} = this.locusInfo;
5546
5604
 
5547
5605
  const isJoined = this.isJoined();
5548
5606
 
5607
+ // webinar panelist should use new data channel in practice session
5608
+ const dataChannelUrl =
5609
+ this.webinar.isJoinPracticeSessionDataChannel() && practiceSessionDatachannelUrl
5610
+ ? practiceSessionDatachannelUrl
5611
+ : datachannelUrl;
5612
+
5549
5613
  // @ts-ignore - Fix type
5550
5614
  if (this.webex.internal.llm.isConnected()) {
5551
5615
  if (
5552
5616
  // @ts-ignore - Fix type
5553
5617
  url === this.webex.internal.llm.getLocusUrl() &&
5554
5618
  // @ts-ignore - Fix type
5555
- datachannelUrl === this.webex.internal.llm.getDatachannelUrl() &&
5619
+ dataChannelUrl === this.webex.internal.llm.getDatachannelUrl() &&
5556
5620
  isJoined
5557
5621
  ) {
5558
5622
  return undefined;
@@ -5569,7 +5633,7 @@ export default class Meeting extends StatelessWebexPlugin {
5569
5633
 
5570
5634
  // @ts-ignore - Fix type
5571
5635
  return this.webex.internal.llm
5572
- .registerAndConnect(url, datachannelUrl)
5636
+ .registerAndConnect(url, dataChannelUrl)
5573
5637
  .then((registerAndConnectResult) => {
5574
5638
  // @ts-ignore - Fix type
5575
5639
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
@@ -6739,32 +6803,6 @@ export default class Meeting extends StatelessWebexPlugin {
6739
6803
  }
6740
6804
  }
6741
6805
 
6742
- /**
6743
- * Handles device logging
6744
- *
6745
- * @private
6746
- * @static
6747
- * @param {boolean} isAudioEnabled
6748
- * @param {boolean} isVideoEnabled
6749
- * @returns {Promise<void>}
6750
- */
6751
-
6752
- private static async handleDeviceLogging(isAudioEnabled, isVideoEnabled): Promise<void> {
6753
- try {
6754
- let devices = [];
6755
- if (isVideoEnabled && isAudioEnabled) {
6756
- devices = await getDevices();
6757
- } else if (isVideoEnabled) {
6758
- devices = await getDevices(Media.DeviceKind.VIDEO_INPUT);
6759
- } else if (isAudioEnabled) {
6760
- devices = await getDevices(Media.DeviceKind.AUDIO_INPUT);
6761
- }
6762
- MeetingUtil.handleDeviceLogging(devices);
6763
- } catch {
6764
- // getDevices may fail if we don't have browser permissions, that's ok, we still can have a media connection
6765
- }
6766
- }
6767
-
6768
6806
  /**
6769
6807
  * Returns a promise. This promise is created once the local sdp offer has been successfully created and is resolved
6770
6808
  * once the remote sdp answer has been received.
@@ -7267,12 +7305,6 @@ export default class Meeting extends StatelessWebexPlugin {
7267
7305
  turnServerInfo
7268
7306
  );
7269
7307
 
7270
- if (audioEnabled || videoEnabled) {
7271
- await Meeting.handleDeviceLogging(audioEnabled, videoEnabled);
7272
- } else {
7273
- LoggerProxy.logger.info(`${LOG_HEADER} device logging not required`);
7274
- }
7275
-
7276
7308
  if (this.mediaProperties.hasLocalShareStream()) {
7277
7309
  await this.enqueueScreenShareFloorRequest();
7278
7310
  }
@@ -441,6 +441,9 @@ const MeetingUtil = {
441
441
  displayHints.includes(DISPLAY_HINTS.LEAVE_END_MEETING),
442
442
 
443
443
  canManageBreakout: (displayHints) => displayHints.includes(DISPLAY_HINTS.BREAKOUT_MANAGEMENT),
444
+
445
+ canStartBreakout: (displayHints) => !displayHints.includes(DISPLAY_HINTS.DISABLE_BREAKOUT_START),
446
+
444
447
  canBroadcastMessageToBreakout: (displayHints, policies = {}) =>
445
448
  displayHints.includes(DISPLAY_HINTS.BROADCAST_MESSAGE_TO_BREAKOUT) &&
446
449
  !!policies[SELF_POLICY.SUPPORT_BROADCAST_MESSAGE],
@@ -496,15 +499,6 @@ const MeetingUtil = {
496
499
  }
497
500
  },
498
501
 
499
- handleDeviceLogging: (devices = []) => {
500
- const LOG_HEADER = 'MeetingUtil#handleDeviceLogging -->';
501
-
502
- devices.forEach((device) => {
503
- LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${device.deviceId}`);
504
- LoggerProxy.logger.log(LOG_HEADER, 'settings', JSON.stringify(device));
505
- });
506
- },
507
-
508
502
  endMeetingForAll: (meeting) => {
509
503
  if (meeting.meetingState === FULL_STATE.INACTIVE) {
510
504
  return Promise.reject(new MeetingNotActiveError());
@@ -18,7 +18,19 @@ const ADHOC_MEETING_DEFAULT_ERROR =
18
18
  'Failed starting the adhoc meeting, Please contact support team ';
19
19
  const CAPTCHA_ERROR_REQUIRES_PASSWORD_CODES = [423005, 423006];
20
20
  const POLICY_ERROR_CODES = [403049, 403104, 403103, 403048, 403102, 403101];
21
- const WEBINAR_REGISTRATION_ERROR_CODES = [403021, 403022, 403024];
21
+ /**
22
+ * 403021 - Meeting registration is required
23
+ * 403022 - Meeting registration is still pending
24
+ * 403024 - Meeting registration have been rejected
25
+ * 403137 - Registration ID verified failure
26
+ * 423007 - Registration ID input too many time,please input captcha code
27
+ * 403026 - Need to join meeting via webcast
28
+ * 403037 - Meeting join required registration ID
29
+ * 403137 - Registration ID verified failure
30
+ *
31
+ */
32
+ const JOIN_WEBINAR_ERROR_CODES = [403021, 403022, 403024, 403137, 423007, 403026, 403037, 403137];
33
+
22
34
  /**
23
35
  * Error to indicate that wbxappapi requires a password
24
36
  */
@@ -126,9 +138,9 @@ export class MeetingInfoV2CaptchaError extends Error {
126
138
  }
127
139
 
128
140
  /**
129
- * Error preventing join because of a webinar registration error
141
+ * Error preventing join because of a webinar have some error
130
142
  */
131
- export class MeetingInfoV2WebinarRegistrationError extends Error {
143
+ export class MeetingInfoV2JoinWebinarError extends Error {
132
144
  meetingInfo: any;
133
145
  sdkMessage: any;
134
146
  wbxAppApiCode: any;
@@ -142,7 +154,7 @@ export class MeetingInfoV2WebinarRegistrationError extends Error {
142
154
  */
143
155
  constructor(wbxAppApiErrorCode?: number, meetingInfo?: object, message?: string) {
144
156
  super(`${message}, code=${wbxAppApiErrorCode}`);
145
- this.name = 'MeetingInfoV2WebinarRegistrationError';
157
+ this.name = 'MeetingInfoV2JoinWebinarError';
146
158
  this.sdkMessage = message;
147
159
  this.stack = new Error().stack;
148
160
  this.wbxAppApiCode = wbxAppApiErrorCode;
@@ -204,21 +216,21 @@ export default class MeetingInfoV2 {
204
216
  };
205
217
 
206
218
  /**
207
- * Raises a handleWebinarRegistrationError for webinar registration error codes
219
+ * Raises a handleJoinWebinarError for join webinar error codes
208
220
  * @param {any} err the error from the request
209
221
  * @returns {void}
210
222
  */
211
- handleWebinarRegistrationError = (err) => {
223
+ handleJoinWebinarError = (err) => {
212
224
  if (!err.body) {
213
225
  return;
214
226
  }
215
227
 
216
- if (WEBINAR_REGISTRATION_ERROR_CODES.includes(err.body?.code)) {
217
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.WEBINAR_REGISTRATION_ERROR, {
228
+ if (JOIN_WEBINAR_ERROR_CODES.includes(err.body?.code)) {
229
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.JOIN_WEBINAR_ERROR, {
218
230
  code: err.body?.code,
219
231
  });
220
232
 
221
- throw new MeetingInfoV2WebinarRegistrationError(
233
+ throw new MeetingInfoV2JoinWebinarError(
222
234
  err.body?.code,
223
235
  err.body?.data?.meetingInfo,
224
236
  err.body?.message
@@ -286,7 +298,7 @@ export default class MeetingInfoV2 {
286
298
  })
287
299
  .catch((err) => {
288
300
  this.handlePolicyError(err);
289
- this.handleWebinarRegistrationError(err);
301
+ this.handleJoinWebinarError(err);
290
302
 
291
303
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADHOC_MEETING_FAILURE, {
292
304
  reason: err.message,
@@ -441,7 +453,7 @@ export default class MeetingInfoV2 {
441
453
 
442
454
  if (err?.statusCode === 403) {
443
455
  this.handlePolicyError(err);
444
- this.handleWebinarRegistrationError(err);
456
+ this.handleJoinWebinarError(err);
445
457
 
446
458
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.VERIFY_PASSWORD_ERROR, {
447
459
  reason: err.message,
@@ -56,7 +56,7 @@ import MeetingCollection from './collection';
56
56
  import {MEETING_KEY, INoiseReductionEffect, IVirtualBackgroundEffect} from './meetings.types';
57
57
  import MeetingsUtil from './util';
58
58
  import PermissionError from '../common/errors/permission';
59
- import WebinarRegistrationError from '../common/errors/webinar-registration-error';
59
+ import JoinWebinarError from '../common/errors/join-webinar-error';
60
60
  import {SpaceIDDeprecatedError} from '../common/errors/webex-errors';
61
61
  import NoMeetingInfoError from '../common/errors/no-meeting-info';
62
62
 
@@ -155,6 +155,9 @@ export type BasicMeetingInformation = {
155
155
  };
156
156
  meetingInfo: any;
157
157
  sessionCorrelationId: string;
158
+ roles: string[];
159
+ getCurUserType: () => string | null;
160
+ callStateForMetrics: CallStateForMetrics;
158
161
  };
159
162
 
160
163
  /**
@@ -1143,6 +1146,9 @@ export default class Meetings extends WebexPlugin {
1143
1146
  sessionId: meeting.locusInfo?.fullState?.sessionId,
1144
1147
  },
1145
1148
  },
1149
+ roles: meeting.roles,
1150
+ callStateForMetrics: meeting.callStateForMetrics,
1151
+ getCurUserType: meeting.getCurUserType,
1146
1152
  });
1147
1153
  this.meetingCollection.delete(meeting.id);
1148
1154
  Trigger.trigger(
@@ -1406,7 +1412,7 @@ export default class Meetings extends WebexPlugin {
1406
1412
  !(err instanceof CaptchaError) &&
1407
1413
  !(err instanceof PasswordError) &&
1408
1414
  !(err instanceof PermissionError) &&
1409
- !(err instanceof WebinarRegistrationError)
1415
+ !(err instanceof JoinWebinarError)
1410
1416
  ) {
1411
1417
  LoggerProxy.logger.info(
1412
1418
  `Meetings:index#createMeeting --> Info Unable to fetch meeting info for ${destination}.`
@@ -46,6 +46,7 @@ const MembersUtil = {
46
46
  {
47
47
  address:
48
48
  options.invitee.emailAddress || options.invitee.email || options.invitee.phoneNumber,
49
+ ...(options.invitee.roles ? {roles: options.invitee.roles} : {}),
49
50
  },
50
51
  ],
51
52
  alertIfActive: options.alertIfActive,
@@ -70,7 +70,9 @@ const BEHAVIORAL_METRICS = {
70
70
  ROAP_HTTP_RESPONSE_MISSING: 'js_sdk_roap_http_response_missing',
71
71
  TURN_DISCOVERY_REQUIRES_OK: 'js_sdk_turn_discovery_requires_ok',
72
72
  REACHABILITY_COMPLETED: 'js_sdk_reachability_completed',
73
- WEBINAR_REGISTRATION_ERROR: 'js_sdk_webinar_registration_error',
73
+ JOIN_WEBINAR_ERROR: 'js_sdk_join_webinar_error',
74
+ GUEST_ENTERED_LOBBY: 'js_sdk_guest_entered_lobby',
75
+ GUEST_EXITED_LOBBY: 'js_sdk_guest_exited_lobby',
74
76
  };
75
77
 
76
78
  export {BEHAVIORAL_METRICS as default};