@webex/plugin-meetings 3.0.0-beta.31 → 3.0.0-beta.310

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