@webex/plugin-meetings 2.37.2 → 2.38.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 (77) hide show
  1. package/dist/config.js +1 -1
  2. package/dist/config.js.map +1 -1
  3. package/dist/constants.js +2 -1
  4. package/dist/constants.js.map +1 -1
  5. package/dist/locus-info/index.js +24 -0
  6. package/dist/locus-info/index.js.map +1 -1
  7. package/dist/locus-info/parser.js +1 -0
  8. package/dist/locus-info/parser.js.map +1 -1
  9. package/dist/media/properties.js.map +1 -1
  10. package/dist/meeting/index.js +405 -352
  11. package/dist/meeting/index.js.map +1 -1
  12. package/dist/meeting/muteState.js +13 -0
  13. package/dist/meeting/muteState.js.map +1 -1
  14. package/dist/meeting/request.js +0 -27
  15. package/dist/meeting/request.js.map +1 -1
  16. package/dist/meeting/util.js +0 -56
  17. package/dist/meeting/util.js.map +1 -1
  18. package/dist/meeting-info/meeting-info-v2.js +2 -0
  19. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  20. package/dist/meetings/index.js +27 -17
  21. package/dist/meetings/index.js.map +1 -1
  22. package/dist/meetings/request.js +14 -12
  23. package/dist/meetings/request.js.map +1 -1
  24. package/dist/member/util.js +3 -1
  25. package/dist/member/util.js.map +1 -1
  26. package/dist/members/request.js +3 -1
  27. package/dist/members/request.js.map +1 -1
  28. package/dist/reachability/index.js +4 -4
  29. package/dist/reachability/index.js.map +1 -1
  30. package/dist/reactions/reactions.type.js +1 -0
  31. package/dist/reactions/reactions.type.js.map +1 -1
  32. package/dist/recording-controller/enums.js +17 -0
  33. package/dist/recording-controller/enums.js.map +1 -0
  34. package/dist/recording-controller/index.js +343 -0
  35. package/dist/recording-controller/index.js.map +1 -0
  36. package/dist/recording-controller/util.js +63 -0
  37. package/dist/recording-controller/util.js.map +1 -0
  38. package/dist/roap/index.js +16 -10
  39. package/dist/roap/index.js.map +1 -1
  40. package/dist/roap/turnDiscovery.js +6 -4
  41. package/dist/roap/turnDiscovery.js.map +1 -1
  42. package/dist/statsAnalyzer/mqaUtil.js +18 -6
  43. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  44. package/package.json +23 -18
  45. package/src/config.ts +1 -1
  46. package/src/constants.ts +1 -0
  47. package/src/locus-info/index.ts +24 -0
  48. package/src/locus-info/parser.ts +1 -0
  49. package/src/media/properties.ts +1 -1
  50. package/src/meeting/index.ts +121 -70
  51. package/src/meeting/muteState.ts +11 -0
  52. package/src/meeting/request.ts +0 -31
  53. package/src/meeting/util.ts +0 -60
  54. package/src/meeting-info/meeting-info-v2.ts +2 -0
  55. package/src/meetings/index.ts +8 -3
  56. package/src/meetings/request.ts +1 -1
  57. package/src/member/util.ts +2 -1
  58. package/src/members/request.ts +1 -0
  59. package/src/reachability/index.ts +2 -1
  60. package/src/reactions/reactions.type.ts +2 -1
  61. package/src/recording-controller/enums.ts +8 -0
  62. package/src/recording-controller/index.ts +315 -0
  63. package/src/recording-controller/util.ts +58 -0
  64. package/src/roap/index.ts +8 -8
  65. package/src/roap/turnDiscovery.ts +5 -5
  66. package/src/statsAnalyzer/mqaUtil.ts +6 -0
  67. package/test/integration/spec/journey.js +1 -1
  68. package/test/integration/spec/space-meeting.js +1 -1
  69. package/test/integration/spec/transcription.js +1 -1
  70. package/test/unit/spec/meeting/index.js +33 -6
  71. package/test/unit/spec/meeting/muteState.js +11 -0
  72. package/test/unit/spec/meeting/utils.js +0 -127
  73. package/test/unit/spec/recording-controller/index.js +231 -0
  74. package/test/unit/spec/recording-controller/util.js +102 -0
  75. package/test/unit/spec/roap/index.ts +10 -5
  76. package/test/unit/spec/roap/turnDiscovery.ts +13 -8
  77. package/tsconfig.json +6 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webex/plugin-meetings",
3
- "version": "2.37.2",
3
+ "version": "2.38.1",
4
4
  "description": "",
5
5
  "license": "Cisco EULA (https://www.cisco.com/c/en/us/products/end-user-license-agreement.html)",
6
6
  "contributors": [
@@ -12,6 +12,7 @@
12
12
  "Taymoor Khan <taykhan@cisco.com>"
13
13
  ],
14
14
  "main": "dist/index.js",
15
+ "types": "types/index.d.ts",
15
16
  "devMain": "src/index.js",
16
17
  "repository": {
17
18
  "type": "git",
@@ -27,31 +28,35 @@
27
28
  "envify"
28
29
  ]
29
30
  },
31
+ "scripts": {
32
+ "build": "yarn run -T tsc --declaration true --declarationDir ./types"
33
+ },
30
34
  "devDependencies": {
31
- "@webex/plugin-meetings": "2.37.2",
32
- "@webex/test-helper-chai": "2.37.2",
33
- "@webex/test-helper-mocha": "2.37.2",
34
- "@webex/test-helper-mock-webex": "2.37.2",
35
- "@webex/test-helper-retry": "2.37.2",
36
- "@webex/test-helper-test-users": "2.37.2",
35
+ "@webex/plugin-meetings": "2.38.1",
36
+ "@webex/test-helper-chai": "2.38.1",
37
+ "@webex/test-helper-mocha": "2.38.1",
38
+ "@webex/test-helper-mock-webex": "2.38.1",
39
+ "@webex/test-helper-retry": "2.38.1",
40
+ "@webex/test-helper-test-users": "2.38.1",
37
41
  "chai": "^4.3.4",
38
42
  "chai-as-promised": "^7.1.1",
39
43
  "jsdom-global": "3.0.2",
40
- "sinon": "^9.2.4"
44
+ "sinon": "^9.2.4",
45
+ "typescript": "^4.7.4"
41
46
  },
42
47
  "dependencies": {
43
- "@webex/common": "2.37.2",
48
+ "@webex/common": "2.38.1",
44
49
  "@webex/internal-media-core": "0.0.7-beta",
45
- "@webex/internal-plugin-conversation": "2.37.2",
46
- "@webex/internal-plugin-device": "2.37.2",
47
- "@webex/internal-plugin-mercury": "2.37.2",
48
- "@webex/internal-plugin-metrics": "2.37.2",
49
- "@webex/internal-plugin-support": "2.37.2",
50
- "@webex/internal-plugin-user": "2.37.2",
51
- "@webex/plugin-people": "2.37.2",
52
- "@webex/plugin-rooms": "2.37.2",
50
+ "@webex/internal-plugin-conversation": "2.38.1",
51
+ "@webex/internal-plugin-device": "2.38.1",
52
+ "@webex/internal-plugin-mercury": "2.38.1",
53
+ "@webex/internal-plugin-metrics": "2.38.1",
54
+ "@webex/internal-plugin-support": "2.38.1",
55
+ "@webex/internal-plugin-user": "2.38.1",
56
+ "@webex/plugin-people": "2.38.1",
57
+ "@webex/plugin-rooms": "2.38.1",
53
58
  "@webex/ts-sdp": "1.0.1",
54
- "@webex/webex-core": "2.37.2",
59
+ "@webex/webex-core": "2.38.1",
55
60
  "bowser": "^2.11.0",
56
61
  "btoa": "^1.2.1",
57
62
  "dotenv": "^4.0.0",
package/src/config.ts CHANGED
@@ -92,7 +92,7 @@ export default {
92
92
  enableMediaNegotiatedEvent: false,
93
93
  enableUnifiedMeetings: false,
94
94
  enableAdhocMeetings: false,
95
- enableTurnDiscovery: false,
95
+ enableTurnDiscovery: true,
96
96
  },
97
97
  },
98
98
  };
package/src/constants.ts CHANGED
@@ -539,6 +539,7 @@ export const LOCUSINFO = {
539
539
  EMBEDDED_APPS_UPDATED: 'EMBEDDED_APPS_UPDATED',
540
540
  SELF_CANNOT_VIEW_PARTICIPANT_LIST_CHANGE: 'SELF_CANNOT_VIEW_PARTICIPANT_LIST_CHANGE',
541
541
  SELF_IS_SHARING_BLOCKED_CHANGE: 'SELF_IS_SHARING_BLOCKED_CHANGE',
542
+ LINKS_SERVICES: 'LINKS_SERVICES',
542
543
  },
543
544
  };
544
545
 
@@ -62,6 +62,7 @@ export default class LocusInfo extends EventsScope {
62
62
  mediaShares: any;
63
63
  replace: any;
64
64
  url: any;
65
+ services: any;
65
66
 
66
67
  constructor(updateMeeting, webex, meetingId) {
67
68
  super();
@@ -185,6 +186,7 @@ export default class LocusInfo extends EventsScope {
185
186
  this.updateSelf(locus.self, locus.participants);
186
187
  this.updateHostInfo(locus.host);
187
188
  this.updateMediaShares(locus.mediaShares);
189
+ this.updateServices(locus.links?.services);
188
190
  }
189
191
 
190
192
  /**
@@ -355,6 +357,7 @@ export default class LocusInfo extends EventsScope {
355
357
  this.updateMemberShip(locus.membership);
356
358
  this.updateIdentifiers(locus.identities);
357
359
  this.updateEmbeddedApps(locus.embeddedApps);
360
+ this.updateServices(locus.links?.services);
358
361
  this.compareAndUpdate();
359
362
  // update which required to compare different objects from locus
360
363
  }
@@ -789,6 +792,27 @@ export default class LocusInfo extends EventsScope {
789
792
  }
790
793
  }
791
794
 
795
+ /**
796
+ * @param {Object} services
797
+ * @returns {undefined}
798
+ * @memberof LocusInfo
799
+ */
800
+ updateServices(services: Record<'breakout' | 'record', {url: string}>) {
801
+ if (services && !isEqual(this.services, services)) {
802
+ this.services = services;
803
+ this.emitScoped(
804
+ {
805
+ file: 'locus-info',
806
+ function: 'updateServices',
807
+ },
808
+ LOCUSINFO.EVENTS.LINKS_SERVICES,
809
+ {
810
+ services,
811
+ }
812
+ );
813
+ }
814
+ }
815
+
792
816
  /**
793
817
  * @param {Object} fullState
794
818
  * @returns {undefined}
@@ -397,6 +397,7 @@ export default class Parser {
397
397
  const {isLoci} = Parser;
398
398
  // @ts-ignore
399
399
  const setStatus = (status) => {
400
+ // @ts-ignore
400
401
  this.status = status;
401
402
  };
402
403
 
@@ -225,7 +225,7 @@ export default class MediaProperties {
225
225
  return Promise.resolve();
226
226
  }
227
227
 
228
- return new Promise((resolve, reject) => {
228
+ return new Promise<void>((resolve, reject) => {
229
229
  let timer;
230
230
 
231
231
  const iceListener = () => {
@@ -30,6 +30,7 @@ import ReconnectionManager from '../reconnection-manager';
30
30
  import MeetingRequest from './request';
31
31
  import Members from '../members/index';
32
32
  import MeetingUtil from './util';
33
+ import RecordingUtil from '../recording-controller/util';
33
34
  import MediaUtil from '../media/util';
34
35
  import Transcription from '../transcription';
35
36
  import PasswordError from '../common/errors/password-error';
@@ -88,6 +89,7 @@ import {SkinTones, Reactions} from '../reactions/reactions';
88
89
  import {Reaction, ReactionType, SkinToneType} from '../reactions/reactions.type';
89
90
 
90
91
  import InMeetingActions from './in-meeting-actions';
92
+ import RecordingController from '../recording-controller';
91
93
 
92
94
  const {isBrowser} = BrowserDetection();
93
95
 
@@ -452,6 +454,7 @@ export default class Meeting extends StatelessWebexPlugin {
452
454
  passwordStatus: string;
453
455
  queuedMediaUpdates: any[];
454
456
  recording: any;
457
+ recordingController: RecordingController;
455
458
  requiredCaptcha: any;
456
459
  shareStatus: string;
457
460
  statsAnalyzer: StatsAnalyzer;
@@ -885,6 +888,7 @@ export default class Meeting extends StatelessWebexPlugin {
885
888
  */
886
889
  // @ts-ignore - Fix type
887
890
  this.locusInfo = new LocusInfo(this.updateMeetingObject.bind(this), this.webex, this.id);
891
+
888
892
  // We had to add listeners first before setting up the locus instance
889
893
  /**
890
894
  * @instance
@@ -984,6 +988,20 @@ export default class Meeting extends StatelessWebexPlugin {
984
988
  */
985
989
  this.keepAliveTimerId = null;
986
990
 
991
+ /**
992
+ * The class that helps to control recording functions: start, stop, pause, resume, etc
993
+ * @instance
994
+ * @type {RecordingController}
995
+ * @public
996
+ * @memberof Meeting
997
+ */
998
+ this.recordingController = new RecordingController(this.meetingRequest, {
999
+ serviceUrl: this.locusInfo?.links?.services?.record?.url,
1000
+ sessionId: this.locusInfo?.fullState?.sessionId,
1001
+ locusUrl: this.locusInfo?.url,
1002
+ displayHints: [],
1003
+ });
1004
+
987
1005
  this.setUpLocusInfoListeners();
988
1006
  this.locusInfo.init(attrs.locus ? attrs.locus : {});
989
1007
  this.hasJoinedOnce = false;
@@ -1155,22 +1173,25 @@ export default class Meeting extends StatelessWebexPlugin {
1155
1173
  // we have to pass the wbxappapi hostname as the siteFullName parameter
1156
1174
  const {hostname} = new URL(this.requiredCaptcha.refreshURL);
1157
1175
 
1158
- return this.meetingRequest
1159
- .refreshCaptcha({
1160
- captchaRefreshUrl: `${this.requiredCaptcha.refreshURL}&siteFullName=${hostname}`,
1161
- captchaId: this.requiredCaptcha.captchaId,
1162
- })
1163
- .then((response) => {
1164
- this.requiredCaptcha.captchaId = response.body.captchaID;
1165
- this.requiredCaptcha.verificationImageURL = response.body.verificationImageURL;
1166
- this.requiredCaptcha.verificationAudioURL = response.body.verificationAudioURL;
1167
- })
1168
- .catch((error) => {
1169
- LoggerProxy.logger.error(
1170
- `Meeting:index#refreshCaptcha --> Error Unable to refresh captcha for ${this.destination} - ${error}`
1171
- );
1172
- throw error;
1173
- });
1176
+ return (
1177
+ this.meetingRequest
1178
+ // @ts-ignore
1179
+ .refreshCaptcha({
1180
+ captchaRefreshUrl: `${this.requiredCaptcha.refreshURL}&siteFullName=${hostname}`,
1181
+ captchaId: this.requiredCaptcha.captchaId,
1182
+ })
1183
+ .then((response) => {
1184
+ this.requiredCaptcha.captchaId = response.body.captchaID;
1185
+ this.requiredCaptcha.verificationImageURL = response.body.verificationImageURL;
1186
+ this.requiredCaptcha.verificationAudioURL = response.body.verificationAudioURL;
1187
+ })
1188
+ .catch((error) => {
1189
+ LoggerProxy.logger.error(
1190
+ `Meeting:index#refreshCaptcha --> Error Unable to refresh captcha for ${this.destination} - ${error}`
1191
+ );
1192
+ throw error;
1193
+ })
1194
+ );
1174
1195
  }
1175
1196
 
1176
1197
  /**
@@ -1183,6 +1204,7 @@ export default class Meeting extends StatelessWebexPlugin {
1183
1204
  // meeting update listeners
1184
1205
  this.setUpLocusInfoSelfListener();
1185
1206
  this.setUpLocusInfoMeetingListener();
1207
+ this.setUpLocusServicesListener();
1186
1208
  // members update listeners
1187
1209
  this.setUpLocusFullStateListener();
1188
1210
  this.setUpLocusUrlListener();
@@ -1997,6 +2019,23 @@ export default class Meeting extends StatelessWebexPlugin {
1997
2019
  this.members.locusUrlUpdate(payload);
1998
2020
  this.locusUrl = payload;
1999
2021
  this.locusId = this.locusUrl?.split('/').pop();
2022
+ this.recordingController.setLocusUrl(this.locusUrl);
2023
+ });
2024
+ }
2025
+
2026
+ /**
2027
+ * Set up the locus info service link listener
2028
+ * update the locusInfo for recording controller
2029
+ * does not currently re-emit the event as it's internal only
2030
+ * payload is unused
2031
+ * @returns {undefined}
2032
+ * @private
2033
+ * @memberof Meeting
2034
+ */
2035
+ private setUpLocusServicesListener() {
2036
+ this.locusInfo.on(LOCUSINFO.EVENTS.LINKS_SERVICES, (payload) => {
2037
+ this.recordingController.setServiceUrl(payload?.services?.record?.url);
2038
+ this.recordingController.setSessionId(this.locusInfo?.fullState?.sessionId);
2000
2039
  });
2001
2040
  }
2002
2041
 
@@ -2046,10 +2085,10 @@ export default class Meeting extends StatelessWebexPlugin {
2046
2085
  canAdmitParticipant: MeetingUtil.canAdmitParticipant(payload.info.userDisplayHints),
2047
2086
  canLock: MeetingUtil.canUserLock(payload.info.userDisplayHints),
2048
2087
  canUnlock: MeetingUtil.canUserUnlock(payload.info.userDisplayHints),
2049
- canStartRecording: MeetingUtil.canUserRecord(payload.info.userDisplayHints),
2050
- canStopRecording: MeetingUtil.canUserStop(payload.info.userDisplayHints),
2051
- canPauseRecording: MeetingUtil.canUserPause(payload.info.userDisplayHints),
2052
- canResumeRecording: MeetingUtil.canUserResume(payload.info.userDisplayHints),
2088
+ canStartRecording: RecordingUtil.canUserStart(payload.info.userDisplayHints),
2089
+ canStopRecording: RecordingUtil.canUserStop(payload.info.userDisplayHints),
2090
+ canPauseRecording: RecordingUtil.canUserPause(payload.info.userDisplayHints),
2091
+ canResumeRecording: RecordingUtil.canUserResume(payload.info.userDisplayHints),
2053
2092
  canRaiseHand: MeetingUtil.canUserRaiseHand(payload.info.userDisplayHints),
2054
2093
  canLowerAllHands: MeetingUtil.canUserLowerAllHands(payload.info.userDisplayHints),
2055
2094
  canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(
@@ -2073,6 +2112,8 @@ export default class Meeting extends StatelessWebexPlugin {
2073
2112
  waitingForOthersToJoin: MeetingUtil.waitingForOthersToJoin(payload.info.userDisplayHints),
2074
2113
  });
2075
2114
 
2115
+ this.recordingController.setDisplayHints(payload.info.userDisplayHints);
2116
+
2076
2117
  if (changed) {
2077
2118
  Trigger.trigger(
2078
2119
  this,
@@ -2294,7 +2335,7 @@ export default class Meeting extends StatelessWebexPlugin {
2294
2335
  .catch((error) => {
2295
2336
  // @ts-ignore
2296
2337
  LoggerProxy.logger.error(
2297
- `Meeting:index#setUpLocusInfoMeetingListener --> REMOTE_RESPONSE. Issue with leave for meeting, meeting still in collection: ${this.meeting}, error: ${error}`
2338
+ `Meeting:index#setUpLocusInfoMeetingListener --> REMOTE_RESPONSE. Issue with leave for meeting, meeting still in collection: ${this}, error: ${error}`
2298
2339
  );
2299
2340
  });
2300
2341
  }
@@ -2329,7 +2370,7 @@ export default class Meeting extends StatelessWebexPlugin {
2329
2370
  .catch((error) => {
2330
2371
  // @ts-ignore
2331
2372
  LoggerProxy.logger.error(
2332
- `Meeting:index#setUpLocusInfoMeetingListener --> DESTROY_MEETING. Issue with leave for meeting, meeting still in collection: ${this.meeting}, error: ${error}`
2373
+ `Meeting:index#setUpLocusInfoMeetingListener --> DESTROY_MEETING. Issue with leave for meeting, meeting still in collection: ${this}, error: ${error}`
2333
2374
  );
2334
2375
  });
2335
2376
  } else {
@@ -2571,6 +2612,7 @@ export default class Meeting extends StatelessWebexPlugin {
2571
2612
  this.locusUrl = locusMeetingObject?.url || webexMeetingInfo?.locusUrl || this.locusUrl;
2572
2613
  // @ts-ignore - config coming from registerPlugin
2573
2614
  this.setSipUri(
2615
+ // @ts-ignore
2574
2616
  this.config.experimental.enableUnifiedMeetings
2575
2617
  ? locusMeetingObject?.info.sipUri || webexMeetingInfo?.sipUrl
2576
2618
  : locusMeetingObject?.info.sipUri || webexMeetingInfo?.sipMeetingUri || this.sipUri
@@ -3667,6 +3709,7 @@ export default class Meeting extends StatelessWebexPlugin {
3667
3709
  // @ts-ignore - fix type
3668
3710
  const {
3669
3711
  body: {webSocketUrl},
3712
+ // @ts-ignore
3670
3713
  } = await this.request({
3671
3714
  method: HTTP_VERBS.POST,
3672
3715
  uri: datachannelUrl,
@@ -3980,28 +4023,31 @@ export default class Meeting extends StatelessWebexPlugin {
3980
4023
 
3981
4024
  if (!this.dialInUrl) this.dialInUrl = `dialin:///${uuid.v4()}`;
3982
4025
 
3983
- return this.meetingRequest
3984
- .dialIn({
3985
- correlationId,
3986
- dialInUrl: this.dialInUrl,
3987
- locusUrl,
3988
- clientUrl: this.deviceUrl,
3989
- })
3990
- .then((res) => {
3991
- this.locusInfo.onFullLocus(res.body.locus);
3992
- })
3993
- .catch((error) => {
3994
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_DIAL_IN_FAILURE, {
3995
- correlation_id: this.correlationId,
3996
- dial_in_url: this.dialInUrl,
3997
- locus_id: locusUrl.split('/').pop(),
3998
- client_url: this.deviceUrl,
3999
- reason: error.error?.message,
4000
- stack: error.stack,
4001
- });
4026
+ return (
4027
+ this.meetingRequest
4028
+ // @ts-ignore
4029
+ .dialIn({
4030
+ correlationId,
4031
+ dialInUrl: this.dialInUrl,
4032
+ locusUrl,
4033
+ clientUrl: this.deviceUrl,
4034
+ })
4035
+ .then((res) => {
4036
+ this.locusInfo.onFullLocus(res.body.locus);
4037
+ })
4038
+ .catch((error) => {
4039
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_DIAL_IN_FAILURE, {
4040
+ correlation_id: this.correlationId,
4041
+ dial_in_url: this.dialInUrl,
4042
+ locus_id: locusUrl.split('/').pop(),
4043
+ client_url: this.deviceUrl,
4044
+ reason: error.error?.message,
4045
+ stack: error.stack,
4046
+ });
4002
4047
 
4003
- return Promise.reject(error);
4004
- });
4048
+ return Promise.reject(error);
4049
+ })
4050
+ );
4005
4051
  }
4006
4052
 
4007
4053
  /**
@@ -4018,29 +4064,32 @@ export default class Meeting extends StatelessWebexPlugin {
4018
4064
 
4019
4065
  if (!this.dialOutUrl) this.dialOutUrl = `dialout:///${uuid.v4()}`;
4020
4066
 
4021
- return this.meetingRequest
4022
- .dialOut({
4023
- correlationId,
4024
- dialOutUrl: this.dialOutUrl,
4025
- phoneNumber,
4026
- locusUrl,
4027
- clientUrl: this.deviceUrl,
4028
- })
4029
- .then((res) => {
4030
- this.locusInfo.onFullLocus(res.body.locus);
4031
- })
4032
- .catch((error) => {
4033
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_DIAL_OUT_FAILURE, {
4034
- correlation_id: this.correlationId,
4035
- dial_out_url: this.dialOutUrl,
4036
- locus_id: locusUrl.split('/').pop(),
4037
- client_url: this.deviceUrl,
4038
- reason: error.error?.message,
4039
- stack: error.stack,
4040
- });
4067
+ return (
4068
+ this.meetingRequest
4069
+ // @ts-ignore
4070
+ .dialOut({
4071
+ correlationId,
4072
+ dialOutUrl: this.dialOutUrl,
4073
+ phoneNumber,
4074
+ locusUrl,
4075
+ clientUrl: this.deviceUrl,
4076
+ })
4077
+ .then((res) => {
4078
+ this.locusInfo.onFullLocus(res.body.locus);
4079
+ })
4080
+ .catch((error) => {
4081
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_DIAL_OUT_FAILURE, {
4082
+ correlation_id: this.correlationId,
4083
+ dial_out_url: this.dialOutUrl,
4084
+ locus_id: locusUrl.split('/').pop(),
4085
+ client_url: this.deviceUrl,
4086
+ reason: error.error?.message,
4087
+ stack: error.stack,
4088
+ });
4041
4089
 
4042
- return Promise.reject(error);
4043
- });
4090
+ return Promise.reject(error);
4091
+ })
4092
+ );
4044
4093
  }
4045
4094
 
4046
4095
  /**
@@ -4374,6 +4423,7 @@ export default class Meeting extends StatelessWebexPlugin {
4374
4423
  // Add ip address info if geo hint is present
4375
4424
  // @ts-ignore fix type
4376
4425
  options.data.intervalMetadata.peerReflexiveIP =
4426
+ // @ts-ignore
4377
4427
  this.webex.meetings.geoHintInfo?.clientAddress ||
4378
4428
  options.data.intervalMetadata.peerReflexiveIP ||
4379
4429
  MQA_STATS.DEFAULT_IP;
@@ -4855,10 +4905,11 @@ export default class Meeting extends StatelessWebexPlugin {
4855
4905
  // we actually get a OFFER from the server and a GLAR condition happens
4856
4906
  if (startShare) {
4857
4907
  // We are assuming that the clients are connected when doing an update
4908
+ // @ts-ignore
4858
4909
  return this.share();
4859
4910
  }
4860
4911
 
4861
- throw error;
4912
+ return Promise.resolve();
4862
4913
  })
4863
4914
  .then(() =>
4864
4915
  logRequest(
@@ -5498,7 +5549,7 @@ export default class Meeting extends StatelessWebexPlugin {
5498
5549
  * @memberof Meeting
5499
5550
  */
5500
5551
  public startRecording() {
5501
- return MeetingUtil.startRecording(this.meetingRequest, this.locusUrl, this.locusInfo);
5552
+ return this.recordingController.startRecording();
5502
5553
  }
5503
5554
 
5504
5555
  /**
@@ -5508,7 +5559,7 @@ export default class Meeting extends StatelessWebexPlugin {
5508
5559
  * @memberof Meeting
5509
5560
  */
5510
5561
  public stopRecording() {
5511
- return MeetingUtil.stopRecording(this.meetingRequest, this.locusUrl, this.locusInfo);
5562
+ return this.recordingController.stopRecording();
5512
5563
  }
5513
5564
 
5514
5565
  /**
@@ -5518,7 +5569,7 @@ export default class Meeting extends StatelessWebexPlugin {
5518
5569
  * @memberof Meeting
5519
5570
  */
5520
5571
  public pauseRecording() {
5521
- return MeetingUtil.pauseRecording(this.meetingRequest, this.locusUrl, this.locusInfo);
5572
+ return this.recordingController.pauseRecording();
5522
5573
  }
5523
5574
 
5524
5575
  /**
@@ -5528,7 +5579,7 @@ export default class Meeting extends StatelessWebexPlugin {
5528
5579
  * @memberof Meeting
5529
5580
  */
5530
5581
  public resumeRecording() {
5531
- return MeetingUtil.resumeRecording(this.meetingRequest, this.locusUrl, this.locusInfo);
5582
+ return this.recordingController.resumeRecording();
5532
5583
  }
5533
5584
 
5534
5585
  /**
@@ -329,6 +329,17 @@ class MuteState {
329
329
  );
330
330
  }
331
331
 
332
+ /**
333
+ * Returns true if the user is locally muted
334
+ *
335
+ * @public
336
+ * @memberof MuteState
337
+ * @returns {Boolean}
338
+ */
339
+ public isLocallyMuted() {
340
+ return this.state.client.localMute || this.state.server.localMute;
341
+ }
342
+
332
343
  /**
333
344
  * Returns true if the user is muted as a result of the client request (and not remotely muted)
334
345
  *
@@ -496,37 +496,6 @@ export default class MeetingRequest extends StatelessWebexPlugin {
496
496
  });
497
497
  }
498
498
 
499
- /**
500
- * Make a network request to acknowledge a meeting
501
- * @param {Object} options
502
- * @param {String} options.locusUrl
503
- * @param {String} options.deviceUrl
504
- * @param {String} options.id
505
- * @returns {Promise}
506
- */
507
- recordMeeting(options: {
508
- locusUrl: string;
509
- deviceUrl: string;
510
- id: string;
511
- recording: any;
512
- paused: any;
513
- }) {
514
- const uri = `${options.locusUrl}/${CONTROLS}`;
515
- const body = {
516
- record: {
517
- recording: options.recording,
518
- paused: options.paused,
519
- },
520
- };
521
-
522
- // @ts-ignore
523
- return this.request({
524
- method: HTTP_VERBS.PATCH,
525
- uri,
526
- body,
527
- });
528
- }
529
-
530
499
  lockMeeting(options) {
531
500
  const uri = `${options.locusUrl}/${CONTROLS}`;
532
501
  const body = {
@@ -411,66 +411,6 @@ MeetingUtil.canUserUnlock = (displayHints) =>
411
411
  displayHints.includes(DISPLAY_HINTS.LOCK_CONTROL_UNLOCK) &&
412
412
  displayHints.includes(DISPLAY_HINTS.LOCK_STATUS_LOCKED);
413
413
 
414
- MeetingUtil.canUserRecord = (displayHints) =>
415
- displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_START);
416
-
417
- MeetingUtil.canUserPause = (displayHints) =>
418
- displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_PAUSE);
419
-
420
- MeetingUtil.canUserResume = (displayHints) =>
421
- displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_RESUME);
422
-
423
- MeetingUtil.canUserStop = (displayHints) =>
424
- displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_STOP);
425
-
426
- MeetingUtil.startRecording = (request, locusUrl, locusInfo) => {
427
- const displayHints = MeetingUtil.getUserDisplayHintsFromLocusInfo(locusInfo);
428
-
429
- if (MeetingUtil.canUserRecord(displayHints)) {
430
- return request.recordMeeting({locusUrl, recording: true, paused: false});
431
- }
432
-
433
- return Promise.reject(
434
- new PermissionError('Start recording not allowed, due to moderator property.')
435
- );
436
- };
437
-
438
- MeetingUtil.pauseRecording = (request, locusUrl, locusInfo) => {
439
- const displayHints = MeetingUtil.getUserDisplayHintsFromLocusInfo(locusInfo);
440
-
441
- if (MeetingUtil.canUserPause(displayHints)) {
442
- return request.recordMeeting({locusUrl, recording: true, paused: true});
443
- }
444
-
445
- return Promise.reject(
446
- new PermissionError('Pause recording not allowed, due to moderator property.')
447
- );
448
- };
449
-
450
- MeetingUtil.resumeRecording = (request, locusUrl, locusInfo) => {
451
- const displayHints = MeetingUtil.getUserDisplayHintsFromLocusInfo(locusInfo);
452
-
453
- if (MeetingUtil.canUserResume(displayHints)) {
454
- return request.recordMeeting({locusUrl, recording: true, paused: false});
455
- }
456
-
457
- return Promise.reject(
458
- new PermissionError('Resume recording not allowed, due to moderator property.')
459
- );
460
- };
461
-
462
- MeetingUtil.stopRecording = (request, locusUrl, locusInfo) => {
463
- const displayHints = MeetingUtil.getUserDisplayHintsFromLocusInfo(locusInfo);
464
-
465
- if (MeetingUtil.canUserStop(displayHints)) {
466
- return request.recordMeeting({locusUrl, recording: false, paused: false});
467
- }
468
-
469
- return Promise.reject(
470
- new PermissionError('Stop recording not allowed, due to moderator property.')
471
- );
472
- };
473
-
474
414
  MeetingUtil.canUserRaiseHand = (displayHints) => displayHints.includes(DISPLAY_HINTS.RAISE_HAND);
475
415
 
476
416
  MeetingUtil.canUserLowerAllHands = (displayHints) =>
@@ -19,6 +19,7 @@ export class MeetingInfoV2PasswordError extends Error {
19
19
  meetingInfo: any;
20
20
  sdkMessage: any;
21
21
  wbxAppApiCode: any;
22
+ body: any;
22
23
 
23
24
  /**
24
25
  *
@@ -70,6 +71,7 @@ export class MeetingInfoV2CaptchaError extends Error {
70
71
  isPasswordRequired: any;
71
72
  sdkMessage: any;
72
73
  wbxAppApiCode: any;
74
+ body: any;
73
75
  /**
74
76
  *
75
77
  * @constructor