@webex/plugin-meetings 3.0.0-beta.24 → 3.0.0-beta.241

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 (360) hide show
  1. package/README.md +46 -8
  2. package/dist/annotation/annotation.types.js +7 -0
  3. package/dist/annotation/annotation.types.js.map +1 -0
  4. package/dist/annotation/constants.js +49 -0
  5. package/dist/annotation/constants.js.map +1 -0
  6. package/dist/annotation/index.js +342 -0
  7. package/dist/annotation/index.js.map +1 -0
  8. package/dist/breakouts/breakout.js +114 -14
  9. package/dist/breakouts/breakout.js.map +1 -1
  10. package/dist/breakouts/edit-lock-error.js +52 -0
  11. package/dist/breakouts/edit-lock-error.js.map +1 -0
  12. package/dist/breakouts/events.js +45 -0
  13. package/dist/breakouts/events.js.map +1 -0
  14. package/dist/breakouts/index.js +841 -19
  15. package/dist/breakouts/index.js.map +1 -1
  16. package/dist/breakouts/request.js +78 -0
  17. package/dist/breakouts/request.js.map +1 -0
  18. package/dist/breakouts/utils.js +67 -0
  19. package/dist/breakouts/utils.js.map +1 -0
  20. package/dist/common/errors/webex-errors.js +28 -7
  21. package/dist/common/errors/webex-errors.js.map +1 -1
  22. package/dist/common/logs/logger-proxy.js +1 -1
  23. package/dist/common/logs/logger-proxy.js.map +1 -1
  24. package/dist/common/queue.js +24 -9
  25. package/dist/common/queue.js.map +1 -1
  26. package/dist/config.js +5 -10
  27. package/dist/config.js.map +1 -1
  28. package/dist/constants.js +190 -27
  29. package/dist/constants.js.map +1 -1
  30. package/dist/controls-options-manager/constants.js +14 -0
  31. package/dist/controls-options-manager/constants.js.map +1 -0
  32. package/dist/controls-options-manager/enums.js +27 -0
  33. package/dist/controls-options-manager/enums.js.map +1 -0
  34. package/dist/controls-options-manager/index.js +297 -0
  35. package/dist/controls-options-manager/index.js.map +1 -0
  36. package/dist/controls-options-manager/types.js +7 -0
  37. package/dist/controls-options-manager/types.js.map +1 -0
  38. package/dist/controls-options-manager/util.js +319 -0
  39. package/dist/controls-options-manager/util.js.map +1 -0
  40. package/dist/index.js +106 -1
  41. package/dist/index.js.map +1 -1
  42. package/dist/interpretation/collection.js +23 -0
  43. package/dist/interpretation/collection.js.map +1 -0
  44. package/dist/interpretation/index.js +366 -0
  45. package/dist/interpretation/index.js.map +1 -0
  46. package/dist/interpretation/siLanguage.js +25 -0
  47. package/dist/interpretation/siLanguage.js.map +1 -0
  48. package/dist/locus-info/controlsUtils.js +91 -2
  49. package/dist/locus-info/controlsUtils.js.map +1 -1
  50. package/dist/locus-info/index.js +357 -62
  51. package/dist/locus-info/index.js.map +1 -1
  52. package/dist/locus-info/infoUtils.js +7 -1
  53. package/dist/locus-info/infoUtils.js.map +1 -1
  54. package/dist/locus-info/mediaSharesUtils.js +43 -1
  55. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  56. package/dist/locus-info/parser.js +219 -63
  57. package/dist/locus-info/parser.js.map +1 -1
  58. package/dist/locus-info/selfUtils.js +89 -14
  59. package/dist/locus-info/selfUtils.js.map +1 -1
  60. package/dist/media/index.js +49 -106
  61. package/dist/media/index.js.map +1 -1
  62. package/dist/media/properties.js +29 -90
  63. package/dist/media/properties.js.map +1 -1
  64. package/dist/meeting/in-meeting-actions.js +90 -2
  65. package/dist/meeting/in-meeting-actions.js.map +1 -1
  66. package/dist/meeting/index.js +2593 -2558
  67. package/dist/meeting/index.js.map +1 -1
  68. package/dist/meeting/locusMediaRequest.js +292 -0
  69. package/dist/meeting/locusMediaRequest.js.map +1 -0
  70. package/dist/meeting/muteState.js +228 -123
  71. package/dist/meeting/muteState.js.map +1 -1
  72. package/dist/meeting/request.js +255 -195
  73. package/dist/meeting/request.js.map +1 -1
  74. package/dist/meeting/util.js +601 -417
  75. package/dist/meeting/util.js.map +1 -1
  76. package/dist/meeting-info/index.js +48 -7
  77. package/dist/meeting-info/index.js.map +1 -1
  78. package/dist/meeting-info/meeting-info-v2.js +171 -51
  79. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  80. package/dist/meeting-info/util.js +1 -1
  81. package/dist/meeting-info/util.js.map +1 -1
  82. package/dist/meeting-info/utilv2.js +36 -36
  83. package/dist/meeting-info/utilv2.js.map +1 -1
  84. package/dist/meetings/collection.js +22 -0
  85. package/dist/meetings/collection.js.map +1 -1
  86. package/dist/meetings/index.js +370 -90
  87. package/dist/meetings/index.js.map +1 -1
  88. package/dist/meetings/meetings.types.js +7 -0
  89. package/dist/meetings/meetings.types.js.map +1 -0
  90. package/dist/meetings/request.js +2 -0
  91. package/dist/meetings/request.js.map +1 -1
  92. package/dist/meetings/util.js +88 -1
  93. package/dist/meetings/util.js.map +1 -1
  94. package/dist/member/index.js +49 -0
  95. package/dist/member/index.js.map +1 -1
  96. package/dist/member/types.js +25 -0
  97. package/dist/member/types.js.map +1 -0
  98. package/dist/member/util.js +121 -25
  99. package/dist/member/util.js.map +1 -1
  100. package/dist/members/collection.js +10 -0
  101. package/dist/members/collection.js.map +1 -1
  102. package/dist/members/index.js +86 -5
  103. package/dist/members/index.js.map +1 -1
  104. package/dist/members/request.js +106 -38
  105. package/dist/members/request.js.map +1 -1
  106. package/dist/members/types.js +15 -0
  107. package/dist/members/types.js.map +1 -0
  108. package/dist/members/util.js +316 -233
  109. package/dist/members/util.js.map +1 -1
  110. package/dist/metrics/constants.js +4 -5
  111. package/dist/metrics/constants.js.map +1 -1
  112. package/dist/metrics/index.js +1 -468
  113. package/dist/metrics/index.js.map +1 -1
  114. package/dist/multistream/mediaRequestManager.js +238 -49
  115. package/dist/multistream/mediaRequestManager.js.map +1 -1
  116. package/dist/multistream/receiveSlot.js +49 -16
  117. package/dist/multistream/receiveSlot.js.map +1 -1
  118. package/dist/multistream/receiveSlotManager.js +52 -34
  119. package/dist/multistream/receiveSlotManager.js.map +1 -1
  120. package/dist/multistream/remoteMedia.js +44 -18
  121. package/dist/multistream/remoteMedia.js.map +1 -1
  122. package/dist/multistream/remoteMediaGroup.js +60 -3
  123. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  124. package/dist/multistream/remoteMediaManager.js +213 -62
  125. package/dist/multistream/remoteMediaManager.js.map +1 -1
  126. package/dist/reachability/index.js +81 -30
  127. package/dist/reachability/index.js.map +1 -1
  128. package/dist/reachability/request.js +16 -7
  129. package/dist/reachability/request.js.map +1 -1
  130. package/dist/reconnection-manager/index.js +199 -154
  131. package/dist/reconnection-manager/index.js.map +1 -1
  132. package/dist/recording-controller/index.js +21 -2
  133. package/dist/recording-controller/index.js.map +1 -1
  134. package/dist/recording-controller/util.js +9 -8
  135. package/dist/recording-controller/util.js.map +1 -1
  136. package/dist/roap/index.js +23 -29
  137. package/dist/roap/index.js.map +1 -1
  138. package/dist/roap/request.js +112 -89
  139. package/dist/roap/request.js.map +1 -1
  140. package/dist/roap/turnDiscovery.js +96 -36
  141. package/dist/roap/turnDiscovery.js.map +1 -1
  142. package/dist/rtcMetrics/constants.js +12 -0
  143. package/dist/rtcMetrics/constants.js.map +1 -0
  144. package/dist/rtcMetrics/index.js +117 -0
  145. package/dist/rtcMetrics/index.js.map +1 -0
  146. package/dist/statsAnalyzer/index.js +51 -34
  147. package/dist/statsAnalyzer/index.js.map +1 -1
  148. package/dist/statsAnalyzer/mqaUtil.js +6 -6
  149. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  150. package/dist/types/annotation/annotation.types.d.ts +42 -0
  151. package/dist/types/annotation/constants.d.ts +31 -0
  152. package/dist/types/annotation/index.d.ts +117 -0
  153. package/dist/types/breakouts/edit-lock-error.d.ts +15 -0
  154. package/dist/types/breakouts/events.d.ts +8 -0
  155. package/dist/types/breakouts/request.d.ts +22 -0
  156. package/dist/types/breakouts/utils.d.ts +15 -0
  157. package/dist/types/common/errors/webex-errors.d.ts +13 -1
  158. package/dist/types/common/queue.d.ts +9 -7
  159. package/dist/types/config.d.ts +1 -6
  160. package/dist/types/constants.d.ts +154 -21
  161. package/dist/types/controls-options-manager/constants.d.ts +4 -0
  162. package/dist/types/controls-options-manager/enums.d.ts +15 -0
  163. package/dist/types/controls-options-manager/index.d.ts +136 -0
  164. package/dist/types/controls-options-manager/types.d.ts +43 -0
  165. package/dist/types/controls-options-manager/util.d.ts +1 -0
  166. package/dist/types/index.d.ts +6 -4
  167. package/dist/types/interpretation/collection.d.ts +5 -0
  168. package/dist/types/interpretation/index.d.ts +5 -0
  169. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  170. package/dist/types/locus-info/index.d.ts +57 -4
  171. package/dist/types/locus-info/parser.d.ts +65 -6
  172. package/dist/types/media/index.d.ts +2 -0
  173. package/dist/types/media/properties.d.ts +22 -36
  174. package/dist/types/meeting/in-meeting-actions.d.ts +90 -2
  175. package/dist/types/meeting/index.d.ts +297 -491
  176. package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
  177. package/dist/types/meeting/muteState.d.ts +98 -22
  178. package/dist/types/meeting/request.d.ts +72 -43
  179. package/dist/types/meeting/util.d.ts +101 -1
  180. package/dist/types/meeting-info/index.d.ts +6 -1
  181. package/dist/types/meeting-info/meeting-info-v2.d.ts +30 -1
  182. package/dist/types/meetings/collection.d.ts +8 -0
  183. package/dist/types/meetings/index.d.ts +76 -12
  184. package/dist/types/meetings/meetings.types.d.ts +4 -0
  185. package/dist/types/member/index.d.ts +13 -0
  186. package/dist/types/member/types.d.ts +32 -0
  187. package/dist/types/members/collection.d.ts +5 -0
  188. package/dist/types/members/index.d.ts +35 -2
  189. package/dist/types/members/request.d.ts +73 -9
  190. package/dist/types/members/types.d.ts +24 -0
  191. package/dist/types/members/util.d.ts +209 -1
  192. package/dist/types/metrics/constants.d.ts +3 -4
  193. package/dist/types/metrics/index.d.ts +4 -119
  194. package/dist/types/multistream/mediaRequestManager.d.ts +73 -5
  195. package/dist/types/multistream/receiveSlot.d.ts +16 -12
  196. package/dist/types/multistream/receiveSlotManager.d.ts +19 -4
  197. package/dist/types/multistream/remoteMedia.d.ts +8 -29
  198. package/dist/types/multistream/remoteMediaGroup.d.ts +0 -9
  199. package/dist/types/multistream/remoteMediaManager.d.ts +46 -2
  200. package/dist/types/reachability/index.d.ts +15 -3
  201. package/dist/types/reachability/request.d.ts +7 -3
  202. package/dist/types/reconnection-manager/index.d.ts +9 -0
  203. package/dist/types/recording-controller/index.d.ts +15 -1
  204. package/dist/types/recording-controller/util.d.ts +5 -4
  205. package/dist/types/roap/request.d.ts +15 -11
  206. package/dist/types/roap/turnDiscovery.d.ts +18 -1
  207. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  208. package/dist/types/rtcMetrics/index.d.ts +47 -0
  209. package/dist/types/statsAnalyzer/index.d.ts +6 -1
  210. package/package.json +23 -20
  211. package/src/annotation/annotation.types.ts +50 -0
  212. package/src/annotation/constants.ts +36 -0
  213. package/src/annotation/index.ts +328 -0
  214. package/src/breakouts/README.md +44 -14
  215. package/src/breakouts/breakout.ts +87 -9
  216. package/src/breakouts/edit-lock-error.ts +25 -0
  217. package/src/breakouts/events.ts +56 -0
  218. package/src/breakouts/index.ts +710 -10
  219. package/src/breakouts/request.ts +55 -0
  220. package/src/breakouts/utils.ts +57 -0
  221. package/src/common/errors/webex-errors.ts +27 -2
  222. package/src/common/logs/logger-proxy.ts +1 -1
  223. package/src/common/queue.ts +22 -8
  224. package/src/config.ts +4 -9
  225. package/src/constants.ts +175 -18
  226. package/src/controls-options-manager/constants.ts +5 -0
  227. package/src/controls-options-manager/enums.ts +18 -0
  228. package/src/controls-options-manager/index.ts +278 -0
  229. package/src/controls-options-manager/types.ts +59 -0
  230. package/src/controls-options-manager/util.ts +300 -0
  231. package/src/index.ts +39 -0
  232. package/src/interpretation/README.md +60 -0
  233. package/src/interpretation/collection.ts +19 -0
  234. package/src/interpretation/index.ts +332 -0
  235. package/src/interpretation/siLanguage.ts +18 -0
  236. package/src/locus-info/controlsUtils.ts +108 -0
  237. package/src/locus-info/index.ts +381 -59
  238. package/src/locus-info/infoUtils.ts +10 -2
  239. package/src/locus-info/mediaSharesUtils.ts +48 -0
  240. package/src/locus-info/parser.ts +224 -39
  241. package/src/locus-info/selfUtils.ts +81 -5
  242. package/src/media/index.ts +89 -109
  243. package/src/media/properties.ts +48 -87
  244. package/src/meeting/in-meeting-actions.ts +179 -3
  245. package/src/meeting/index.ts +2086 -2151
  246. package/src/meeting/locusMediaRequest.ts +313 -0
  247. package/src/meeting/muteState.ts +227 -130
  248. package/src/meeting/request.ts +171 -120
  249. package/src/meeting/util.ts +588 -395
  250. package/src/meeting-info/index.ts +54 -8
  251. package/src/meeting-info/meeting-info-v2.ts +148 -14
  252. package/src/meeting-info/util.ts +1 -1
  253. package/src/meeting-info/utilv2.ts +23 -23
  254. package/src/meetings/collection.ts +20 -0
  255. package/src/meetings/index.ts +407 -108
  256. package/src/meetings/meetings.types.ts +12 -0
  257. package/src/meetings/request.ts +2 -0
  258. package/src/meetings/util.ts +103 -4
  259. package/src/member/index.ts +49 -0
  260. package/src/member/types.ts +38 -0
  261. package/src/member/util.ts +127 -25
  262. package/src/members/collection.ts +8 -0
  263. package/src/members/index.ts +107 -6
  264. package/src/members/request.ts +97 -17
  265. package/src/members/types.ts +28 -0
  266. package/src/members/util.ts +319 -240
  267. package/src/metrics/constants.ts +3 -4
  268. package/src/metrics/index.ts +1 -490
  269. package/src/multistream/mediaRequestManager.ts +289 -79
  270. package/src/multistream/receiveSlot.ts +55 -18
  271. package/src/multistream/receiveSlotManager.ts +46 -24
  272. package/src/multistream/remoteMedia.ts +27 -2
  273. package/src/multistream/remoteMediaGroup.ts +59 -0
  274. package/src/multistream/remoteMediaManager.ts +150 -32
  275. package/src/reachability/index.ts +69 -17
  276. package/src/reachability/request.ts +16 -7
  277. package/src/reconnection-manager/index.ts +81 -54
  278. package/src/recording-controller/index.ts +20 -3
  279. package/src/recording-controller/util.ts +26 -9
  280. package/src/roap/index.ts +23 -30
  281. package/src/roap/request.ts +104 -95
  282. package/src/roap/turnDiscovery.ts +50 -25
  283. package/src/rtcMetrics/constants.ts +3 -0
  284. package/src/rtcMetrics/index.ts +100 -0
  285. package/src/statsAnalyzer/index.ts +73 -35
  286. package/src/statsAnalyzer/mqaUtil.ts +8 -10
  287. package/test/integration/spec/converged-space-meetings.js +233 -0
  288. package/test/integration/spec/journey.js +336 -259
  289. package/test/integration/spec/space-meeting.js +76 -3
  290. package/test/unit/spec/annotation/index.ts +418 -0
  291. package/test/unit/spec/breakouts/breakout.ts +142 -24
  292. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  293. package/test/unit/spec/breakouts/events.ts +89 -0
  294. package/test/unit/spec/breakouts/index.ts +1545 -48
  295. package/test/unit/spec/breakouts/request.ts +104 -0
  296. package/test/unit/spec/breakouts/utils.js +72 -0
  297. package/test/unit/spec/common/queue.js +31 -2
  298. package/test/unit/spec/controls-options-manager/index.js +287 -0
  299. package/test/unit/spec/controls-options-manager/util.js +582 -0
  300. package/test/unit/spec/fixture/locus.js +1 -0
  301. package/test/unit/spec/interpretation/collection.ts +15 -0
  302. package/test/unit/spec/interpretation/index.ts +589 -0
  303. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  304. package/test/unit/spec/locus-info/controlsUtils.js +316 -43
  305. package/test/unit/spec/locus-info/index.js +1169 -36
  306. package/test/unit/spec/locus-info/infoUtils.js +37 -15
  307. package/test/unit/spec/locus-info/mediaSharesUtils.ts +22 -0
  308. package/test/unit/spec/locus-info/parser.js +62 -22
  309. package/test/unit/spec/locus-info/selfConstant.js +27 -4
  310. package/test/unit/spec/locus-info/selfUtils.js +208 -17
  311. package/test/unit/spec/media/index.ts +138 -28
  312. package/test/unit/spec/meeting/in-meeting-actions.ts +89 -3
  313. package/test/unit/spec/meeting/index.js +3514 -1746
  314. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  315. package/test/unit/spec/meeting/muteState.js +370 -208
  316. package/test/unit/spec/meeting/request.js +440 -45
  317. package/test/unit/spec/meeting/utils.js +671 -54
  318. package/test/unit/spec/meeting-info/index.js +181 -0
  319. package/test/unit/spec/meeting-info/meetinginfov2.js +383 -5
  320. package/test/unit/spec/meeting-info/utilv2.js +21 -0
  321. package/test/unit/spec/meetings/collection.js +14 -0
  322. package/test/unit/spec/meetings/index.js +939 -150
  323. package/test/unit/spec/meetings/utils.js +206 -2
  324. package/test/unit/spec/member/index.js +58 -4
  325. package/test/unit/spec/member/util.js +479 -35
  326. package/test/unit/spec/members/index.js +319 -1
  327. package/test/unit/spec/members/request.js +206 -27
  328. package/test/unit/spec/members/utils.js +184 -0
  329. package/test/unit/spec/metrics/index.js +1 -50
  330. package/test/unit/spec/multistream/mediaRequestManager.ts +803 -162
  331. package/test/unit/spec/multistream/receiveSlot.ts +72 -13
  332. package/test/unit/spec/multistream/receiveSlotManager.ts +58 -28
  333. package/test/unit/spec/multistream/remoteMedia.ts +30 -0
  334. package/test/unit/spec/multistream/remoteMediaGroup.ts +266 -0
  335. package/test/unit/spec/multistream/remoteMediaManager.ts +326 -0
  336. package/test/unit/spec/reachability/index.ts +185 -7
  337. package/test/unit/spec/reachability/request.js +68 -0
  338. package/test/unit/spec/reconnection-manager/index.js +80 -6
  339. package/test/unit/spec/recording-controller/index.js +294 -218
  340. package/test/unit/spec/recording-controller/util.js +223 -96
  341. package/test/unit/spec/roap/index.ts +31 -51
  342. package/test/unit/spec/roap/request.ts +202 -85
  343. package/test/unit/spec/roap/turnDiscovery.ts +45 -10
  344. package/test/unit/spec/rtcMetrics/index.ts +68 -0
  345. package/test/unit/spec/stats-analyzer/index.js +29 -2
  346. package/test/utils/constants.js +9 -0
  347. package/test/utils/integrationTestUtils.js +46 -0
  348. package/test/utils/testUtils.js +0 -45
  349. package/test/utils/webex-config.js +4 -0
  350. package/test/utils/webex-test-users.js +6 -3
  351. package/dist/meeting/effectsState.js +0 -262
  352. package/dist/meeting/effectsState.js.map +0 -1
  353. package/dist/metrics/config.js +0 -299
  354. package/dist/metrics/config.js.map +0 -1
  355. package/dist/types/meeting/effectsState.d.ts +0 -42
  356. package/dist/types/metrics/config.d.ts +0 -178
  357. package/src/index.js +0 -16
  358. package/src/meeting/effectsState.ts +0 -211
  359. package/src/metrics/config.ts +0 -495
  360. package/test/unit/spec/meeting/effectsState.js +0 -285
@@ -1,9 +1,6 @@
1
- import {isEmpty} from 'lodash';
2
-
1
+ import {LocalCameraTrack, LocalMicrophoneTrack} from '@webex/media-helpers';
2
+ import {cloneDeep} from 'lodash';
3
3
  import {MeetingNotActiveError, UserNotJoinedError} from '../common/errors/webex-errors';
4
- import Metrics from '../metrics';
5
- import {eventType, trigger} from '../metrics/config';
6
- import Media from '../media';
7
4
  import LoggerProxy from '../common/logs/logger-proxy';
8
5
  import {
9
6
  INTENT_TO_JOIN,
@@ -13,491 +10,687 @@ import {
13
10
  PASSWORD_STATUS,
14
11
  DISPLAY_HINTS,
15
12
  FULL_STATE,
13
+ SELF_POLICY,
14
+ EVENT_TRIGGERS,
15
+ IP_VERSION,
16
16
  } from '../constants';
17
+ import BrowserDetection from '../common/browser-detection';
17
18
  import IntentToJoinError from '../common/errors/intent-to-join';
18
19
  import JoinMeetingError from '../common/errors/join-meeting';
19
20
  import ParameterError from '../common/errors/parameter';
20
21
  import PermissionError from '../common/errors/permission';
21
22
  import PasswordError from '../common/errors/password-error';
22
23
  import CaptchaError from '../common/errors/captcha-error';
24
+ import Trigger from '../common/events/trigger-proxy';
25
+
26
+ const MeetingUtil = {
27
+ parseLocusJoin: (response) => {
28
+ const parsed: any = {};
29
+
30
+ // First todo: add check for existance
31
+ parsed.locus = response.body.locus;
32
+ parsed.mediaConnections = response.body.mediaConnections;
33
+ parsed.locusUrl = parsed.locus.url;
34
+ parsed.locusId = parsed.locus.url.split('/').pop();
35
+ parsed.selfId = parsed.locus.self.id;
36
+
37
+ // we need mediaId before making roap calls
38
+ parsed.mediaConnections.forEach((mediaConnection) => {
39
+ if (mediaConnection.mediaId) {
40
+ parsed.mediaId = mediaConnection.mediaId;
41
+ }
42
+ });
43
+
44
+ return parsed;
45
+ },
46
+
47
+ remoteUpdateAudioVideo: (meeting, audioMuted?: boolean, videoMuted?: boolean) => {
48
+ const webex = meeting.getWebexObject();
49
+ if (!meeting) {
50
+ return Promise.reject(new ParameterError('You need a meeting object.'));
51
+ }
52
+
53
+ if (!meeting.locusMediaRequest) {
54
+ return Promise.reject(
55
+ new ParameterError(
56
+ 'You need a meeting with a media connection, call Meeting.addMedia() first.'
57
+ )
58
+ );
59
+ }
23
60
 
24
- const MeetingUtil: any = {};
61
+ // @ts-ignore
62
+ webex.internal.newMetrics.submitClientEvent({
63
+ name: 'client.locus.media.request',
64
+ options: {meetingId: meeting.id},
65
+ });
25
66
 
26
- MeetingUtil.parseLocusJoin = (response) => {
27
- const parsed: any = {};
67
+ return meeting.locusMediaRequest
68
+ .send({
69
+ type: 'LocalMute',
70
+ selfUrl: meeting.selfUrl,
71
+ mediaId: meeting.mediaId,
72
+ sequence: meeting.locusInfo.sequence,
73
+ muteOptions: {
74
+ audioMuted,
75
+ videoMuted,
76
+ },
77
+ })
78
+ .then((response) => {
79
+ // @ts-ignore
80
+ webex.internal.newMetrics.submitClientEvent({
81
+ name: 'client.locus.media.response',
82
+ options: {meetingId: meeting.id},
83
+ });
84
+
85
+ return response?.body?.locus;
86
+ });
87
+ },
28
88
 
29
- // First todo: add check for existance
30
- parsed.locus = response.body.locus;
31
- parsed.mediaConnections = response.body.mediaConnections;
32
- parsed.locusUrl = parsed.locus.url;
33
- parsed.locusId = parsed.locus.url.split('/').pop();
34
- parsed.selfId = parsed.locus.self.id;
89
+ hasOwner: (info) => info && info.owner,
35
90
 
36
- // we need mediaId before making roap calls
37
- parsed.mediaConnections.forEach((mediaConnection) => {
38
- if (mediaConnection.mediaId) {
39
- parsed.mediaId = mediaConnection.mediaId;
91
+ isOwnerSelf: (owner, selfId) => owner === selfId,
92
+
93
+ isPinOrGuest: (err) => err?.body?.errorCode && INTENT_TO_JOIN.includes(err.body.errorCode),
94
+
95
+ /**
96
+ * Returns the current state of knowledge about whether we are on an ipv4-only or ipv6-only or mixed (ipv4 and ipv6) network.
97
+ * The return value matches the possible values of "ipver" parameter used by the backend APIs.
98
+ *
99
+ * @param {Object} webex webex instance
100
+ * @returns {IP_VERSION|undefined} ipver value to be passed to the backend APIs or undefined if we should not pass any value to the backend
101
+ */
102
+ getIpVersion(webex: any): IP_VERSION | undefined {
103
+ const {supportsIpV4, supportsIpV6} = webex.internal.device.ipNetworkDetector;
104
+
105
+ if (BrowserDetection().isBrowser('firefox')) {
106
+ // our ipv6 solution relies on FQDN ICE candidates, but Firefox doesn't support them,
107
+ // see https://bugzilla.mozilla.org/show_bug.cgi?id=1713128
108
+ // so for Firefox we don't want the backend to activate the "ipv6 feature"
109
+ return undefined;
40
110
  }
41
- });
42
111
 
43
- return parsed;
44
- };
112
+ if (supportsIpV4 && supportsIpV6) {
113
+ return IP_VERSION.ipv4_and_ipv6;
114
+ }
45
115
 
46
- MeetingUtil.remoteUpdateAudioVideo = (audioMuted, videoMuted, meeting) => {
47
- if (!meeting) {
48
- return Promise.reject(new ParameterError('You need a meeting object.'));
49
- }
50
- const localMedias = Media.generateLocalMedias(meeting.mediaId, audioMuted, videoMuted);
116
+ if (supportsIpV4) {
117
+ return IP_VERSION.only_ipv4;
118
+ }
51
119
 
52
- if (isEmpty(localMedias)) {
53
- return Promise.reject(
54
- new ParameterError('You need a media id on the meeting to change remote audio.')
55
- );
56
- }
120
+ if (supportsIpV6) {
121
+ return IP_VERSION.only_ipv6;
122
+ }
57
123
 
58
- Metrics.postEvent({event: eventType.MEDIA_REQUEST, meeting});
124
+ return IP_VERSION.unknown;
125
+ },
59
126
 
60
- return meeting.meetingRequest
61
- .remoteAudioVideoToggle({
127
+ joinMeeting: (meeting, options) => {
128
+ if (!meeting) {
129
+ return Promise.reject(new ParameterError('You need a meeting object.'));
130
+ }
131
+ const webex = meeting.getWebexObject();
132
+
133
+ // @ts-ignore
134
+ webex.internal.newMetrics.submitClientEvent({
135
+ name: 'client.locus.join.request',
136
+ options: {meetingId: meeting.id},
137
+ });
138
+
139
+ // eslint-disable-next-line no-warning-comments
140
+ // TODO: check if the meeting is in JOINING state
141
+ // if Joining state termintate the request as user might click multiple times
142
+ return meeting.meetingRequest
143
+ .joinMeeting({
144
+ inviteeAddress: meeting.meetingJoinUrl || meeting.sipUri,
145
+ meetingNumber: meeting.meetingNumber,
146
+ deviceUrl: meeting.deviceUrl,
147
+ locusUrl: meeting.locusUrl,
148
+ locusClusterUrl: meeting.meetingInfo?.locusClusterUrl,
149
+ correlationId: meeting.correlationId,
150
+ roapMessage: options.roapMessage,
151
+ permissionToken: meeting.permissionToken,
152
+ resourceId: options.resourceId || null,
153
+ moderator: options.moderator,
154
+ pin: options.pin,
155
+ moveToResource: options.moveToResource,
156
+ preferTranscoding: !meeting.isMultistream,
157
+ asResourceOccupant: options.asResourceOccupant,
158
+ breakoutsSupported: options.breakoutsSupported,
159
+ locale: options.locale,
160
+ deviceCapabilities: options.deviceCapabilities,
161
+ liveAnnotationSupported: options.liveAnnotationSupported,
162
+ ipVersion: MeetingUtil.getIpVersion(meeting.getWebexObject()),
163
+ })
164
+ .then((res) => {
165
+ // @ts-ignore
166
+ webex.internal.newMetrics.submitClientEvent({
167
+ name: 'client.locus.join.response',
168
+ payload: {
169
+ trigger: 'loci-update',
170
+ identifiers: {
171
+ trackingId: res.headers.trackingid,
172
+ },
173
+ },
174
+ options: {
175
+ meetingId: meeting.id,
176
+ mediaConnections: res.body.mediaConnections,
177
+ },
178
+ });
179
+
180
+ return MeetingUtil.parseLocusJoin(res);
181
+ });
182
+ },
183
+
184
+ cleanUp: (meeting) => {
185
+ meeting.breakouts.cleanUp();
186
+ meeting.simultaneousInterpretation.cleanUp();
187
+
188
+ // make sure we send last metrics before we close the peerconnection
189
+ const stopStatsAnalyzer = meeting.statsAnalyzer
190
+ ? meeting.statsAnalyzer.stopAnalyzer()
191
+ : Promise.resolve();
192
+
193
+ return stopStatsAnalyzer
194
+ .then(() => meeting.closeRemoteTracks())
195
+ .then(() => meeting.closePeerConnections())
196
+ .then(() => {
197
+ meeting.cleanupLocalTracks();
198
+ meeting.unsetRemoteTracks();
199
+ meeting.unsetPeerConnections();
200
+ meeting.reconnectionManager.cleanUp();
201
+ })
202
+ .then(() => meeting.stopKeepAlive())
203
+ .then(() => meeting.updateLLMConnection());
204
+ },
205
+
206
+ disconnectPhoneAudio: (meeting, phoneUrl) => {
207
+ if (meeting.meetingState === FULL_STATE.INACTIVE) {
208
+ return Promise.reject(new MeetingNotActiveError());
209
+ }
210
+
211
+ const options = {
62
212
  locusUrl: meeting.locusUrl,
63
213
  selfId: meeting.selfId,
64
- localMedias,
65
- deviceUrl: meeting.deviceUrl,
66
214
  correlationId: meeting.correlationId,
67
- preferTranscoding: !meeting.isMultistream,
68
- })
69
- .then((response) => {
70
- Metrics.postEvent({event: eventType.MEDIA_RESPONSE, meeting});
215
+ phoneUrl,
216
+ };
71
217
 
72
- return response.body.locus;
218
+ return meeting.meetingRequest.disconnectPhoneAudio(options).catch((err) => {
219
+ LoggerProxy.logger.error(
220
+ `Meeting:util#disconnectPhoneAudio --> An error occured while disconnecting phone audio in meeting ${meeting.id}, error: ${err}`
221
+ );
222
+
223
+ return Promise.reject(err);
73
224
  });
74
- };
225
+ },
226
+
227
+ /**
228
+ * Returns options for leaving a meeting.
229
+ * @param {any} meeting
230
+ * @param {any} options
231
+ * @returns {any} leave options
232
+ */
233
+ prepareLeaveMeetingOptions: (meeting, options: any = {}) => {
234
+ const defaultOptions = {
235
+ locusUrl: meeting.locusUrl,
236
+ selfId: meeting.selfId,
237
+ correlationId: meeting.correlationId,
238
+ resourceId: meeting.resourceId,
239
+ deviceUrl: meeting.deviceUrl,
240
+ };
241
+
242
+ return {...defaultOptions, ...options};
243
+ },
244
+
245
+ // by default will leave on meeting's resourceId
246
+ // if you explicity want it not to leave on resource id, pass
247
+ // {resourceId: null}
248
+ // TODO: chris, you can modify this however you want
249
+ leaveMeeting: (meeting, options: any = {}) => {
250
+ if (meeting.meetingState === FULL_STATE.INACTIVE) {
251
+ // TODO: clean up if the meeting is already inactive
252
+ return Promise.reject(new MeetingNotActiveError());
253
+ }
75
254
 
76
- MeetingUtil.hasOwner = (info) => info && info.owner;
255
+ if (MeetingUtil.isUserInLeftState(meeting.locusInfo)) {
256
+ return Promise.reject(new UserNotJoinedError());
257
+ }
77
258
 
78
- MeetingUtil.isOwnerSelf = (owner, selfId) => owner === selfId;
259
+ const leaveOptions = MeetingUtil.prepareLeaveMeetingOptions(meeting, options);
260
+
261
+ return meeting.meetingRequest
262
+ .leaveMeeting(leaveOptions)
263
+ .then(() => {
264
+ if (options.moveMeeting) {
265
+ return Promise.resolve();
266
+ }
267
+
268
+ return MeetingUtil.cleanUp(meeting);
269
+ })
270
+ .catch((err) => {
271
+ // TODO: If the meeting state comes as LEFT or INACTIVE as response then
272
+ // 1) on leave clean up the meeting or simply do a sync on the meeting
273
+ // 2) If the error says meeting is inactive then destroy the meeting object
274
+ LoggerProxy.logger.error(
275
+ `Meeting:util#leaveMeeting --> An error occured while trying to leave meeting with an id of ${meeting.id}, error: ${err}`
276
+ );
277
+
278
+ return Promise.reject(err);
279
+ });
280
+ },
281
+ declineMeeting: (meeting, reason) =>
282
+ meeting.meetingRequest.declineMeeting({
283
+ locusUrl: meeting.locusUrl,
284
+ deviceUrl: meeting.deviceUrl,
285
+ reason,
286
+ }),
79
287
 
80
- MeetingUtil.isPinOrGuest = (err) =>
81
- err?.body?.errorCode && INTENT_TO_JOIN.includes(err.body.errorCode);
288
+ isUserInLeftState: (locusInfo) => locusInfo.parsedLocus?.self?.state === _LEFT_,
82
289
 
83
- MeetingUtil.joinMeeting = (meeting, options) => {
84
- if (!meeting) {
85
- return Promise.reject(new ParameterError('You need a meeting object.'));
86
- }
290
+ isUserInIdleState: (locusInfo) => locusInfo.parsedLocus?.self?.state === _IDLE_,
87
291
 
88
- Metrics.postEvent({event: eventType.LOCUS_JOIN_REQUEST, meeting});
292
+ isUserInJoinedState: (locusInfo) => locusInfo.parsedLocus?.self?.state === _JOINED_,
89
293
 
90
- // eslint-disable-next-line no-warning-comments
91
- // TODO: check if the meeting is in JOINING state
92
- // if Joining state termintate the request as user might click multiple times
93
- return meeting.meetingRequest
94
- .joinMeeting({
95
- inviteeAddress: meeting.meetingJoinUrl || meeting.sipUri,
96
- meetingNumber: meeting.meetingNumber,
97
- deviceUrl: meeting.deviceUrl,
98
- locusUrl: meeting.locusUrl,
99
- correlationId: meeting.correlationId,
100
- roapMessage: options.roapMessage,
101
- permissionToken: meeting.permissionToken,
102
- resourceId: options.resourceId || null,
103
- moderator: options.moderator,
104
- pin: options.pin,
105
- moveToResource: options.moveToResource,
106
- preferTranscoding: !meeting.isMultistream,
107
- asResourceOccupant: options.asResourceOccupant,
108
- breakoutsSupported: options.breakoutsSupported,
109
- })
110
- .then((res) => {
111
- Metrics.postEvent({
112
- event: eventType.LOCUS_JOIN_RESPONSE,
113
- meeting,
114
- data: {
115
- trigger: trigger.LOCI_UPDATE,
116
- locus: res.body.locus,
117
- mediaConnections: res.body.mediaConnections,
118
- trackingId: res.headers.trackingid,
294
+ isMediaEstablished: (currentMediaStatus) =>
295
+ currentMediaStatus &&
296
+ (currentMediaStatus.audio || currentMediaStatus.video || currentMediaStatus.share),
297
+
298
+ joinMeetingOptions: (meeting, options: any = {}) => {
299
+ const webex = meeting.getWebexObject();
300
+
301
+ meeting.resourceId = meeting.resourceId || options.resourceId;
302
+
303
+ if (meeting.requiredCaptcha) {
304
+ return Promise.reject(new CaptchaError());
305
+ }
306
+ if (meeting.passwordStatus === PASSWORD_STATUS.REQUIRED) {
307
+ return Promise.reject(new PasswordError());
308
+ }
309
+
310
+ if (options.pin) {
311
+ // @ts-ignore
312
+ webex.internal.newMetrics.submitClientEvent({
313
+ name: 'client.pin.collected',
314
+ options: {
315
+ meetingId: meeting.id,
119
316
  },
120
317
  });
318
+ }
121
319
 
122
- return MeetingUtil.parseLocusJoin(res);
123
- });
124
- };
320
+ // normal join meeting, scenario A, D
321
+ return MeetingUtil.joinMeeting(meeting, options)
322
+ .then((response) => {
323
+ meeting.setLocus(response);
324
+
325
+ return Promise.resolve(response);
326
+ })
327
+ .catch((err) => {
328
+ // joining a claimed PMR that is not my own, scenario B
329
+ if (MeetingUtil.isPinOrGuest(err)) {
330
+ // @ts-ignore
331
+ webex.internal.newMetrics.submitClientEvent({
332
+ name: 'client.pin.prompt',
333
+ options: {
334
+ meetingId: meeting.id,
335
+ },
336
+ });
337
+
338
+ // request host pin or non host for unclaimed PMR, start of Scenario C
339
+ // see https://sqbu-github.cisco.com/WebExSquared/locus/wiki/Locus-Lobby-and--IVR-Feature
340
+ return Promise.reject(new IntentToJoinError('Error Joining Meeting', err));
341
+ }
342
+ LoggerProxy.logger.error(
343
+ 'Meeting:util#joinMeetingOptions --> Error joining the call, ',
344
+ err
345
+ );
346
+
347
+ return Promise.reject(new JoinMeetingError(options, 'Error Joining Meeting', err));
348
+ });
349
+ },
350
+
351
+ /**
352
+ * Returns request options for leaving a meeting.
353
+ * @param {any} meeting
354
+ * @param {any} options
355
+ * @returns {any} request options
356
+ */
357
+ buildLeaveFetchRequestOptions: (meeting, options: any = {}) => {
358
+ const leaveOptions = MeetingUtil.prepareLeaveMeetingOptions(meeting, options);
359
+
360
+ return meeting.meetingRequest.buildLeaveMeetingRequestOptions(leaveOptions);
361
+ },
362
+
363
+ getTrack: (stream) => {
364
+ let audioTrack = null;
365
+ let videoTrack = null;
366
+ let audioTracks = null;
367
+ let videoTracks = null;
368
+
369
+ if (!stream) {
370
+ return {audioTrack: null, videoTrack: null};
371
+ }
372
+ if (stream.getAudioTracks) {
373
+ audioTracks = stream.getAudioTracks();
374
+ }
375
+ if (stream.getVideoTracks) {
376
+ videoTracks = stream.getVideoTracks();
377
+ }
125
378
 
126
- MeetingUtil.cleanUp = (meeting) => {
127
- meeting.breakouts.cleanUp();
128
-
129
- // make sure we send last metrics before we close the peerconnection
130
- const stopStatsAnalyzer = meeting.statsAnalyzer
131
- ? meeting.statsAnalyzer.stopAnalyzer()
132
- : Promise.resolve();
133
-
134
- return stopStatsAnalyzer
135
- .then(() => meeting.closeLocalStream())
136
- .then(() => meeting.closeLocalShare())
137
- .then(() => meeting.closeRemoteTracks())
138
- .then(() => meeting.closePeerConnections())
139
- .then(() => {
140
- meeting.unsetLocalVideoTrack();
141
- meeting.unsetLocalShareTrack();
142
- meeting.unsetRemoteTracks();
143
- meeting.unsetPeerConnections();
144
- meeting.reconnectionManager.cleanUp();
145
- })
146
- .then(() => meeting.stopKeepAlive())
147
- .then(() => meeting.updateLLMConnection());
148
- };
379
+ if (audioTracks && audioTracks.length > 0) {
380
+ [audioTrack] = audioTracks;
381
+ }
149
382
 
150
- MeetingUtil.disconnectPhoneAudio = (meeting, phoneUrl) => {
151
- if (meeting.meetingState === FULL_STATE.INACTIVE) {
152
- return Promise.reject(new MeetingNotActiveError());
153
- }
154
-
155
- const options = {
156
- locusUrl: meeting.locusUrl,
157
- selfId: meeting.selfId,
158
- correlationId: meeting.correlationId,
159
- phoneUrl,
160
- };
161
-
162
- return meeting.meetingRequest
163
- .disconnectPhoneAudio(options)
164
- .then((response) => {
165
- if (response?.body?.locus) {
166
- meeting.locusInfo.onFullLocus(response.body.locus);
167
- }
168
- })
169
- .catch((err) => {
170
- LoggerProxy.logger.error(
171
- `Meeting:util#disconnectPhoneAudio --> An error occured while disconnecting phone audio in meeting ${meeting.id}, error: ${err}`
172
- );
383
+ if (videoTracks && videoTracks.length > 0) {
384
+ [videoTrack] = videoTracks;
385
+ }
173
386
 
174
- return Promise.reject(err);
175
- });
176
- };
387
+ return {audioTrack, videoTrack};
388
+ },
177
389
 
178
- // by default will leave on meeting's resourceId
179
- // if you explicity want it not to leave on resource id, pass
180
- // {resourceId: null}
181
- // TODO: chris, you can modify this however you want
182
- MeetingUtil.leaveMeeting = (meeting, options: any = {}) => {
183
- if (meeting.meetingState === FULL_STATE.INACTIVE) {
184
- // TODO: clean up if the meeting is already inactive
185
- return Promise.reject(new MeetingNotActiveError());
186
- }
187
-
188
- if (MeetingUtil.isUserInLeftState(meeting.locusInfo)) {
189
- return Promise.reject(new UserNotJoinedError());
190
- }
191
-
192
- const defaultOptions = {
193
- locusUrl: meeting.locusUrl,
194
- selfId: meeting.selfId,
195
- correlationId: meeting.correlationId,
196
- resourceId: meeting.resourceId,
197
- deviceUrl: meeting.deviceUrl,
198
- };
199
-
200
- const leaveOptions = {...defaultOptions, ...options};
201
-
202
- return meeting.meetingRequest
203
- .leaveMeeting(leaveOptions)
204
- .then((response) => {
205
- if (response && response.body && response.body.locus) {
206
- // && !options.moveMeeting) {
207
- meeting.locusInfo.onFullLocus(response.body.locus);
208
- }
390
+ getModeratorFromLocusInfo: (locusInfo) =>
391
+ locusInfo &&
392
+ locusInfo.parsedLocus &&
393
+ locusInfo.parsedLocus.info &&
394
+ locusInfo.parsedLocus.info &&
395
+ locusInfo.parsedLocus.info.moderator,
209
396
 
210
- return Promise.resolve();
211
- })
212
- .then(() => {
213
- if (options.moveMeeting) {
214
- return Promise.resolve();
215
- }
397
+ getPolicyFromLocusInfo: (locusInfo) =>
398
+ locusInfo &&
399
+ locusInfo.parsedLocus &&
400
+ locusInfo.parsedLocus.info &&
401
+ locusInfo.parsedLocus.info &&
402
+ locusInfo.parsedLocus.info.policy,
216
403
 
217
- return MeetingUtil.cleanUp(meeting);
218
- })
219
- .catch((err) => {
220
- // TODO: If the meeting state comes as LEFT or INACTIVE as response then
221
- // 1) on leave clean up the meeting or simply do a sync on the meeting
222
- // 2) If the error says meeting is inactive then destroy the meeting object
223
- LoggerProxy.logger.error(
224
- `Meeting:util#leaveMeeting --> An error occured while trying to leave meeting with an id of ${meeting.id}, error: ${err}`
225
- );
404
+ getUserDisplayHintsFromLocusInfo: (locusInfo) =>
405
+ locusInfo?.parsedLocus?.info?.userDisplayHints || [],
226
406
 
227
- return Promise.reject(err);
228
- });
229
- };
230
- MeetingUtil.declineMeeting = (meeting, reason) =>
231
- meeting.meetingRequest.declineMeeting({
232
- locusUrl: meeting.locusUrl,
233
- deviceUrl: meeting.deviceUrl,
234
- reason,
235
- });
407
+ canInviteNewParticipants: (displayHints) => displayHints.includes(DISPLAY_HINTS.ADD_GUEST),
236
408
 
237
- MeetingUtil.isUserInLeftState = (locusInfo) => locusInfo.parsedLocus?.self?.state === _LEFT_;
409
+ canAdmitParticipant: (displayHints) =>
410
+ displayHints.includes(DISPLAY_HINTS.ROSTER_WAITING_TO_JOIN),
238
411
 
239
- MeetingUtil.isUserInIdleState = (locusInfo) => locusInfo.parsedLocus?.self?.state === _IDLE_;
412
+ canUserLock: (displayHints) =>
413
+ displayHints.includes(DISPLAY_HINTS.LOCK_CONTROL_LOCK) &&
414
+ displayHints.includes(DISPLAY_HINTS.LOCK_STATUS_UNLOCKED),
240
415
 
241
- MeetingUtil.isUserInJoinedState = (locusInfo) => locusInfo.parsedLocus?.self?.state === _JOINED_;
416
+ canUserUnlock: (displayHints) =>
417
+ displayHints.includes(DISPLAY_HINTS.LOCK_CONTROL_UNLOCK) &&
418
+ displayHints.includes(DISPLAY_HINTS.LOCK_STATUS_LOCKED),
242
419
 
243
- MeetingUtil.isMediaEstablished = (currentMediaStatus) =>
244
- currentMediaStatus &&
245
- (currentMediaStatus.audio || currentMediaStatus.video || currentMediaStatus.share);
420
+ canUserRaiseHand: (displayHints) => displayHints.includes(DISPLAY_HINTS.RAISE_HAND),
246
421
 
247
- MeetingUtil.joinMeetingOptions = (meeting, options: any = {}) => {
248
- meeting.resourceId = meeting.resourceId || options.resourceId;
422
+ canUserLowerAllHands: (displayHints) => displayHints.includes(DISPLAY_HINTS.LOWER_ALL_HANDS),
249
423
 
250
- if (meeting.requiredCaptcha) {
251
- return Promise.reject(new CaptchaError());
252
- }
253
- if (meeting.passwordStatus === PASSWORD_STATUS.REQUIRED) {
254
- return Promise.reject(new PasswordError());
255
- }
424
+ canUserLowerSomeoneElsesHand: (displayHints) =>
425
+ displayHints.includes(DISPLAY_HINTS.LOWER_SOMEONE_ELSES_HAND),
256
426
 
257
- if (options.pin) {
258
- Metrics.postEvent({
259
- event: eventType.PIN_COLLECTED,
260
- meeting,
261
- });
262
- }
263
-
264
- // normal join meeting, scenario A, D
265
- return MeetingUtil.joinMeeting(meeting, options)
266
- .then((response) => {
267
- meeting.setLocus(response);
268
-
269
- return Promise.resolve(response);
270
- })
271
- .catch((err) => {
272
- // joining a claimed PMR that is not my own, scenario B
273
- if (MeetingUtil.isPinOrGuest(err)) {
274
- Metrics.postEvent({
275
- event: eventType.PIN_PROMPT,
276
- meeting,
277
- });
427
+ bothLeaveAndEndMeetingAvailable: (displayHints) =>
428
+ displayHints.includes(DISPLAY_HINTS.LEAVE_TRANSFER_HOST_END_MEETING) ||
429
+ displayHints.includes(DISPLAY_HINTS.LEAVE_END_MEETING),
278
430
 
279
- // request host pin or non host for unclaimed PMR, start of Scenario C
280
- // see https://sqbu-github.cisco.com/WebExSquared/locus/wiki/Locus-Lobby-and--IVR-Feature
281
- return Promise.reject(new IntentToJoinError('Error Joining Meeting', err));
282
- }
283
- LoggerProxy.logger.error('Meeting:util#joinMeetingOptions --> Error joining the call, ', err);
431
+ canManageBreakout: (displayHints) => displayHints.includes(DISPLAY_HINTS.BREAKOUT_MANAGEMENT),
432
+ canBroadcastMessageToBreakout: (displayHints, policies = {}) =>
433
+ displayHints.includes(DISPLAY_HINTS.BROADCAST_MESSAGE_TO_BREAKOUT) &&
434
+ !!policies[SELF_POLICY.SUPPORT_BROADCAST_MESSAGE],
284
435
 
285
- return Promise.reject(new JoinMeetingError(options, 'Error Joining Meeting', err));
286
- });
287
- };
436
+ isSuppressBreakoutSupport: (displayHints) =>
437
+ displayHints.includes(DISPLAY_HINTS.UCF_SUPPRESS_BREAKOUTS_SUPPORT),
288
438
 
289
- MeetingUtil.validateOptions = (options) => {
290
- const {sendVideo, sendAudio, sendShare, localStream, localShare} = options;
439
+ canAdmitLobbyToBreakout: (displayHints) =>
440
+ !displayHints.includes(DISPLAY_HINTS.DISABLE_LOBBY_TO_BREAKOUT),
291
441
 
292
- if (sendVideo && !MeetingUtil.getTrack(localStream).videoTrack) {
293
- return Promise.reject(new ParameterError('please pass valid video streams'));
294
- }
442
+ isBreakoutPreassignmentsEnabled: (displayHints) =>
443
+ !displayHints.includes(DISPLAY_HINTS.DISABLE_BREAKOUT_PREASSIGNMENTS),
295
444
 
296
- if (sendAudio && !MeetingUtil.getTrack(localStream).audioTrack) {
297
- return Promise.reject(new ParameterError('please pass valid audio streams'));
298
- }
445
+ canUserAskForHelp: (displayHints) => !displayHints.includes(DISPLAY_HINTS.DISABLE_ASK_FOR_HELP),
299
446
 
300
- if (sendShare && !MeetingUtil.getTrack(localShare).videoTrack) {
301
- return Promise.reject(new ParameterError('please pass valid share streams'));
302
- }
447
+ lockMeeting: (actions, request, locusUrl) => {
448
+ if (actions && actions.canLock) {
449
+ return request.lockMeeting({locusUrl, lock: true});
450
+ }
303
451
 
304
- return Promise.resolve();
305
- };
452
+ return Promise.reject(new PermissionError('Lock not allowed, due to joined property.'));
453
+ },
306
454
 
307
- MeetingUtil.getTrack = (stream) => {
308
- let audioTrack = null;
309
- let videoTrack = null;
310
- let audioTracks = null;
311
- let videoTracks = null;
312
-
313
- if (!stream) {
314
- return {audioTrack: null, videoTrack: null};
315
- }
316
- if (stream.getAudioTracks) {
317
- audioTracks = stream.getAudioTracks();
318
- }
319
- if (stream.getVideoTracks) {
320
- videoTracks = stream.getVideoTracks();
321
- }
322
-
323
- if (audioTracks && audioTracks.length > 0) {
324
- [audioTrack] = audioTracks;
325
- }
326
-
327
- if (videoTracks && videoTracks.length > 0) {
328
- [videoTrack] = videoTracks;
329
- }
330
-
331
- return {audioTrack, videoTrack};
332
- };
455
+ unlockMeeting: (actions, request, locusUrl) => {
456
+ if (actions && actions.canUnlock) {
457
+ return request.lockMeeting({locusUrl, lock: false});
458
+ }
333
459
 
334
- MeetingUtil.getModeratorFromLocusInfo = (locusInfo) =>
335
- locusInfo &&
336
- locusInfo.parsedLocus &&
337
- locusInfo.parsedLocus.info &&
338
- locusInfo.parsedLocus.info &&
339
- locusInfo.parsedLocus.info.moderator;
460
+ return Promise.reject(new PermissionError('Unlock not allowed, due to joined property.'));
461
+ },
340
462
 
341
- MeetingUtil.getPolicyFromLocusInfo = (locusInfo) =>
342
- locusInfo &&
343
- locusInfo.parsedLocus &&
344
- locusInfo.parsedLocus.info &&
345
- locusInfo.parsedLocus.info &&
346
- locusInfo.parsedLocus.info.policy;
463
+ handleAudioLogging: (audioTrack?: LocalMicrophoneTrack) => {
464
+ const LOG_HEADER = 'MeetingUtil#handleAudioLogging -->';
347
465
 
348
- MeetingUtil.getUserDisplayHintsFromLocusInfo = (locusInfo) =>
349
- locusInfo?.parsedLocus?.info?.userDisplayHints || [];
466
+ if (audioTrack) {
467
+ const settings = audioTrack.underlyingTrack.getSettings();
468
+ const {deviceId} = settings;
350
469
 
351
- MeetingUtil.canInviteNewParticipants = (displayHints) =>
352
- displayHints.includes(DISPLAY_HINTS.ADD_GUEST);
470
+ LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${deviceId}`);
471
+ LoggerProxy.logger.log(LOG_HEADER, 'settings =', JSON.stringify(settings));
472
+ }
473
+ },
353
474
 
354
- MeetingUtil.canAdmitParticipant = (displayHints) =>
355
- displayHints.includes(DISPLAY_HINTS.ROSTER_WAITING_TO_JOIN);
475
+ handleVideoLogging: (videoTrack?: LocalCameraTrack) => {
476
+ const LOG_HEADER = 'MeetingUtil#handleVideoLogging -->';
356
477
 
357
- MeetingUtil.canUserLock = (displayHints) =>
358
- displayHints.includes(DISPLAY_HINTS.LOCK_CONTROL_LOCK) &&
359
- displayHints.includes(DISPLAY_HINTS.LOCK_STATUS_UNLOCKED);
478
+ if (videoTrack) {
479
+ const settings = videoTrack.underlyingTrack.getSettings();
480
+ const {deviceId} = settings;
360
481
 
361
- MeetingUtil.canUserUnlock = (displayHints) =>
362
- displayHints.includes(DISPLAY_HINTS.LOCK_CONTROL_UNLOCK) &&
363
- displayHints.includes(DISPLAY_HINTS.LOCK_STATUS_LOCKED);
482
+ LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${deviceId}`);
483
+ LoggerProxy.logger.log(LOG_HEADER, 'settings =', JSON.stringify(settings));
484
+ }
485
+ },
364
486
 
365
- MeetingUtil.canUserRaiseHand = (displayHints) => displayHints.includes(DISPLAY_HINTS.RAISE_HAND);
487
+ handleDeviceLogging: (devices = []) => {
488
+ const LOG_HEADER = 'MeetingUtil#handleDeviceLogging -->';
366
489
 
367
- MeetingUtil.canUserLowerAllHands = (displayHints) =>
368
- displayHints.includes(DISPLAY_HINTS.LOWER_ALL_HANDS);
490
+ devices.forEach((device) => {
491
+ LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${device.deviceId}`);
492
+ LoggerProxy.logger.log(LOG_HEADER, 'settings', JSON.stringify(device));
493
+ });
494
+ },
369
495
 
370
- MeetingUtil.canUserLowerSomeoneElsesHand = (displayHints) =>
371
- displayHints.includes(DISPLAY_HINTS.LOWER_SOMEONE_ELSES_HAND);
496
+ endMeetingForAll: (meeting) => {
497
+ if (meeting.meetingState === FULL_STATE.INACTIVE) {
498
+ return Promise.reject(new MeetingNotActiveError());
499
+ }
372
500
 
373
- MeetingUtil.bothLeaveAndEndMeetingAvailable = (displayHints) =>
374
- displayHints.includes(DISPLAY_HINTS.LEAVE_TRANSFER_HOST_END_MEETING) ||
375
- displayHints.includes(DISPLAY_HINTS.LEAVE_END_MEETING);
501
+ const endOptions = {
502
+ locusUrl: meeting.locusUrl,
503
+ };
376
504
 
377
- MeetingUtil.lockMeeting = (actions, request, locusUrl) => {
378
- if (actions && actions.canLock) {
379
- return request.lockMeeting({locusUrl, lock: true});
380
- }
505
+ return meeting.meetingRequest
506
+ .endMeetingForAll(endOptions)
507
+ .then(() => MeetingUtil.cleanUp(meeting))
508
+ .catch((err) => {
509
+ LoggerProxy.logger.error(
510
+ `Meeting:util#endMeetingForAll An error occured while trying to end meeting for all with an id of ${meeting.id}, error: ${err}`
511
+ );
381
512
 
382
- return Promise.reject(new PermissionError('Lock not allowed, due to joined property.'));
383
- };
513
+ return Promise.reject(err);
514
+ });
515
+ },
384
516
 
385
- MeetingUtil.unlockMeeting = (actions, request, locusUrl) => {
386
- if (actions && actions.canUnlock) {
387
- return request.lockMeeting({locusUrl, lock: false});
388
- }
517
+ canEnableClosedCaption: (displayHints) => displayHints.includes(DISPLAY_HINTS.CAPTION_START),
389
518
 
390
- return Promise.reject(new PermissionError('Unlock not allowed, due to joined property.'));
391
- };
519
+ isSaveTranscriptsEnabled: (displayHints) =>
520
+ displayHints.includes(DISPLAY_HINTS.SAVE_TRANSCRIPTS_ENABLED),
392
521
 
393
- MeetingUtil.handleAudioLogging = (audioTrack) => {
394
- const LOG_HEADER = 'MeetingUtil#handleAudioLogging -->';
522
+ canStartTranscribing: (displayHints) =>
523
+ displayHints.includes(DISPLAY_HINTS.TRANSCRIPTION_CONTROL_START),
395
524
 
396
- if (audioTrack) {
397
- const settings = audioTrack.getSettings();
398
- const {deviceId} = settings;
525
+ canStopTranscribing: (displayHints) =>
526
+ displayHints.includes(DISPLAY_HINTS.TRANSCRIPTION_CONTROL_STOP),
399
527
 
400
- LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${deviceId}`);
401
- LoggerProxy.logger.log(LOG_HEADER, 'settings =', JSON.stringify(settings));
402
- }
403
- };
528
+ isClosedCaptionActive: (displayHints) =>
529
+ displayHints.includes(DISPLAY_HINTS.CAPTION_STATUS_ACTIVE),
404
530
 
405
- MeetingUtil.handleVideoLogging = (videoTrack) => {
406
- const LOG_HEADER = 'MeetingUtil#handleVideoLogging -->';
531
+ isWebexAssistantActive: (displayHints) =>
532
+ displayHints.includes(DISPLAY_HINTS.WEBEX_ASSISTANT_STATUS_ACTIVE),
407
533
 
408
- if (videoTrack) {
409
- const settings = videoTrack.getSettings();
410
- const {deviceId} = settings;
534
+ canViewCaptionPanel: (displayHints) => displayHints.includes(DISPLAY_HINTS.ENABLE_CAPTION_PANEL),
411
535
 
412
- LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${deviceId}`);
413
- LoggerProxy.logger.log(LOG_HEADER, 'settings =', JSON.stringify(settings));
414
- }
415
- };
536
+ isRealTimeTranslationEnabled: (displayHints) =>
537
+ displayHints.includes(DISPLAY_HINTS.DISPLAY_REAL_TIME_TRANSLATION),
416
538
 
417
- MeetingUtil.handleDeviceLogging = (devices = []) => {
418
- const LOG_HEADER = 'MeetingUtil#handleDeviceLogging -->';
539
+ canSelectSpokenLanguages: (displayHints) =>
540
+ displayHints.includes(DISPLAY_HINTS.DISPLAY_NON_ENGLISH_ASR),
419
541
 
420
- devices.forEach((device) => {
421
- LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${device.deviceId}`);
422
- LoggerProxy.logger.log(LOG_HEADER, 'settings', JSON.stringify(device));
423
- });
424
- };
542
+ waitingForOthersToJoin: (displayHints) => displayHints.includes(DISPLAY_HINTS.WAITING_FOR_OTHERS),
425
543
 
426
- MeetingUtil.endMeetingForAll = (meeting) => {
427
- if (meeting.meetingState === FULL_STATE.INACTIVE) {
428
- return Promise.reject(new MeetingNotActiveError());
429
- }
544
+ canSendReactions: (originalValue, displayHints) => {
545
+ if (displayHints.includes(DISPLAY_HINTS.REACTIONS_ACTIVE)) {
546
+ return true;
547
+ }
548
+ if (displayHints.includes(DISPLAY_HINTS.REACTIONS_INACTIVE)) {
549
+ return false;
550
+ }
430
551
 
431
- const endOptions = {
432
- locusUrl: meeting.locusUrl,
433
- };
552
+ return originalValue;
553
+ },
554
+ canUserRenameSelfAndObserved: (displayHints) =>
555
+ displayHints.includes(DISPLAY_HINTS.CAN_RENAME_SELF_AND_OBSERVED),
434
556
 
435
- return meeting.meetingRequest
436
- .endMeetingForAll(endOptions)
437
- .then((response) => {
438
- if (response && response.body && response.body.locus) {
439
- meeting.locusInfo.onFullLocus(response.body.locus);
557
+ canUserRenameOthers: (displayHints) => displayHints.includes(DISPLAY_HINTS.CAN_RENAME_OTHERS),
558
+
559
+ canShareWhiteBoard: (displayHints) => displayHints.includes(DISPLAY_HINTS.SHARE_WHITEBOARD),
560
+
561
+ /**
562
+ * Adds the current locus sequence information to a request body
563
+ * @param {Object} meeting The meeting object
564
+ * @param {Object} requestBody The body of a request to locus
565
+ * @returns {void}
566
+ */
567
+ addSequence: (meeting, requestBody) => {
568
+ const sequence = meeting?.locusInfo?.sequence;
569
+
570
+ if (!sequence) {
571
+ return;
572
+ }
573
+
574
+ requestBody.sequence = sequence;
575
+ },
576
+
577
+ /**
578
+ * Updates the locus info for the meeting with the delta locus
579
+ * returned from requests that include the sequence information
580
+ * Returns the original response object
581
+ * @param {Object} meeting The meeting object
582
+ * @param {Object} response The response of the http request
583
+ * @returns {Object}
584
+ */
585
+ updateLocusWithDelta: (meeting, response) => {
586
+ if (!meeting) {
587
+ return response;
588
+ }
589
+
590
+ const locus = response?.body?.locus;
591
+
592
+ if (locus) {
593
+ meeting.locusInfo.handleLocusDelta(locus, meeting);
594
+ }
595
+
596
+ return response;
597
+ },
598
+
599
+ generateBuildLocusDeltaRequestOptions: (originalMeeting) => {
600
+ const meetingRef = new WeakRef(originalMeeting);
601
+
602
+ const buildLocusDeltaRequestOptions = (originalOptions) => {
603
+ const meeting = meetingRef.deref();
604
+
605
+ if (!meeting) {
606
+ return originalOptions;
440
607
  }
441
608
 
442
- return Promise.resolve();
443
- })
444
- .then(() => MeetingUtil.cleanUp(meeting))
445
- .catch((err) => {
446
- LoggerProxy.logger.error(
447
- `Meeting:util#endMeetingForAll An error occured while trying to end meeting for all with an id of ${meeting.id}, error: ${err}`
448
- );
609
+ const options = cloneDeep(originalOptions);
449
610
 
450
- return Promise.reject(err);
451
- });
452
- };
611
+ if (!options.body) {
612
+ options.body = {};
613
+ }
453
614
 
454
- MeetingUtil.canEnableClosedCaption = (displayHints) =>
455
- displayHints.includes(DISPLAY_HINTS.CAPTION_START);
615
+ MeetingUtil.addSequence(meeting, options.body);
456
616
 
457
- MeetingUtil.canStartTranscribing = (displayHints) =>
458
- displayHints.includes(DISPLAY_HINTS.TRANSCRIPTION_CONTROL_START);
617
+ return options;
618
+ };
459
619
 
460
- MeetingUtil.canStopTranscribing = (displayHints) =>
461
- displayHints.includes(DISPLAY_HINTS.TRANSCRIPTION_CONTROL_STOP);
620
+ return buildLocusDeltaRequestOptions;
621
+ },
462
622
 
463
- MeetingUtil.isClosedCaptionActive = (displayHints) =>
464
- displayHints.includes(DISPLAY_HINTS.CAPTION_STATUS_ACTIVE);
623
+ generateLocusDeltaRequest: (originalMeeting) => {
624
+ const meetingRef = new WeakRef(originalMeeting);
465
625
 
466
- MeetingUtil.isWebexAssistantActive = (displayHints) =>
467
- displayHints.includes(DISPLAY_HINTS.WEBEX_ASSISTANT_STATUS_ACTIVE);
626
+ const buildLocusDeltaRequestOptions =
627
+ MeetingUtil.generateBuildLocusDeltaRequestOptions(originalMeeting);
468
628
 
469
- MeetingUtil.canViewCaptionPanel = (displayHints) =>
470
- displayHints.includes(DISPLAY_HINTS.ENABLE_CAPTION_PANEL);
629
+ const locusDeltaRequest = (originalOptions) => {
630
+ const meeting = meetingRef.deref();
471
631
 
472
- MeetingUtil.isRealTimeTranslationEnabled = (displayHints) =>
473
- displayHints.includes(DISPLAY_HINTS.DISPLAY_REAL_TIME_TRANSLATION);
632
+ if (!meeting) {
633
+ return Promise.resolve();
634
+ }
474
635
 
475
- MeetingUtil.canSelectSpokenLanguages = (displayHints) =>
476
- displayHints.includes(DISPLAY_HINTS.DISPLAY_NON_ENGLISH_ASR);
636
+ const options = buildLocusDeltaRequestOptions(originalOptions);
477
637
 
478
- MeetingUtil.waitingForOthersToJoin = (displayHints) =>
479
- displayHints.includes(DISPLAY_HINTS.WAITING_FOR_OTHERS);
638
+ return meeting
639
+ .request(options)
640
+ .then((response) => MeetingUtil.updateLocusWithDelta(meeting, response));
641
+ };
480
642
 
481
- MeetingUtil.canEnableReactions = (originalValue, displayHints) => {
482
- if (displayHints.includes(DISPLAY_HINTS.ENABLE_REACTIONS)) {
483
- return true;
484
- }
485
- if (displayHints.includes(DISPLAY_HINTS.DISABLE_REACTIONS)) {
486
- return false;
487
- }
643
+ return locusDeltaRequest;
644
+ },
488
645
 
489
- return originalValue;
490
- };
646
+ selfSupportsFeature: (feature: SELF_POLICY, userPolicies: Record<SELF_POLICY, boolean>) => {
647
+ if (!userPolicies) {
648
+ return true;
649
+ }
491
650
 
492
- MeetingUtil.canSendReactions = (originalValue, displayHints) => {
493
- if (displayHints.includes(DISPLAY_HINTS.REACTIONS_ACTIVE)) {
494
- return true;
495
- }
496
- if (displayHints.includes(DISPLAY_HINTS.REACTIONS_INACTIVE)) {
497
- return false;
498
- }
651
+ return userPolicies[feature];
652
+ },
499
653
 
500
- return originalValue;
654
+ parseInterpretationInfo: (meeting, meetingInfo) => {
655
+ if (!meeting || !meetingInfo) {
656
+ return;
657
+ }
658
+ const siInfo = meetingInfo.simultaneousInterpretation;
659
+ meeting.simultaneousInterpretation.updateMeetingSIEnabled(
660
+ !!meetingInfo.turnOnSimultaneousInterpretation,
661
+ !!siInfo?.currentSIInterpreter
662
+ );
663
+ const hostSIEnabled = !!(
664
+ meetingInfo.turnOnSimultaneousInterpretation &&
665
+ meetingInfo?.meetingSiteSetting?.enableHostInterpreterControlSI
666
+ );
667
+ meeting.simultaneousInterpretation.updateHostSIEnabled(hostSIEnabled);
668
+
669
+ function renameKey(obj, oldKey, newKey) {
670
+ if (oldKey in obj) {
671
+ obj[newKey] = obj[oldKey];
672
+ delete obj[oldKey];
673
+ }
674
+ }
675
+ if (siInfo) {
676
+ const lanuagesInfo = cloneDeep(siInfo.siLanguages);
677
+ for (const language of lanuagesInfo) {
678
+ renameKey(language, 'languageCode', 'languageName');
679
+ renameKey(language, 'languageGroupId', 'languageCode');
680
+ }
681
+ if (!meeting.simultaneousInterpretation?.siLanguages?.length) {
682
+ meeting.simultaneousInterpretation.updateInterpretation({siLanguages: lanuagesInfo});
683
+ }
684
+ }
685
+ Trigger.trigger(
686
+ meeting,
687
+ {
688
+ file: 'meeting/util',
689
+ function: 'parseInterpretationInfo',
690
+ },
691
+ EVENT_TRIGGERS.MEETING_INTERPRETATION_UPDATE
692
+ );
693
+ },
501
694
  };
502
695
 
503
696
  export default MeetingUtil;