@webex/plugin-meetings 3.7.0-wxcc.1 → 3.8.0

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 (220) hide show
  1. package/dist/annotation/annotation.types.d.ts +42 -0
  2. package/dist/annotation/constants.d.ts +31 -0
  3. package/dist/annotation/index.d.ts +117 -0
  4. package/dist/breakouts/breakout.d.ts +8 -0
  5. package/dist/breakouts/breakout.js +1 -1
  6. package/dist/breakouts/collection.d.ts +5 -0
  7. package/dist/breakouts/edit-lock-error.d.ts +15 -0
  8. package/dist/breakouts/events.d.ts +8 -0
  9. package/dist/breakouts/index.d.ts +5 -0
  10. package/dist/breakouts/index.js +1 -1
  11. package/dist/breakouts/request.d.ts +22 -0
  12. package/dist/breakouts/utils.d.ts +15 -0
  13. package/dist/common/browser-detection.d.ts +9 -0
  14. package/dist/common/collection.d.ts +48 -0
  15. package/dist/common/config.d.ts +2 -0
  16. package/dist/common/errors/captcha-error.d.ts +15 -0
  17. package/dist/common/errors/intent-to-join.d.ts +16 -0
  18. package/dist/common/errors/join-meeting.d.ts +17 -0
  19. package/dist/common/errors/media.d.ts +15 -0
  20. package/dist/common/errors/no-meeting-info.d.ts +14 -0
  21. package/dist/common/errors/parameter.d.ts +15 -0
  22. package/dist/common/errors/password-error.d.ts +15 -0
  23. package/dist/common/errors/permission.d.ts +14 -0
  24. package/dist/common/errors/reclaim-host-role-error.js +149 -0
  25. package/dist/common/errors/reclaim-host-role-error.js.map +1 -0
  26. package/dist/common/errors/reclaim-host-role-errors.d.ts +60 -0
  27. package/dist/common/errors/reconnection-in-progress.d.ts +9 -0
  28. package/dist/common/errors/reconnection-in-progress.js +33 -0
  29. package/dist/common/errors/reconnection-in-progress.js.map +1 -0
  30. package/dist/common/errors/reconnection.d.ts +15 -0
  31. package/dist/common/errors/stats.d.ts +15 -0
  32. package/dist/common/errors/webex-errors.d.ts +93 -0
  33. package/dist/common/errors/webex-meetings-error.d.ts +20 -0
  34. package/dist/common/events/events-scope.d.ts +17 -0
  35. package/dist/common/events/events.d.ts +12 -0
  36. package/dist/common/events/trigger-proxy.d.ts +2 -0
  37. package/dist/common/events/util.d.ts +2 -0
  38. package/dist/common/logs/logger-config.d.ts +2 -0
  39. package/dist/common/logs/logger-proxy.d.ts +2 -0
  40. package/dist/common/logs/request.d.ts +36 -0
  41. package/dist/common/queue.d.ts +34 -0
  42. package/dist/config.d.ts +72 -0
  43. package/dist/constants.d.ts +1088 -0
  44. package/dist/constants.js +15 -3
  45. package/dist/constants.js.map +1 -1
  46. package/dist/controls-options-manager/constants.d.ts +4 -0
  47. package/dist/controls-options-manager/enums.d.ts +15 -0
  48. package/dist/controls-options-manager/index.d.ts +136 -0
  49. package/dist/controls-options-manager/types.d.ts +43 -0
  50. package/dist/controls-options-manager/util.d.ts +1 -0
  51. package/dist/index.d.ts +7 -0
  52. package/dist/interceptors/index.d.ts +2 -0
  53. package/dist/interceptors/locusRetry.d.ts +27 -0
  54. package/dist/interpretation/collection.d.ts +5 -0
  55. package/dist/interpretation/index.d.ts +5 -0
  56. package/dist/interpretation/index.js +1 -1
  57. package/dist/interpretation/siLanguage.d.ts +5 -0
  58. package/dist/interpretation/siLanguage.js +1 -1
  59. package/dist/locus-info/controlsUtils.d.ts +2 -0
  60. package/dist/locus-info/embeddedAppsUtils.d.ts +2 -0
  61. package/dist/locus-info/fullState.d.ts +2 -0
  62. package/dist/locus-info/hostUtils.d.ts +2 -0
  63. package/dist/locus-info/index.d.ts +322 -0
  64. package/dist/locus-info/infoUtils.d.ts +2 -0
  65. package/dist/locus-info/mediaSharesUtils.d.ts +2 -0
  66. package/dist/locus-info/parser.d.ts +272 -0
  67. package/dist/locus-info/selfUtils.d.ts +2 -0
  68. package/dist/locus-info/selfUtils.js +5 -0
  69. package/dist/locus-info/selfUtils.js.map +1 -1
  70. package/dist/media/MediaConnectionAwaiter.js +1 -0
  71. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  72. package/dist/media/index.d.ts +34 -0
  73. package/dist/media/properties.d.ts +93 -0
  74. package/dist/media/properties.js +30 -16
  75. package/dist/media/properties.js.map +1 -1
  76. package/dist/media/util.d.ts +2 -0
  77. package/dist/mediaQualityMetrics/config.d.ts +241 -0
  78. package/dist/mediaQualityMetrics/config.js +502 -0
  79. package/dist/mediaQualityMetrics/config.js.map +1 -0
  80. package/dist/meeting/brbState.js +167 -0
  81. package/dist/meeting/brbState.js.map +1 -0
  82. package/dist/meeting/effectsState.js +260 -0
  83. package/dist/meeting/effectsState.js.map +1 -0
  84. package/dist/meeting/in-meeting-actions.d.ts +167 -0
  85. package/dist/meeting/index.d.ts +1825 -0
  86. package/dist/meeting/index.js +363 -295
  87. package/dist/meeting/index.js.map +1 -1
  88. package/dist/meeting/locusMediaRequest.d.ts +74 -0
  89. package/dist/meeting/muteState.d.ts +178 -0
  90. package/dist/meeting/muteState.js +1 -6
  91. package/dist/meeting/muteState.js.map +1 -1
  92. package/dist/meeting/request.d.ts +295 -0
  93. package/dist/meeting/request.type.d.ts +11 -0
  94. package/dist/meeting/state.d.ts +9 -0
  95. package/dist/meeting/util.d.ts +119 -0
  96. package/dist/meeting/voicea-meeting.d.ts +16 -0
  97. package/dist/meeting-info/collection.d.ts +20 -0
  98. package/dist/meeting-info/index.d.ts +69 -0
  99. package/dist/meeting-info/meeting-info-v2.d.ts +123 -0
  100. package/dist/meeting-info/meeting-info-v2.js +19 -12
  101. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  102. package/dist/meeting-info/request.d.ts +22 -0
  103. package/dist/meeting-info/util.d.ts +2 -0
  104. package/dist/meeting-info/utilv2.d.ts +2 -0
  105. package/dist/meeting-info/utilv2.js +5 -1
  106. package/dist/meeting-info/utilv2.js.map +1 -1
  107. package/dist/meetings/collection.d.ts +40 -0
  108. package/dist/meetings/index.d.ts +390 -0
  109. package/dist/meetings/meetings.types.d.ts +4 -0
  110. package/dist/meetings/request.d.ts +27 -0
  111. package/dist/meetings/util.d.ts +18 -0
  112. package/dist/member/index.d.ts +160 -0
  113. package/dist/member/member.types.js +17 -0
  114. package/dist/member/member.types.js.map +1 -0
  115. package/dist/member/types.d.ts +32 -0
  116. package/dist/member/util.d.ts +2 -0
  117. package/dist/members/collection.d.ts +29 -0
  118. package/dist/members/index.d.ts +353 -0
  119. package/dist/members/request.d.ts +114 -0
  120. package/dist/members/types.d.ts +25 -0
  121. package/dist/members/util.d.ts +215 -0
  122. package/dist/metrics/config.js +276 -0
  123. package/dist/metrics/config.js.map +1 -0
  124. package/dist/metrics/constants.d.ts +70 -0
  125. package/dist/metrics/constants.js +2 -0
  126. package/dist/metrics/constants.js.map +1 -1
  127. package/dist/metrics/index.d.ts +45 -0
  128. package/dist/multistream/mediaRequestManager.d.ts +119 -0
  129. package/dist/multistream/receiveSlot.d.ts +68 -0
  130. package/dist/multistream/receiveSlotManager.d.ts +56 -0
  131. package/dist/multistream/remoteMedia.d.ts +72 -0
  132. package/dist/multistream/remoteMediaGroup.d.ts +49 -0
  133. package/dist/multistream/remoteMediaManager.d.ts +300 -0
  134. package/dist/multistream/sendSlotManager.d.ts +69 -0
  135. package/dist/networkQualityMonitor/index.d.ts +70 -0
  136. package/dist/networkQualityMonitor/index.js +221 -0
  137. package/dist/networkQualityMonitor/index.js.map +1 -0
  138. package/dist/peer-connection-manager/index.js +671 -0
  139. package/dist/peer-connection-manager/index.js.map +1 -0
  140. package/dist/peer-connection-manager/util.js +109 -0
  141. package/dist/peer-connection-manager/util.js.map +1 -0
  142. package/dist/personal-meeting-room/index.d.ts +47 -0
  143. package/dist/personal-meeting-room/request.d.ts +14 -0
  144. package/dist/personal-meeting-room/util.d.ts +2 -0
  145. package/dist/reachability/clusterReachability.d.ts +109 -0
  146. package/dist/reachability/index.d.ts +105 -0
  147. package/dist/reachability/index.js +31 -3
  148. package/dist/reachability/index.js.map +1 -1
  149. package/dist/reachability/request.d.ts +39 -0
  150. package/dist/reachability/util.d.ts +8 -0
  151. package/dist/reactions/constants.d.ts +3 -0
  152. package/dist/reactions/reactions.d.ts +4 -0
  153. package/dist/reactions/reactions.type.d.ts +52 -0
  154. package/dist/reconnection-manager/index.d.ts +136 -0
  155. package/dist/recording-controller/enums.d.ts +7 -0
  156. package/dist/recording-controller/index.d.ts +207 -0
  157. package/dist/recording-controller/util.d.ts +14 -0
  158. package/dist/roap/collection.js +62 -0
  159. package/dist/roap/collection.js.map +1 -0
  160. package/dist/roap/handler.js +275 -0
  161. package/dist/roap/handler.js.map +1 -0
  162. package/dist/roap/index.d.ts +86 -0
  163. package/dist/roap/request.d.ts +39 -0
  164. package/dist/roap/state.js +126 -0
  165. package/dist/roap/state.js.map +1 -0
  166. package/dist/roap/turnDiscovery.d.ts +155 -0
  167. package/dist/roap/util.js +75 -0
  168. package/dist/roap/util.js.map +1 -0
  169. package/dist/rtcMetrics/constants.d.ts +4 -0
  170. package/dist/rtcMetrics/constants.js +11 -0
  171. package/dist/rtcMetrics/constants.js.map +1 -0
  172. package/dist/rtcMetrics/index.d.ts +61 -0
  173. package/dist/rtcMetrics/index.js +197 -0
  174. package/dist/rtcMetrics/index.js.map +1 -0
  175. package/dist/statsAnalyzer/global.d.ts +36 -0
  176. package/dist/statsAnalyzer/global.js +126 -0
  177. package/dist/statsAnalyzer/global.js.map +1 -0
  178. package/dist/statsAnalyzer/index.d.ts +217 -0
  179. package/dist/statsAnalyzer/index.js +1013 -0
  180. package/dist/statsAnalyzer/index.js.map +1 -0
  181. package/dist/statsAnalyzer/mqaUtil.d.ts +48 -0
  182. package/dist/statsAnalyzer/mqaUtil.js +179 -0
  183. package/dist/statsAnalyzer/mqaUtil.js.map +1 -0
  184. package/dist/transcription/index.d.ts +64 -0
  185. package/dist/types/common/errors/reconnection-in-progress.d.ts +9 -0
  186. package/dist/types/constants.d.ts +9 -2
  187. package/dist/types/mediaQualityMetrics/config.d.ts +241 -0
  188. package/dist/types/meeting/brbState.d.ts +54 -0
  189. package/dist/types/meeting/index.d.ts +23 -0
  190. package/dist/types/meeting-info/meeting-info-v2.d.ts +3 -1
  191. package/dist/types/metrics/constants.d.ts +2 -0
  192. package/dist/types/networkQualityMonitor/index.d.ts +70 -0
  193. package/dist/types/reachability/index.d.ts +9 -1
  194. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  195. package/dist/types/rtcMetrics/index.d.ts +71 -0
  196. package/dist/types/statsAnalyzer/global.d.ts +36 -0
  197. package/dist/types/statsAnalyzer/index.d.ts +217 -0
  198. package/dist/types/statsAnalyzer/mqaUtil.d.ts +48 -0
  199. package/dist/webinar/collection.d.ts +16 -0
  200. package/dist/webinar/index.d.ts +5 -0
  201. package/dist/webinar/index.js +1 -1
  202. package/package.json +23 -23
  203. package/src/constants.ts +10 -2
  204. package/src/locus-info/selfUtils.ts +5 -0
  205. package/src/media/MediaConnectionAwaiter.ts +2 -0
  206. package/src/media/properties.ts +34 -13
  207. package/src/meeting/brbState.ts +169 -0
  208. package/src/meeting/index.ts +112 -26
  209. package/src/meeting/muteState.ts +1 -6
  210. package/src/meeting-info/meeting-info-v2.ts +9 -1
  211. package/src/meeting-info/utilv2.ts +14 -2
  212. package/src/metrics/constants.ts +2 -0
  213. package/src/reachability/index.ts +29 -1
  214. package/test/unit/spec/locus-info/selfUtils.js +10 -0
  215. package/test/unit/spec/media/properties.ts +15 -0
  216. package/test/unit/spec/meeting/brbState.ts +114 -0
  217. package/test/unit/spec/meeting/index.js +92 -30
  218. package/test/unit/spec/meeting/muteState.js +0 -24
  219. package/test/unit/spec/meeting-info/utilv2.js +9 -0
  220. package/test/unit/spec/reachability/index.ts +120 -10
@@ -122,8 +122,9 @@ import {
122
122
  ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT,
123
123
  NAMED_MEDIA_GROUP_TYPE_AUDIO,
124
124
  WEBINAR_ERROR_WEBCAST,
125
- WEBINAR_ERROR_REGISTRATIONID,
125
+ WEBINAR_ERROR_REGISTRATION_ID,
126
126
  JOIN_BEFORE_HOST,
127
+ REGISTRATION_ID_STATUS,
127
128
  } from '../constants';
128
129
  import BEHAVIORAL_METRICS from '../metrics/constants';
129
130
  import ParameterError from '../common/errors/parameter';
@@ -163,6 +164,7 @@ import {LocusMediaRequest} from './locusMediaRequest';
163
164
  import {ConnectionStateHandler, ConnectionStateEvent} from './connectionStateHandler';
164
165
  import JoinWebinarError from '../common/errors/join-webinar-error';
165
166
  import Member from '../member';
167
+ import {BrbState, createBrbState} from './brbState';
166
168
  import MultistreamNotSupportedError from '../common/errors/multistream-not-supported-error';
167
169
  import JoinForbiddenError from '../common/errors/join-forbidden-error';
168
170
 
@@ -255,6 +257,7 @@ export enum ScreenShareFloorStatus {
255
257
 
256
258
  type FetchMeetingInfoParams = {
257
259
  password?: string;
260
+ registrationId?: string;
258
261
  captchaCode?: string;
259
262
  extraParams?: Record<string, any>;
260
263
  sendCAevents?: boolean;
@@ -649,6 +652,8 @@ export default class Meeting extends StatelessWebexPlugin {
649
652
  turnServerUsed: boolean;
650
653
  areVoiceaEventsSetup = false;
651
654
  isMoveToInProgress = false;
655
+ registrationIdStatus: string;
656
+ brbState: BrbState;
652
657
 
653
658
  voiceaListenerCallbacks: object = {
654
659
  [VOICEAEVENTS.VOICEA_ANNOUNCEMENT]: (payload: Transcription['languageOptions']) => {
@@ -1345,6 +1350,16 @@ export default class Meeting extends StatelessWebexPlugin {
1345
1350
  */
1346
1351
  this.passwordStatus = PASSWORD_STATUS.UNKNOWN;
1347
1352
 
1353
+ /**
1354
+ * registrationId status. If it's REGISTRATIONID_STATUS.REQUIRED then verifyRegistrationId() needs to be called
1355
+ * with the correct registrationId before calling join()
1356
+ * @instance
1357
+ * @type {REGISTRATION_ID_STATUS}
1358
+ * @public
1359
+ * @memberof Meeting
1360
+ */
1361
+ this.registrationIdStatus = REGISTRATION_ID_STATUS.UNKNOWN;
1362
+
1348
1363
  /**
1349
1364
  * Information about required captcha. If null, then no captcha is required. status. If it's PASSWORD_STATUS.REQUIRED then verifyPassword() needs to be called
1350
1365
  * with the correct password before calling join()
@@ -1657,6 +1672,15 @@ export default class Meeting extends StatelessWebexPlugin {
1657
1672
  this.passwordStatus = PASSWORD_STATUS.NOT_REQUIRED;
1658
1673
  }
1659
1674
 
1675
+ if (
1676
+ this.registrationIdStatus === REGISTRATION_ID_STATUS.REQUIRED ||
1677
+ this.registrationIdStatus === REGISTRATION_ID_STATUS.VERIFIED
1678
+ ) {
1679
+ this.registrationIdStatus = REGISTRATION_ID_STATUS.VERIFIED;
1680
+ } else {
1681
+ this.registrationIdStatus = REGISTRATION_ID_STATUS.NOT_REQUIRED;
1682
+ }
1683
+
1660
1684
  Trigger.trigger(
1661
1685
  this,
1662
1686
  {
@@ -1700,7 +1724,12 @@ export default class Meeting extends StatelessWebexPlugin {
1700
1724
  * @private
1701
1725
  */
1702
1726
  private prepForFetchMeetingInfo(
1703
- {password = null, captchaCode = null, extraParams = {}}: FetchMeetingInfoParams,
1727
+ {
1728
+ password = null,
1729
+ registrationId = null,
1730
+ captchaCode = null,
1731
+ extraParams = {},
1732
+ }: FetchMeetingInfoParams,
1704
1733
  caller: string
1705
1734
  ): Promise<void> {
1706
1735
  // when fetch meeting info is called directly by the client, we want to clear out the random timer for sdk to do it
@@ -1740,6 +1769,7 @@ export default class Meeting extends StatelessWebexPlugin {
1740
1769
  captchaCode = null,
1741
1770
  extraParams = {},
1742
1771
  sendCAevents = false,
1772
+ registrationId = null,
1743
1773
  }): Promise<void> {
1744
1774
  try {
1745
1775
  const captchaInfo = captchaCode
@@ -1755,7 +1785,8 @@ export default class Meeting extends StatelessWebexPlugin {
1755
1785
  this.config.installedOrgID,
1756
1786
  this.locusId,
1757
1787
  extraParams,
1758
- {meetingId: this.id, sendCAevents}
1788
+ {meetingId: this.id, sendCAevents},
1789
+ registrationId
1759
1790
  );
1760
1791
 
1761
1792
  this.parseMeetingInfo(info?.body, this.destination, info?.errors);
@@ -1777,14 +1808,15 @@ export default class Meeting extends StatelessWebexPlugin {
1777
1808
  this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION;
1778
1809
  if (WEBINAR_ERROR_WEBCAST.includes(err.wbxAppApiCode)) {
1779
1810
  this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NEED_JOIN_WITH_WEBCAST;
1780
- } else if (WEBINAR_ERROR_REGISTRATIONID.includes(err.wbxAppApiCode)) {
1781
- this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_NEED_REGISTRATIONID;
1811
+ } else if (WEBINAR_ERROR_REGISTRATION_ID.includes(err.wbxAppApiCode)) {
1812
+ this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_NEED_REGISTRATION_ID;
1782
1813
  }
1783
1814
  this.meetingInfoFailureCode = err.wbxAppApiCode;
1784
1815
 
1785
1816
  if (err.meetingInfo) {
1786
1817
  this.meetingInfo = err.meetingInfo;
1787
1818
  }
1819
+ this.requiredCaptcha = null;
1788
1820
 
1789
1821
  throw new JoinWebinarError();
1790
1822
  } else if (err instanceof MeetingInfoV2JoinForbiddenError) {
@@ -1829,9 +1861,13 @@ export default class Meeting extends StatelessWebexPlugin {
1829
1861
  `Meeting:index#fetchMeetingInfo --> Info Unable to fetch meeting info for ${this.destination} - captcha required (code=${err?.body?.code}).`
1830
1862
  );
1831
1863
 
1832
- this.meetingInfoFailureReason = this.requiredCaptcha
1833
- ? MEETING_INFO_FAILURE_REASON.WRONG_CAPTCHA
1834
- : MEETING_INFO_FAILURE_REASON.WRONG_PASSWORD;
1864
+ if (this.requiredCaptcha) {
1865
+ this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WRONG_CAPTCHA;
1866
+ } else if (err.isRegistrationIdRequired) {
1867
+ this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WRONG_REGISTRATION_ID;
1868
+ } else {
1869
+ this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WRONG_PASSWORD;
1870
+ }
1835
1871
 
1836
1872
  this.meetingInfoFailureCode = err.wbxAppApiCode;
1837
1873
 
@@ -1839,6 +1875,10 @@ export default class Meeting extends StatelessWebexPlugin {
1839
1875
  this.passwordStatus = PASSWORD_STATUS.REQUIRED;
1840
1876
  }
1841
1877
 
1878
+ if (err.isRegistrationIdRequired) {
1879
+ this.registrationIdStatus = REGISTRATION_ID_STATUS.REQUIRED;
1880
+ }
1881
+
1842
1882
  this.requiredCaptcha = err.captchaInfo;
1843
1883
  throw new CaptchaError();
1844
1884
  } else {
@@ -1979,6 +2019,48 @@ export default class Meeting extends StatelessWebexPlugin {
1979
2019
  });
1980
2020
  }
1981
2021
 
2022
+ /**
2023
+ * Checks if the supplied registrationId is correct. It returns a promise with information whether the
2024
+ * registrationId and captcha code were correct or not.
2025
+ * @param {String | undefined} registrationId - can be undefined if only captcha was required
2026
+ * @param {String | undefined} captchaCode - can be undefined if captcha was not required by the server
2027
+ * @param {Boolean} sendCAevents - whether Call Analyzer events should be sent when fetching meeting information
2028
+ * @public
2029
+ * @memberof Meeting
2030
+ * @returns {Promise<{isRegistrationIdValid: boolean, requiredCaptcha: boolean, failureReason: MEETING_INFO_FAILURE_REASON}>}
2031
+ */
2032
+ public verifyRegistrationId(registrationId: string, captchaCode: string, sendCAevents = false) {
2033
+ return this.fetchMeetingInfo({
2034
+ registrationId,
2035
+ captchaCode,
2036
+ sendCAevents,
2037
+ })
2038
+ .then(() => {
2039
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.VERIFY_REGISTRATION_ID_SUCCESS);
2040
+
2041
+ return {
2042
+ isRegistrationIdValid: true,
2043
+ requiredCaptcha: null,
2044
+ failureReason: MEETING_INFO_FAILURE_REASON.NONE,
2045
+ };
2046
+ })
2047
+ .catch((error) => {
2048
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.VERIFY_REGISTRATION_ID_ERROR);
2049
+
2050
+ if (error instanceof JoinWebinarError || error instanceof CaptchaError) {
2051
+ return {
2052
+ isRegistrationIdValid: this.registrationIdStatus === REGISTRATION_ID_STATUS.VERIFIED,
2053
+ requiredCaptcha: this.requiredCaptcha,
2054
+ failureReason:
2055
+ error instanceof JoinWebinarError
2056
+ ? MEETING_INFO_FAILURE_REASON.WRONG_REGISTRATION_ID
2057
+ : this.meetingInfoFailureReason,
2058
+ };
2059
+ }
2060
+ throw error;
2061
+ });
2062
+ }
2063
+
1982
2064
  /**
1983
2065
  * Refreshes the captcha. As a result the meeting will have new captcha id, image and audio.
1984
2066
  * If the refresh operation fails, meeting remains with the old captcha properties.
@@ -3349,6 +3431,10 @@ export default class Meeting extends StatelessWebexPlugin {
3349
3431
  // The second on is if the audio is muted, we need to tell the statsAnalyzer when
3350
3432
  // the audio is muted or the user is not willing to send media
3351
3433
  this.locusInfo.on(LOCUSINFO.EVENTS.MEDIA_STATUS_CHANGE, (status) => {
3434
+ LoggerProxy.logger.info(
3435
+ 'Meeting:index#setUpLocusInfoSelfListener --> MEDIA_STATUS_CHANGE received, processing...'
3436
+ );
3437
+
3352
3438
  if (this.statsAnalyzer) {
3353
3439
  this.statsAnalyzer.updateMediaStatus({
3354
3440
  actual: status,
@@ -3362,6 +3448,10 @@ export default class Meeting extends StatelessWebexPlugin {
3362
3448
  receiveShare: this.mediaProperties.mediaDirection?.receiveShare,
3363
3449
  },
3364
3450
  });
3451
+ } else {
3452
+ LoggerProxy.logger.warn(
3453
+ 'Meeting:index#setUpLocusInfoSelfListener --> MEDIA_STATUS_CHANGE, statsAnalyzer is not available.'
3454
+ );
3365
3455
  }
3366
3456
  });
3367
3457
 
@@ -3407,6 +3497,7 @@ export default class Meeting extends StatelessWebexPlugin {
3407
3497
  });
3408
3498
 
3409
3499
  this.locusInfo.on(LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED, (payload) => {
3500
+ this.brbState?.handleServerBrbUpdate(payload?.brb?.enabled);
3410
3501
  Trigger.trigger(
3411
3502
  this,
3412
3503
  {
@@ -3650,22 +3741,7 @@ export default class Meeting extends StatelessWebexPlugin {
3650
3741
  return Promise.reject(error);
3651
3742
  }
3652
3743
 
3653
- // this logic should be applied only to multistream meetings
3654
- return this.meetingRequest
3655
- .setBrb({
3656
- enabled,
3657
- locusUrl: this.locusUrl,
3658
- deviceUrl: this.deviceUrl,
3659
- selfId: this.selfId,
3660
- })
3661
- .then(() => {
3662
- this.sendSlotManager.setSourceStateOverride(MediaType.VideoMain, enabled ? 'away' : null);
3663
- })
3664
- .catch((error) => {
3665
- LoggerProxy.logger.error('Meeting:index#beRightBack --> Error ', error);
3666
-
3667
- return Promise.reject(error);
3668
- });
3744
+ return this.brbState.enable(enabled, this.sendSlotManager);
3669
3745
  }
3670
3746
 
3671
3747
  /**
@@ -6099,9 +6175,12 @@ export default class Meeting extends StatelessWebexPlugin {
6099
6175
  * @returns {undefined}
6100
6176
  */
6101
6177
  public roapMessageReceived = (roapMessage: RoapMessage) => {
6102
- const mediaServer = MeetingsUtil.getMediaServer(roapMessage.sdp);
6178
+ const mediaServer =
6179
+ roapMessage.messageType === 'ANSWER'
6180
+ ? MeetingsUtil.getMediaServer(roapMessage.sdp)
6181
+ : undefined;
6103
6182
 
6104
- if (this.isMultistream && mediaServer !== 'homer') {
6183
+ if (this.isMultistream && mediaServer && mediaServer !== 'homer') {
6105
6184
  throw new MultistreamNotSupportedError(
6106
6185
  `Client asked for multistream backend (Homer), but got ${mediaServer} instead`
6107
6186
  );
@@ -6716,6 +6795,9 @@ export default class Meeting extends StatelessWebexPlugin {
6716
6795
  new RtcMetrics(this.webex, {meetingId: this.id}, this.correlationId)
6717
6796
  : undefined;
6718
6797
 
6798
+ // ongoing reachability checks slow down new media connections especially on Firefox, so we stop them
6799
+ this.getWebexObject().meetings.reachability.stopReachability();
6800
+
6719
6801
  const mc = Media.createMediaConnection(
6720
6802
  this.isMultistream,
6721
6803
  this.getMediaConnectionDebugId(),
@@ -7432,6 +7514,7 @@ export default class Meeting extends StatelessWebexPlugin {
7432
7514
 
7433
7515
  this.audio = createMuteState(AUDIO, this, audioEnabled);
7434
7516
  this.video = createMuteState(VIDEO, this, videoEnabled);
7517
+ this.brbState = createBrbState(this, false);
7435
7518
 
7436
7519
  try {
7437
7520
  await this.setUpLocalStreamReferences(localStreams);
@@ -7467,6 +7550,9 @@ export default class Meeting extends StatelessWebexPlugin {
7467
7550
  throw error;
7468
7551
  }
7469
7552
  }
7553
+
7554
+ LoggerProxy.logger.info(`${LOG_HEADER} media connected, finalizing...`);
7555
+
7470
7556
  if (this.mediaProperties.hasLocalShareStream()) {
7471
7557
  await this.enqueueScreenShareFloorRequest();
7472
7558
  }
@@ -379,12 +379,7 @@ export class MuteState {
379
379
  }
380
380
  if (muted !== undefined) {
381
381
  this.state.server.remoteMute = muted;
382
-
383
- // We never want to unmute the local stream from a server remote mute update.
384
- // Moderated unmute is handled by a different function.
385
- if (muted) {
386
- this.muteLocalStream(meeting, muted, 'remotelyMuted');
387
- }
382
+ this.muteLocalStream(meeting, muted, 'remotelyMuted');
388
383
  }
389
384
  }
390
385
 
@@ -17,6 +17,8 @@ const CAPTCHA_ERROR_DEFAULT_MESSAGE =
17
17
  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
+ const CAPTCHA_ERROR_REQUIRES_REGISTRATION_ID_CODES = [423007];
21
+
20
22
  const POLICY_ERROR_CODES = [403049, 403104, 403103, 403048, 403102, 403101];
21
23
  const JOIN_FORBIDDEN_CODES = [403003];
22
24
  /**
@@ -113,6 +115,7 @@ export class MeetingInfoV2PolicyError extends Error {
113
115
  export class MeetingInfoV2CaptchaError extends Error {
114
116
  captchaInfo: any;
115
117
  isPasswordRequired: any;
118
+ isRegistrationIdRequired: any;
116
119
  sdkMessage: any;
117
120
  wbxAppApiCode: any;
118
121
  body: any;
@@ -134,6 +137,8 @@ export class MeetingInfoV2CaptchaError extends Error {
134
137
  this.stack = new Error().stack;
135
138
  this.wbxAppApiCode = wbxAppApiErrorCode;
136
139
  this.isPasswordRequired = CAPTCHA_ERROR_REQUIRES_PASSWORD_CODES.includes(wbxAppApiErrorCode);
140
+ this.isRegistrationIdRequired =
141
+ CAPTCHA_ERROR_REQUIRES_REGISTRATION_ID_CODES.includes(wbxAppApiErrorCode);
137
142
  this.captchaInfo = captchaInfo;
138
143
  }
139
144
  }
@@ -370,6 +375,7 @@ export default class MeetingInfoV2 {
370
375
  * @param {String} locusId
371
376
  * @param {Object} extraParams
372
377
  * @param {Object} options
378
+ * @param {String} registrationId
373
379
  * @returns {Promise} returns a meeting info object
374
380
  * @public
375
381
  * @memberof MeetingInfo
@@ -385,7 +391,8 @@ export default class MeetingInfoV2 {
385
391
  installedOrgID = null,
386
392
  locusId = null,
387
393
  extraParams: object = {},
388
- options: {meetingId?: string; sendCAevents?: boolean} = {}
394
+ options: {meetingId?: string; sendCAevents?: boolean} = {},
395
+ registrationId: string = null
389
396
  ) {
390
397
  const {meetingId, sendCAevents} = options;
391
398
 
@@ -410,6 +417,7 @@ export default class MeetingInfoV2 {
410
417
  installedOrgID,
411
418
  locusId,
412
419
  extraParams,
420
+ registrationId,
413
421
  });
414
422
 
415
423
  // If the body only contains the default properties, we don't have enough to
@@ -228,8 +228,16 @@ export default class MeetingInfoUtil {
228
228
  * @returns {Object} returns an object with {resource, method}
229
229
  */
230
230
  static getRequestBody(options: {type: DESTINATION_TYPE; destination: object} | any) {
231
- const {type, destination, password, captchaInfo, installedOrgID, locusId, extraParams} =
232
- options;
231
+ const {
232
+ type,
233
+ destination,
234
+ password,
235
+ captchaInfo,
236
+ installedOrgID,
237
+ locusId,
238
+ extraParams,
239
+ registrationId,
240
+ } = options;
233
241
  const body: any = {
234
242
  ...DEFAULT_MEETING_INFO_REQUEST_BODY,
235
243
  ...extraParams,
@@ -271,6 +279,10 @@ export default class MeetingInfoUtil {
271
279
  body.password = password;
272
280
  }
273
281
 
282
+ if (registrationId) {
283
+ body.registrationId = registrationId;
284
+ }
285
+
274
286
  if (captchaInfo) {
275
287
  body.captchaID = captchaInfo.id;
276
288
  body.captchaVerifyCode = captchaInfo.code;
@@ -73,6 +73,8 @@ const BEHAVIORAL_METRICS = {
73
73
  JOIN_WEBINAR_ERROR: 'js_sdk_join_webinar_error',
74
74
  GUEST_ENTERED_LOBBY: 'js_sdk_guest_entered_lobby',
75
75
  GUEST_EXITED_LOBBY: 'js_sdk_guest_exited_lobby',
76
+ VERIFY_REGISTRATION_ID_SUCCESS: 'js_sdk_verify_registrationId_success',
77
+ VERIFY_REGISTRATION_ID_ERROR: 'js_sdk_verify_registrationId_error',
76
78
  JOIN_FORBIDDEN_ERROR: 'js_sdk_join_forbidden_error',
77
79
  };
78
80
 
@@ -259,6 +259,32 @@ export default class Reachability extends EventsScope {
259
259
  }
260
260
  }
261
261
 
262
+ /**
263
+ * Stops all reachability checks that are in progress
264
+ * @public
265
+ * @memberof Reachability
266
+ * @returns {void}
267
+ */
268
+ public stopReachability() {
269
+ // overallTimer is always there only if there is reachability in progress
270
+ if (this.overallTimer) {
271
+ LoggerProxy.logger.log(
272
+ 'Reachability:index#stopReachability --> stopping reachability checks'
273
+ );
274
+ this.abortCurrentChecks();
275
+ this.emit(
276
+ {
277
+ file: 'reachability',
278
+ function: 'stopReachability',
279
+ },
280
+ 'reachability:stopped',
281
+ {}
282
+ );
283
+ this.sendMetric(true);
284
+ this.resolveReachabilityPromise();
285
+ }
286
+ }
287
+
262
288
  /**
263
289
  * Returns statistics about last reachability results. The returned value is an object
264
290
  * with a flat list of properties so that it can be easily sent with metrics
@@ -637,9 +663,10 @@ export default class Reachability extends EventsScope {
637
663
  /**
638
664
  * Sends a metric with all the statistics about how long reachability took
639
665
  *
666
+ * @param {boolean} aborted true if the reachability checks were aborted
640
667
  * @returns {void}
641
668
  */
642
- protected async sendMetric() {
669
+ protected async sendMetric(aborted = false) {
643
670
  const results = [];
644
671
 
645
672
  Object.values(this.clusterReachability).forEach((clusterReachability) => {
@@ -650,6 +677,7 @@ export default class Reachability extends EventsScope {
650
677
  });
651
678
 
652
679
  const stats = {
680
+ aborted,
653
681
  vmn: {
654
682
  udp: this.getStatistics(results, 'udp', true),
655
683
  },
@@ -450,6 +450,16 @@ describe('plugin-meetings', () => {
450
450
  assert.equal(SelfUtils.mutedByOthersChanged(null, {remoteMuted: true}), true);
451
451
  });
452
452
 
453
+ it('should return false when selfIdentity and modifiedBy are the same', function () {
454
+ assert.equal(
455
+ SelfUtils.mutedByOthersChanged(
456
+ {remoteMuted: false},
457
+ {remoteMuted: true, selfIdentity: 'user1', modifiedBy: 'user1'}
458
+ ),
459
+ false
460
+ );
461
+ });
462
+
453
463
  it('should return true when remoteMuted values are different', function () {
454
464
  assert.equal(
455
465
  SelfUtils.mutedByOthersChanged(
@@ -66,6 +66,21 @@ describe('MediaProperties', () => {
66
66
  assert.equal(numTransports, 0);
67
67
  });
68
68
 
69
+ it('handles time out in the case when getStats() is not resolving', async () => {
70
+ // Promise that never resolves
71
+ mockMC.getStats = new Promise(() => {});
72
+
73
+ const promise = mediaProperties.getCurrentConnectionInfo();
74
+
75
+ await clock.tickAsync(1000);
76
+
77
+ const {connectionType, selectedCandidatePairChanges, numTransports} = await promise;
78
+
79
+ assert.equal(connectionType, 'unknown');
80
+ assert.equal(selectedCandidatePairChanges, -1);
81
+ assert.equal(numTransports, 0);
82
+ });
83
+
69
84
  describe('selectedCandidatePairChanges and numTransports', () => {
70
85
  it('returns correct values when getStats() returns no transport stats at all', async () => {
71
86
  mockMC.getStats.resolves([{type: 'something', id: '1234'}]);
@@ -0,0 +1,114 @@
1
+ import sinon from 'sinon';
2
+ import {assert} from '@webex/test-helper-chai';
3
+
4
+ import testUtils from '../../../utils/testUtils';
5
+ import {BrbState, createBrbState} from '@webex/plugin-meetings/src/meeting/brbState';
6
+
7
+ describe('plugin-meetings', () => {
8
+ let meeting: any;
9
+ let brbState: BrbState;
10
+
11
+ beforeEach(async () => {
12
+ meeting = {
13
+ isMultistream: true,
14
+ locusUrl: 'locus url',
15
+ deviceUrl: 'device url',
16
+ selfId: 'self id',
17
+ mediaProperties: {
18
+ webrtcMediaConnection: true,
19
+ },
20
+ sendSlotManager: {
21
+ setSourceStateOverride: sinon.stub(),
22
+ },
23
+ meetingRequest: {
24
+ setBrb: sinon.stub().resolves(),
25
+ },
26
+ };
27
+
28
+ brbState = new BrbState(meeting, false);
29
+ await testUtils.flushPromises();
30
+ });
31
+
32
+ describe('brbState library', () => {
33
+ it('takes into account current status when instantiated', async () => {
34
+ // create a new BrbState instance
35
+ brbState = createBrbState(meeting, true);
36
+ await testUtils.flushPromises();
37
+
38
+ assert.isTrue(brbState.state.client.enabled);
39
+
40
+ // now check the opposite case
41
+ brbState = createBrbState(meeting, false);
42
+ await testUtils.flushPromises();
43
+
44
+ assert.isFalse(brbState.state.client.enabled);
45
+ });
46
+
47
+ it('can be enabled', async () => {
48
+ brbState.enable(true, meeting.sendSlotManager);
49
+ brbState.handleServerBrbUpdate(true);
50
+ await testUtils.flushPromises();
51
+
52
+ assert.isTrue(brbState.state.client.enabled);
53
+ assert.isTrue(brbState.state.server.enabled);
54
+ });
55
+
56
+ it('can be disabled', async () => {
57
+ brbState.enable(false, meeting.sendSlotManager);
58
+ brbState.handleServerBrbUpdate(false);
59
+ await testUtils.flushPromises();
60
+
61
+ assert.isFalse(brbState.state.client.enabled);
62
+ assert.isFalse(brbState.state.server.enabled);
63
+ });
64
+
65
+ it('does not send local brb state to server if it is not a multistream meeting', async () => {
66
+ meeting.isMultistream = false;
67
+ brbState.enable(true, meeting.sendSlotManager);
68
+ brbState.handleServerBrbUpdate(true);
69
+ await testUtils.flushPromises();
70
+
71
+ assert.isTrue(meeting.meetingRequest.setBrb.notCalled);
72
+ });
73
+
74
+ it('does not send local brb state to server if webrtc media connection is not defined', async () => {
75
+ meeting.mediaProperties.webrtcMediaConnection = undefined;
76
+ brbState.enable(true, meeting.sendSlotManager);
77
+ brbState.handleServerBrbUpdate(true);
78
+ await testUtils.flushPromises();
79
+
80
+ assert.isTrue(meeting.meetingRequest.setBrb.notCalled);
81
+ });
82
+
83
+ it('does not send request twice when in progress', async () => {
84
+ brbState.state.syncToServerInProgress = true;
85
+ brbState.enable(true, meeting.sendSlotManager);
86
+ await testUtils.flushPromises();
87
+
88
+ assert.isTrue(meeting.meetingRequest.setBrb.notCalled);
89
+ });
90
+
91
+ it('syncs with server when client state does not match server state', async () => {
92
+ brbState.enable(true, meeting.sendSlotManager);
93
+ brbState.handleServerBrbUpdate(true);
94
+ await testUtils.flushPromises();
95
+
96
+ assert.isTrue(meeting.meetingRequest.setBrb.calledOnce);
97
+ });
98
+
99
+ it('sets source state override when client state does not match server state', async () => {
100
+ brbState.enable(true, meeting.sendSlotManager);
101
+ brbState.handleServerBrbUpdate(true);
102
+ await testUtils.flushPromises();
103
+
104
+ assert.isTrue(meeting.sendSlotManager.setSourceStateOverride.calledOnce);
105
+ });
106
+
107
+ it('handles server update', async () => {
108
+ brbState.handleServerBrbUpdate(true);
109
+ await testUtils.flushPromises();
110
+
111
+ assert.isTrue(brbState.state.server.enabled);
112
+ });
113
+ });
114
+ });