@webex/plugin-meetings 3.0.0-beta.36 → 3.0.0-beta.361

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 (391) hide show
  1. package/README.md +58 -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/events.js +45 -0
  11. package/dist/breakouts/events.js.map +1 -0
  12. package/dist/breakouts/index.js +671 -81
  13. package/dist/breakouts/index.js.map +1 -1
  14. package/dist/breakouts/utils.js +45 -1
  15. package/dist/breakouts/utils.js.map +1 -1
  16. package/dist/common/errors/no-meeting-info.js +51 -0
  17. package/dist/common/errors/no-meeting-info.js.map +1 -0
  18. package/dist/common/errors/reclaim-host-role-errors.js +158 -0
  19. package/dist/common/errors/reclaim-host-role-errors.js.map +1 -0
  20. package/dist/common/errors/webex-errors.js +48 -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/logs/request.js +5 -1
  25. package/dist/common/logs/request.js.map +1 -1
  26. package/dist/common/queue.js +24 -9
  27. package/dist/common/queue.js.map +1 -1
  28. package/dist/config.js +5 -10
  29. package/dist/config.js.map +1 -1
  30. package/dist/constants.js +226 -33
  31. package/dist/constants.js.map +1 -1
  32. package/dist/controls-options-manager/enums.js +14 -2
  33. package/dist/controls-options-manager/enums.js.map +1 -1
  34. package/dist/controls-options-manager/index.js +109 -15
  35. package/dist/controls-options-manager/index.js.map +1 -1
  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 +309 -18
  39. package/dist/controls-options-manager/util.js.map +1 -1
  40. package/dist/index.js +117 -2
  41. package/dist/index.js.map +1 -1
  42. package/dist/interceptors/index.js +15 -0
  43. package/dist/interceptors/index.js.map +1 -0
  44. package/dist/interceptors/locusRetry.js +93 -0
  45. package/dist/interceptors/locusRetry.js.map +1 -0
  46. package/dist/interpretation/collection.js +23 -0
  47. package/dist/interpretation/collection.js.map +1 -0
  48. package/dist/interpretation/index.js +366 -0
  49. package/dist/interpretation/index.js.map +1 -0
  50. package/dist/interpretation/siLanguage.js +25 -0
  51. package/dist/interpretation/siLanguage.js.map +1 -0
  52. package/dist/locus-info/controlsUtils.js +91 -2
  53. package/dist/locus-info/controlsUtils.js.map +1 -1
  54. package/dist/locus-info/index.js +383 -62
  55. package/dist/locus-info/index.js.map +1 -1
  56. package/dist/locus-info/infoUtils.js +7 -1
  57. package/dist/locus-info/infoUtils.js.map +1 -1
  58. package/dist/locus-info/mediaSharesUtils.js +57 -1
  59. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  60. package/dist/locus-info/parser.js +249 -72
  61. package/dist/locus-info/parser.js.map +1 -1
  62. package/dist/locus-info/selfUtils.js +89 -14
  63. package/dist/locus-info/selfUtils.js.map +1 -1
  64. package/dist/media/index.js +61 -101
  65. package/dist/media/index.js.map +1 -1
  66. package/dist/media/properties.js +73 -124
  67. package/dist/media/properties.js.map +1 -1
  68. package/dist/mediaQualityMetrics/config.js +1 -204
  69. package/dist/mediaQualityMetrics/config.js.map +1 -1
  70. package/dist/meeting/in-meeting-actions.js +86 -2
  71. package/dist/meeting/in-meeting-actions.js.map +1 -1
  72. package/dist/meeting/index.js +3938 -2803
  73. package/dist/meeting/index.js.map +1 -1
  74. package/dist/meeting/locusMediaRequest.js +292 -0
  75. package/dist/meeting/locusMediaRequest.js.map +1 -0
  76. package/dist/meeting/muteState.js +224 -136
  77. package/dist/meeting/muteState.js.map +1 -1
  78. package/dist/meeting/request.js +166 -152
  79. package/dist/meeting/request.js.map +1 -1
  80. package/dist/meeting/util.js +601 -417
  81. package/dist/meeting/util.js.map +1 -1
  82. package/dist/meeting-info/index.js +73 -7
  83. package/dist/meeting-info/index.js.map +1 -1
  84. package/dist/meeting-info/meeting-info-v2.js +192 -51
  85. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  86. package/dist/meeting-info/util.js +1 -1
  87. package/dist/meeting-info/util.js.map +1 -1
  88. package/dist/meeting-info/utilv2.js +36 -36
  89. package/dist/meeting-info/utilv2.js.map +1 -1
  90. package/dist/meetings/collection.js +39 -0
  91. package/dist/meetings/collection.js.map +1 -1
  92. package/dist/meetings/index.js +478 -118
  93. package/dist/meetings/index.js.map +1 -1
  94. package/dist/meetings/meetings.types.js +7 -0
  95. package/dist/meetings/meetings.types.js.map +1 -0
  96. package/dist/meetings/request.js +2 -0
  97. package/dist/meetings/request.js.map +1 -1
  98. package/dist/meetings/util.js +72 -6
  99. package/dist/meetings/util.js.map +1 -1
  100. package/dist/member/index.js +58 -0
  101. package/dist/member/index.js.map +1 -1
  102. package/dist/member/types.js +25 -0
  103. package/dist/member/types.js.map +1 -0
  104. package/dist/member/util.js +132 -25
  105. package/dist/member/util.js.map +1 -1
  106. package/dist/members/collection.js +10 -0
  107. package/dist/members/collection.js.map +1 -1
  108. package/dist/members/index.js +102 -6
  109. package/dist/members/index.js.map +1 -1
  110. package/dist/members/request.js +106 -38
  111. package/dist/members/request.js.map +1 -1
  112. package/dist/members/types.js +15 -0
  113. package/dist/members/types.js.map +1 -0
  114. package/dist/members/util.js +326 -232
  115. package/dist/members/util.js.map +1 -1
  116. package/dist/metrics/constants.js +16 -1
  117. package/dist/metrics/constants.js.map +1 -1
  118. package/dist/metrics/index.js +1 -446
  119. package/dist/metrics/index.js.map +1 -1
  120. package/dist/multistream/mediaRequestManager.js +228 -58
  121. package/dist/multistream/mediaRequestManager.js.map +1 -1
  122. package/dist/multistream/receiveSlot.js +29 -16
  123. package/dist/multistream/receiveSlot.js.map +1 -1
  124. package/dist/multistream/receiveSlotManager.js +39 -36
  125. package/dist/multistream/receiveSlotManager.js.map +1 -1
  126. package/dist/multistream/remoteMedia.js +44 -18
  127. package/dist/multistream/remoteMedia.js.map +1 -1
  128. package/dist/multistream/remoteMediaGroup.js +60 -3
  129. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  130. package/dist/multistream/remoteMediaManager.js +209 -59
  131. package/dist/multistream/remoteMediaManager.js.map +1 -1
  132. package/dist/multistream/sendSlotManager.js +233 -0
  133. package/dist/multistream/sendSlotManager.js.map +1 -0
  134. package/dist/reachability/clusterReachability.js +356 -0
  135. package/dist/reachability/clusterReachability.js.map +1 -0
  136. package/dist/reachability/index.js +269 -390
  137. package/dist/reachability/index.js.map +1 -1
  138. package/dist/reachability/request.js +6 -4
  139. package/dist/reachability/request.js.map +1 -1
  140. package/dist/reachability/util.js +29 -0
  141. package/dist/reachability/util.js.map +1 -0
  142. package/dist/reconnection-manager/index.js +217 -170
  143. package/dist/reconnection-manager/index.js.map +1 -1
  144. package/dist/recording-controller/index.js +21 -2
  145. package/dist/recording-controller/index.js.map +1 -1
  146. package/dist/recording-controller/util.js +9 -8
  147. package/dist/recording-controller/util.js.map +1 -1
  148. package/dist/roap/index.js +51 -28
  149. package/dist/roap/index.js.map +1 -1
  150. package/dist/roap/request.js +48 -64
  151. package/dist/roap/request.js.map +1 -1
  152. package/dist/roap/turnDiscovery.js +95 -38
  153. package/dist/roap/turnDiscovery.js.map +1 -1
  154. package/dist/rtcMetrics/constants.js +12 -0
  155. package/dist/rtcMetrics/constants.js.map +1 -0
  156. package/dist/rtcMetrics/index.js +142 -0
  157. package/dist/rtcMetrics/index.js.map +1 -0
  158. package/dist/statsAnalyzer/index.js +182 -215
  159. package/dist/statsAnalyzer/index.js.map +1 -1
  160. package/dist/statsAnalyzer/mqaUtil.js +35 -28
  161. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  162. package/dist/types/annotation/annotation.types.d.ts +42 -0
  163. package/dist/types/annotation/constants.d.ts +31 -0
  164. package/dist/types/annotation/index.d.ts +117 -0
  165. package/dist/types/breakouts/events.d.ts +8 -0
  166. package/dist/types/breakouts/utils.d.ts +14 -0
  167. package/dist/types/common/errors/no-meeting-info.d.ts +14 -0
  168. package/dist/types/common/errors/reclaim-host-role-errors.d.ts +60 -0
  169. package/dist/types/common/errors/webex-errors.d.ts +25 -1
  170. package/dist/types/common/logs/request.d.ts +2 -0
  171. package/dist/types/common/queue.d.ts +9 -7
  172. package/dist/types/config.d.ts +2 -7
  173. package/dist/types/constants.d.ts +191 -31
  174. package/dist/types/controls-options-manager/enums.d.ts +11 -1
  175. package/dist/types/controls-options-manager/index.d.ts +17 -1
  176. package/dist/types/controls-options-manager/types.d.ts +43 -0
  177. package/dist/types/controls-options-manager/util.d.ts +1 -7
  178. package/dist/types/index.d.ts +6 -4
  179. package/dist/types/interceptors/index.d.ts +2 -0
  180. package/dist/types/interceptors/locusRetry.d.ts +27 -0
  181. package/dist/types/interpretation/collection.d.ts +5 -0
  182. package/dist/types/interpretation/index.d.ts +5 -0
  183. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  184. package/dist/types/locus-info/index.d.ts +57 -4
  185. package/dist/types/locus-info/parser.d.ts +66 -6
  186. package/dist/types/media/index.d.ts +2 -0
  187. package/dist/types/media/properties.d.ts +34 -49
  188. package/dist/types/mediaQualityMetrics/config.d.ts +0 -128
  189. package/dist/types/meeting/in-meeting-actions.d.ts +86 -2
  190. package/dist/types/meeting/index.d.ts +557 -496
  191. package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
  192. package/dist/types/meeting/muteState.d.ts +93 -25
  193. package/dist/types/meeting/request.d.ts +62 -43
  194. package/dist/types/meeting/util.d.ts +101 -1
  195. package/dist/types/meeting-info/index.d.ts +13 -1
  196. package/dist/types/meeting-info/meeting-info-v2.d.ts +31 -1
  197. package/dist/types/meetings/collection.d.ts +17 -0
  198. package/dist/types/meetings/index.d.ts +112 -20
  199. package/dist/types/meetings/meetings.types.d.ts +4 -0
  200. package/dist/types/member/index.d.ts +14 -0
  201. package/dist/types/member/types.d.ts +32 -0
  202. package/dist/types/members/collection.d.ts +5 -0
  203. package/dist/types/members/index.d.ts +35 -2
  204. package/dist/types/members/request.d.ts +73 -9
  205. package/dist/types/members/types.d.ts +25 -0
  206. package/dist/types/members/util.d.ts +214 -1
  207. package/dist/types/metrics/constants.d.ts +15 -0
  208. package/dist/types/metrics/index.d.ts +4 -111
  209. package/dist/types/multistream/mediaRequestManager.d.ts +72 -5
  210. package/dist/types/multistream/receiveSlot.d.ts +13 -11
  211. package/dist/types/multistream/receiveSlotManager.d.ts +14 -4
  212. package/dist/types/multistream/remoteMedia.d.ts +8 -29
  213. package/dist/types/multistream/remoteMediaGroup.d.ts +0 -9
  214. package/dist/types/multistream/remoteMediaManager.d.ts +46 -2
  215. package/dist/types/multistream/sendSlotManager.d.ts +61 -0
  216. package/dist/types/reachability/clusterReachability.d.ts +109 -0
  217. package/dist/types/reachability/index.d.ts +60 -95
  218. package/dist/types/reachability/request.d.ts +3 -1
  219. package/dist/types/reachability/util.d.ts +8 -0
  220. package/dist/types/reconnection-manager/index.d.ts +19 -0
  221. package/dist/types/recording-controller/index.d.ts +15 -1
  222. package/dist/types/recording-controller/util.d.ts +5 -4
  223. package/dist/types/roap/index.d.ts +2 -1
  224. package/dist/types/roap/request.d.ts +9 -8
  225. package/dist/types/roap/turnDiscovery.d.ts +21 -3
  226. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  227. package/dist/types/rtcMetrics/index.d.ts +54 -0
  228. package/dist/types/statsAnalyzer/index.d.ts +29 -11
  229. package/dist/types/webinar/collection.d.ts +16 -0
  230. package/dist/types/webinar/index.d.ts +5 -0
  231. package/dist/webinar/collection.js +44 -0
  232. package/dist/webinar/collection.js.map +1 -0
  233. package/dist/webinar/index.js +69 -0
  234. package/dist/webinar/index.js.map +1 -0
  235. package/package.json +22 -19
  236. package/src/annotation/annotation.types.ts +50 -0
  237. package/src/annotation/constants.ts +36 -0
  238. package/src/annotation/index.ts +328 -0
  239. package/src/breakouts/README.md +35 -11
  240. package/src/breakouts/breakout.ts +67 -9
  241. package/src/breakouts/events.ts +56 -0
  242. package/src/breakouts/index.ts +558 -59
  243. package/src/breakouts/utils.ts +42 -0
  244. package/src/common/errors/no-meeting-info.ts +24 -0
  245. package/src/common/errors/reclaim-host-role-errors.ts +134 -0
  246. package/src/common/errors/webex-errors.ts +44 -2
  247. package/src/common/logs/logger-proxy.ts +1 -1
  248. package/src/common/logs/request.ts +5 -1
  249. package/src/common/queue.ts +22 -8
  250. package/src/config.ts +4 -9
  251. package/src/constants.ts +214 -21
  252. package/src/controls-options-manager/enums.ts +12 -0
  253. package/src/controls-options-manager/index.ts +116 -21
  254. package/src/controls-options-manager/types.ts +59 -0
  255. package/src/controls-options-manager/util.ts +294 -14
  256. package/src/index.ts +44 -0
  257. package/src/interceptors/index.ts +3 -0
  258. package/src/interceptors/locusRetry.ts +67 -0
  259. package/src/interpretation/README.md +60 -0
  260. package/src/interpretation/collection.ts +19 -0
  261. package/src/interpretation/index.ts +332 -0
  262. package/src/interpretation/siLanguage.ts +18 -0
  263. package/src/locus-info/controlsUtils.ts +108 -0
  264. package/src/locus-info/index.ts +414 -59
  265. package/src/locus-info/infoUtils.ts +10 -2
  266. package/src/locus-info/mediaSharesUtils.ts +64 -0
  267. package/src/locus-info/parser.ts +258 -47
  268. package/src/locus-info/selfUtils.ts +81 -5
  269. package/src/media/index.ts +97 -107
  270. package/src/media/properties.ts +88 -117
  271. package/src/mediaQualityMetrics/config.ts +0 -135
  272. package/src/meeting/in-meeting-actions.ts +171 -3
  273. package/src/meeting/index.ts +3253 -2408
  274. package/src/meeting/locusMediaRequest.ts +313 -0
  275. package/src/meeting/muteState.ts +223 -136
  276. package/src/meeting/request.ts +142 -120
  277. package/src/meeting/util.ts +588 -395
  278. package/src/meeting-info/index.ts +81 -8
  279. package/src/meeting-info/meeting-info-v2.ts +170 -14
  280. package/src/meeting-info/util.ts +1 -1
  281. package/src/meeting-info/utilv2.ts +23 -23
  282. package/src/meetings/collection.ts +33 -0
  283. package/src/meetings/index.ts +497 -126
  284. package/src/meetings/meetings.types.ts +12 -0
  285. package/src/meetings/request.ts +2 -0
  286. package/src/meetings/util.ts +80 -11
  287. package/src/member/index.ts +58 -0
  288. package/src/member/types.ts +38 -0
  289. package/src/member/util.ts +141 -25
  290. package/src/members/collection.ts +8 -0
  291. package/src/members/index.ts +134 -8
  292. package/src/members/request.ts +97 -17
  293. package/src/members/types.ts +29 -0
  294. package/src/members/util.ts +333 -240
  295. package/src/metrics/constants.ts +15 -0
  296. package/src/metrics/index.ts +1 -469
  297. package/src/multistream/mediaRequestManager.ts +277 -82
  298. package/src/multistream/receiveSlot.ts +31 -17
  299. package/src/multistream/receiveSlotManager.ts +34 -24
  300. package/src/multistream/remoteMedia.ts +27 -2
  301. package/src/multistream/remoteMediaGroup.ts +59 -0
  302. package/src/multistream/remoteMediaManager.ts +148 -30
  303. package/src/multistream/sendSlotManager.ts +170 -0
  304. package/src/reachability/clusterReachability.ts +320 -0
  305. package/src/reachability/index.ts +229 -340
  306. package/src/reachability/request.ts +8 -4
  307. package/src/reachability/util.ts +24 -0
  308. package/src/reconnection-manager/index.ts +128 -106
  309. package/src/recording-controller/index.ts +20 -3
  310. package/src/recording-controller/util.ts +26 -9
  311. package/src/roap/index.ts +52 -23
  312. package/src/roap/request.ts +48 -67
  313. package/src/roap/turnDiscovery.ts +48 -26
  314. package/src/rtcMetrics/constants.ts +3 -0
  315. package/src/rtcMetrics/index.ts +124 -0
  316. package/src/statsAnalyzer/index.ts +220 -291
  317. package/src/statsAnalyzer/mqaUtil.ts +43 -44
  318. package/src/webinar/collection.ts +31 -0
  319. package/src/webinar/index.ts +62 -0
  320. package/test/integration/spec/converged-space-meetings.js +60 -3
  321. package/test/integration/spec/journey.js +320 -261
  322. package/test/integration/spec/space-meeting.js +76 -3
  323. package/test/unit/spec/annotation/index.ts +418 -0
  324. package/test/unit/spec/breakouts/breakout.ts +118 -28
  325. package/test/unit/spec/breakouts/events.ts +89 -0
  326. package/test/unit/spec/breakouts/index.ts +1349 -114
  327. package/test/unit/spec/breakouts/utils.js +52 -1
  328. package/test/unit/spec/common/queue.js +31 -2
  329. package/test/unit/spec/controls-options-manager/index.js +163 -0
  330. package/test/unit/spec/controls-options-manager/util.js +576 -60
  331. package/test/unit/spec/fixture/locus.js +1 -0
  332. package/test/unit/spec/interceptors/locusRetry.ts +131 -0
  333. package/test/unit/spec/interpretation/collection.ts +15 -0
  334. package/test/unit/spec/interpretation/index.ts +589 -0
  335. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  336. package/test/unit/spec/locus-info/controlsUtils.js +316 -43
  337. package/test/unit/spec/locus-info/index.js +1352 -33
  338. package/test/unit/spec/locus-info/infoUtils.js +37 -15
  339. package/test/unit/spec/locus-info/lib/SeqCmp.json +16 -0
  340. package/test/unit/spec/locus-info/mediaSharesUtils.ts +32 -0
  341. package/test/unit/spec/locus-info/parser.js +116 -35
  342. package/test/unit/spec/locus-info/selfConstant.js +27 -4
  343. package/test/unit/spec/locus-info/selfUtils.js +208 -17
  344. package/test/unit/spec/media/index.ts +118 -37
  345. package/test/unit/spec/media/properties.ts +2 -2
  346. package/test/unit/spec/meeting/in-meeting-actions.ts +85 -3
  347. package/test/unit/spec/meeting/index.js +6686 -2212
  348. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  349. package/test/unit/spec/meeting/muteState.js +402 -212
  350. package/test/unit/spec/meeting/request.js +459 -52
  351. package/test/unit/spec/meeting/utils.js +680 -67
  352. package/test/unit/spec/meeting-info/index.js +300 -0
  353. package/test/unit/spec/meeting-info/meetinginfov2.js +526 -5
  354. package/test/unit/spec/meeting-info/utilv2.js +21 -0
  355. package/test/unit/spec/meetings/collection.js +26 -0
  356. package/test/unit/spec/meetings/index.js +1393 -212
  357. package/test/unit/spec/meetings/utils.js +202 -2
  358. package/test/unit/spec/member/index.js +61 -6
  359. package/test/unit/spec/member/util.js +510 -34
  360. package/test/unit/spec/members/index.js +432 -1
  361. package/test/unit/spec/members/request.js +206 -27
  362. package/test/unit/spec/members/utils.js +210 -0
  363. package/test/unit/spec/metrics/index.js +1 -50
  364. package/test/unit/spec/multistream/mediaRequestManager.ts +776 -162
  365. package/test/unit/spec/multistream/receiveSlot.ts +28 -20
  366. package/test/unit/spec/multistream/receiveSlotManager.ts +32 -30
  367. package/test/unit/spec/multistream/remoteMedia.ts +30 -0
  368. package/test/unit/spec/multistream/remoteMediaGroup.ts +266 -0
  369. package/test/unit/spec/multistream/remoteMediaManager.ts +326 -0
  370. package/test/unit/spec/multistream/sendSlotManager.ts +242 -0
  371. package/test/unit/spec/reachability/clusterReachability.ts +279 -0
  372. package/test/unit/spec/reachability/index.ts +477 -14
  373. package/test/unit/spec/reachability/request.js +3 -1
  374. package/test/unit/spec/reachability/util.ts +40 -0
  375. package/test/unit/spec/reconnection-manager/index.js +118 -11
  376. package/test/unit/spec/recording-controller/index.js +294 -218
  377. package/test/unit/spec/recording-controller/util.js +223 -96
  378. package/test/unit/spec/roap/index.ts +180 -83
  379. package/test/unit/spec/roap/request.ts +100 -62
  380. package/test/unit/spec/roap/turnDiscovery.ts +76 -34
  381. package/test/unit/spec/rtcMetrics/index.ts +93 -0
  382. package/test/unit/spec/stats-analyzer/index.js +621 -12
  383. package/test/unit/spec/webinar/collection.ts +13 -0
  384. package/test/unit/spec/webinar/index.ts +60 -0
  385. package/test/utils/integrationTestUtils.js +46 -0
  386. package/test/utils/testUtils.js +0 -57
  387. package/dist/metrics/config.js +0 -289
  388. package/dist/metrics/config.js.map +0 -1
  389. package/dist/types/metrics/config.d.ts +0 -169
  390. package/src/index.js +0 -16
  391. package/src/metrics/config.ts +0 -485
@@ -1,10 +1,7 @@
1
- import {isEmpty} from 'lodash';
2
- import {LocalCameraTrack, LocalMicrophoneTrack} from '@webex/internal-media-core';
1
+ import {LocalCameraStream, LocalMicrophoneStream} from '@webex/media-helpers';
3
2
 
3
+ import {cloneDeep} from 'lodash';
4
4
  import {MeetingNotActiveError, UserNotJoinedError} from '../common/errors/webex-errors';
5
- import Metrics from '../metrics';
6
- import {eventType, trigger} from '../metrics/config';
7
- import Media from '../media';
8
5
  import LoggerProxy from '../common/logs/logger-proxy';
9
6
  import {
10
7
  INTENT_TO_JOIN,
@@ -14,491 +11,687 @@ import {
14
11
  PASSWORD_STATUS,
15
12
  DISPLAY_HINTS,
16
13
  FULL_STATE,
14
+ SELF_POLICY,
15
+ EVENT_TRIGGERS,
16
+ IP_VERSION,
17
17
  } from '../constants';
18
+ import BrowserDetection from '../common/browser-detection';
18
19
  import IntentToJoinError from '../common/errors/intent-to-join';
19
20
  import JoinMeetingError from '../common/errors/join-meeting';
20
21
  import ParameterError from '../common/errors/parameter';
21
22
  import PermissionError from '../common/errors/permission';
22
23
  import PasswordError from '../common/errors/password-error';
23
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
+ });
24
67
 
25
- 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
+ },
26
89
 
27
- MeetingUtil.parseLocusJoin = (response) => {
28
- const parsed: any = {};
90
+ hasOwner: (info) => info && info.owner,
29
91
 
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;
92
+ isOwnerSelf: (owner, selfId) => owner === selfId,
36
93
 
37
- // we need mediaId before making roap calls
38
- parsed.mediaConnections.forEach((mediaConnection) => {
39
- if (mediaConnection.mediaId) {
40
- 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;
41
111
  }
42
- });
43
112
 
44
- return parsed;
45
- };
113
+ if (supportsIpV4 && supportsIpV6) {
114
+ return IP_VERSION.ipv4_and_ipv6;
115
+ }
46
116
 
47
- MeetingUtil.remoteUpdateAudioVideo = (audioMuted, videoMuted, meeting) => {
48
- if (!meeting) {
49
- return Promise.reject(new ParameterError('You need a meeting object.'));
50
- }
51
- const localMedias = Media.generateLocalMedias(meeting.mediaId, audioMuted, videoMuted);
117
+ if (supportsIpV4) {
118
+ return IP_VERSION.only_ipv4;
119
+ }
52
120
 
53
- if (isEmpty(localMedias)) {
54
- return Promise.reject(
55
- new ParameterError('You need a media id on the meeting to change remote audio.')
56
- );
57
- }
121
+ if (supportsIpV6) {
122
+ return IP_VERSION.only_ipv6;
123
+ }
58
124
 
59
- 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
+ });
60
180
 
61
- return meeting.meetingRequest
62
- .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 = {
63
213
  locusUrl: meeting.locusUrl,
64
214
  selfId: meeting.selfId,
65
- localMedias,
66
- deviceUrl: meeting.deviceUrl,
67
215
  correlationId: meeting.correlationId,
68
- preferTranscoding: !meeting.isMultistream,
69
- })
70
- .then((response) => {
71
- 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
+ );
72
223
 
73
- return response.body.locus;
224
+ return Promise.reject(err);
74
225
  });
75
- };
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
+ }
76
255
 
77
- MeetingUtil.hasOwner = (info) => info && info.owner;
256
+ if (MeetingUtil.isUserInLeftState(meeting.locusInfo)) {
257
+ return Promise.reject(new UserNotJoinedError());
258
+ }
78
259
 
79
- 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
+ }),
80
288
 
81
- MeetingUtil.isPinOrGuest = (err) =>
82
- err?.body?.errorCode && INTENT_TO_JOIN.includes(err.body.errorCode);
289
+ isUserInLeftState: (locusInfo) => locusInfo.parsedLocus?.self?.state === _LEFT_,
83
290
 
84
- MeetingUtil.joinMeeting = (meeting, options) => {
85
- if (!meeting) {
86
- return Promise.reject(new ParameterError('You need a meeting object.'));
87
- }
291
+ isUserInIdleState: (locusInfo) => locusInfo.parsedLocus?.self?.state === _IDLE_,
88
292
 
89
- Metrics.postEvent({event: eventType.LOCUS_JOIN_REQUEST, meeting});
293
+ isUserInJoinedState: (locusInfo) => locusInfo.parsedLocus?.self?.state === _JOINED_,
90
294
 
91
- // eslint-disable-next-line no-warning-comments
92
- // TODO: check if the meeting is in JOINING state
93
- // if Joining state termintate the request as user might click multiple times
94
- return meeting.meetingRequest
95
- .joinMeeting({
96
- inviteeAddress: meeting.meetingJoinUrl || meeting.sipUri,
97
- meetingNumber: meeting.meetingNumber,
98
- deviceUrl: meeting.deviceUrl,
99
- locusUrl: meeting.locusUrl,
100
- correlationId: meeting.correlationId,
101
- roapMessage: options.roapMessage,
102
- permissionToken: meeting.permissionToken,
103
- resourceId: options.resourceId || null,
104
- moderator: options.moderator,
105
- pin: options.pin,
106
- moveToResource: options.moveToResource,
107
- preferTranscoding: !meeting.isMultistream,
108
- asResourceOccupant: options.asResourceOccupant,
109
- breakoutsSupported: options.breakoutsSupported,
110
- })
111
- .then((res) => {
112
- Metrics.postEvent({
113
- event: eventType.LOCUS_JOIN_RESPONSE,
114
- meeting,
115
- data: {
116
- trigger: trigger.LOCI_UPDATE,
117
- locus: res.body.locus,
118
- mediaConnections: res.body.mediaConnections,
119
- 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,
120
317
  },
121
318
  });
319
+ }
122
320
 
123
- return MeetingUtil.parseLocusJoin(res);
124
- });
125
- };
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
+ }
126
379
 
127
- MeetingUtil.cleanUp = (meeting) => {
128
- meeting.breakouts.cleanUp();
129
-
130
- // make sure we send last metrics before we close the peerconnection
131
- const stopStatsAnalyzer = meeting.statsAnalyzer
132
- ? meeting.statsAnalyzer.stopAnalyzer()
133
- : Promise.resolve();
134
-
135
- return stopStatsAnalyzer
136
- .then(() => meeting.closeLocalStream())
137
- .then(() => meeting.closeLocalShare())
138
- .then(() => meeting.closeRemoteTracks())
139
- .then(() => meeting.closePeerConnections())
140
- .then(() => {
141
- meeting.unsetLocalVideoTrack();
142
- meeting.unsetLocalShareTrack();
143
- meeting.unsetRemoteTracks();
144
- meeting.unsetPeerConnections();
145
- meeting.reconnectionManager.cleanUp();
146
- })
147
- .then(() => meeting.stopKeepAlive())
148
- .then(() => meeting.updateLLMConnection());
149
- };
380
+ if (audioTracks && audioTracks.length > 0) {
381
+ [audioTrack] = audioTracks;
382
+ }
150
383
 
151
- MeetingUtil.disconnectPhoneAudio = (meeting, phoneUrl) => {
152
- if (meeting.meetingState === FULL_STATE.INACTIVE) {
153
- return Promise.reject(new MeetingNotActiveError());
154
- }
155
-
156
- const options = {
157
- locusUrl: meeting.locusUrl,
158
- selfId: meeting.selfId,
159
- correlationId: meeting.correlationId,
160
- phoneUrl,
161
- };
162
-
163
- return meeting.meetingRequest
164
- .disconnectPhoneAudio(options)
165
- .then((response) => {
166
- if (response?.body?.locus) {
167
- meeting.locusInfo.onFullLocus(response.body.locus);
168
- }
169
- })
170
- .catch((err) => {
171
- LoggerProxy.logger.error(
172
- `Meeting:util#disconnectPhoneAudio --> An error occured while disconnecting phone audio in meeting ${meeting.id}, error: ${err}`
173
- );
384
+ if (videoTracks && videoTracks.length > 0) {
385
+ [videoTrack] = videoTracks;
386
+ }
174
387
 
175
- return Promise.reject(err);
176
- });
177
- };
388
+ return {audioTrack, videoTrack};
389
+ },
178
390
 
179
- // by default will leave on meeting's resourceId
180
- // if you explicity want it not to leave on resource id, pass
181
- // {resourceId: null}
182
- // TODO: chris, you can modify this however you want
183
- MeetingUtil.leaveMeeting = (meeting, options: any = {}) => {
184
- if (meeting.meetingState === FULL_STATE.INACTIVE) {
185
- // TODO: clean up if the meeting is already inactive
186
- return Promise.reject(new MeetingNotActiveError());
187
- }
188
-
189
- if (MeetingUtil.isUserInLeftState(meeting.locusInfo)) {
190
- return Promise.reject(new UserNotJoinedError());
191
- }
192
-
193
- const defaultOptions = {
194
- locusUrl: meeting.locusUrl,
195
- selfId: meeting.selfId,
196
- correlationId: meeting.correlationId,
197
- resourceId: meeting.resourceId,
198
- deviceUrl: meeting.deviceUrl,
199
- };
200
-
201
- const leaveOptions = {...defaultOptions, ...options};
202
-
203
- return meeting.meetingRequest
204
- .leaveMeeting(leaveOptions)
205
- .then((response) => {
206
- if (response && response.body && response.body.locus) {
207
- // && !options.moveMeeting) {
208
- meeting.locusInfo.onFullLocus(response.body.locus);
209
- }
391
+ getModeratorFromLocusInfo: (locusInfo) =>
392
+ locusInfo &&
393
+ locusInfo.parsedLocus &&
394
+ locusInfo.parsedLocus.info &&
395
+ locusInfo.parsedLocus.info &&
396
+ locusInfo.parsedLocus.info.moderator,
210
397
 
211
- return Promise.resolve();
212
- })
213
- .then(() => {
214
- if (options.moveMeeting) {
215
- return Promise.resolve();
216
- }
398
+ getPolicyFromLocusInfo: (locusInfo) =>
399
+ locusInfo &&
400
+ locusInfo.parsedLocus &&
401
+ locusInfo.parsedLocus.info &&
402
+ locusInfo.parsedLocus.info &&
403
+ locusInfo.parsedLocus.info.policy,
217
404
 
218
- return MeetingUtil.cleanUp(meeting);
219
- })
220
- .catch((err) => {
221
- // TODO: If the meeting state comes as LEFT or INACTIVE as response then
222
- // 1) on leave clean up the meeting or simply do a sync on the meeting
223
- // 2) If the error says meeting is inactive then destroy the meeting object
224
- LoggerProxy.logger.error(
225
- `Meeting:util#leaveMeeting --> An error occured while trying to leave meeting with an id of ${meeting.id}, error: ${err}`
226
- );
405
+ getUserDisplayHintsFromLocusInfo: (locusInfo) =>
406
+ locusInfo?.parsedLocus?.info?.userDisplayHints || [],
227
407
 
228
- return Promise.reject(err);
229
- });
230
- };
231
- MeetingUtil.declineMeeting = (meeting, reason) =>
232
- meeting.meetingRequest.declineMeeting({
233
- locusUrl: meeting.locusUrl,
234
- deviceUrl: meeting.deviceUrl,
235
- reason,
236
- });
408
+ canInviteNewParticipants: (displayHints) => displayHints.includes(DISPLAY_HINTS.ADD_GUEST),
237
409
 
238
- MeetingUtil.isUserInLeftState = (locusInfo) => locusInfo.parsedLocus?.self?.state === _LEFT_;
410
+ canAdmitParticipant: (displayHints) =>
411
+ displayHints.includes(DISPLAY_HINTS.ROSTER_WAITING_TO_JOIN),
239
412
 
240
- 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),
241
416
 
242
- 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),
243
420
 
244
- MeetingUtil.isMediaEstablished = (currentMediaStatus) =>
245
- currentMediaStatus &&
246
- (currentMediaStatus.audio || currentMediaStatus.video || currentMediaStatus.share);
421
+ canUserRaiseHand: (displayHints) => displayHints.includes(DISPLAY_HINTS.RAISE_HAND),
247
422
 
248
- MeetingUtil.joinMeetingOptions = (meeting, options: any = {}) => {
249
- meeting.resourceId = meeting.resourceId || options.resourceId;
423
+ canUserLowerAllHands: (displayHints) => displayHints.includes(DISPLAY_HINTS.LOWER_ALL_HANDS),
250
424
 
251
- if (meeting.requiredCaptcha) {
252
- return Promise.reject(new CaptchaError());
253
- }
254
- if (meeting.passwordStatus === PASSWORD_STATUS.REQUIRED) {
255
- return Promise.reject(new PasswordError());
256
- }
425
+ canUserLowerSomeoneElsesHand: (displayHints) =>
426
+ displayHints.includes(DISPLAY_HINTS.LOWER_SOMEONE_ELSES_HAND),
257
427
 
258
- if (options.pin) {
259
- Metrics.postEvent({
260
- event: eventType.PIN_COLLECTED,
261
- meeting,
262
- });
263
- }
264
-
265
- // normal join meeting, scenario A, D
266
- return MeetingUtil.joinMeeting(meeting, options)
267
- .then((response) => {
268
- meeting.setLocus(response);
269
-
270
- return Promise.resolve(response);
271
- })
272
- .catch((err) => {
273
- // joining a claimed PMR that is not my own, scenario B
274
- if (MeetingUtil.isPinOrGuest(err)) {
275
- Metrics.postEvent({
276
- event: eventType.PIN_PROMPT,
277
- meeting,
278
- });
428
+ bothLeaveAndEndMeetingAvailable: (displayHints) =>
429
+ displayHints.includes(DISPLAY_HINTS.LEAVE_TRANSFER_HOST_END_MEETING) ||
430
+ displayHints.includes(DISPLAY_HINTS.LEAVE_END_MEETING),
279
431
 
280
- // request host pin or non host for unclaimed PMR, start of Scenario C
281
- // see https://sqbu-github.cisco.com/WebExSquared/locus/wiki/Locus-Lobby-and--IVR-Feature
282
- return Promise.reject(new IntentToJoinError('Error Joining Meeting', err));
283
- }
284
- 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],
285
436
 
286
- return Promise.reject(new JoinMeetingError(options, 'Error Joining Meeting', err));
287
- });
288
- };
437
+ isSuppressBreakoutSupport: (displayHints) =>
438
+ displayHints.includes(DISPLAY_HINTS.UCF_SUPPRESS_BREAKOUTS_SUPPORT),
289
439
 
290
- MeetingUtil.validateOptions = (options) => {
291
- const {sendVideo, sendAudio, sendShare, localStream, localShare} = options;
440
+ canAdmitLobbyToBreakout: (displayHints) =>
441
+ !displayHints.includes(DISPLAY_HINTS.DISABLE_LOBBY_TO_BREAKOUT),
292
442
 
293
- if (sendVideo && !MeetingUtil.getTrack(localStream).videoTrack) {
294
- return Promise.reject(new ParameterError('please pass valid video streams'));
295
- }
443
+ isBreakoutPreassignmentsEnabled: (displayHints) =>
444
+ !displayHints.includes(DISPLAY_HINTS.DISABLE_BREAKOUT_PREASSIGNMENTS),
296
445
 
297
- if (sendAudio && !MeetingUtil.getTrack(localStream).audioTrack) {
298
- return Promise.reject(new ParameterError('please pass valid audio streams'));
299
- }
446
+ canUserAskForHelp: (displayHints) => !displayHints.includes(DISPLAY_HINTS.DISABLE_ASK_FOR_HELP),
300
447
 
301
- if (sendShare && !MeetingUtil.getTrack(localShare).videoTrack) {
302
- return Promise.reject(new ParameterError('please pass valid share streams'));
303
- }
448
+ lockMeeting: (actions, request, locusUrl) => {
449
+ if (actions && actions.canLock) {
450
+ return request.lockMeeting({locusUrl, lock: true});
451
+ }
304
452
 
305
- return Promise.resolve();
306
- };
453
+ return Promise.reject(new PermissionError('Lock not allowed, due to joined property.'));
454
+ },
307
455
 
308
- MeetingUtil.getTrack = (stream) => {
309
- let audioTrack = null;
310
- let videoTrack = null;
311
- let audioTracks = null;
312
- let videoTracks = null;
313
-
314
- if (!stream) {
315
- return {audioTrack: null, videoTrack: null};
316
- }
317
- if (stream.getAudioTracks) {
318
- audioTracks = stream.getAudioTracks();
319
- }
320
- if (stream.getVideoTracks) {
321
- videoTracks = stream.getVideoTracks();
322
- }
323
-
324
- if (audioTracks && audioTracks.length > 0) {
325
- [audioTrack] = audioTracks;
326
- }
327
-
328
- if (videoTracks && videoTracks.length > 0) {
329
- [videoTrack] = videoTracks;
330
- }
331
-
332
- return {audioTrack, videoTrack};
333
- };
456
+ unlockMeeting: (actions, request, locusUrl) => {
457
+ if (actions && actions.canUnlock) {
458
+ return request.lockMeeting({locusUrl, lock: false});
459
+ }
334
460
 
335
- MeetingUtil.getModeratorFromLocusInfo = (locusInfo) =>
336
- locusInfo &&
337
- locusInfo.parsedLocus &&
338
- locusInfo.parsedLocus.info &&
339
- locusInfo.parsedLocus.info &&
340
- locusInfo.parsedLocus.info.moderator;
461
+ return Promise.reject(new PermissionError('Unlock not allowed, due to joined property.'));
462
+ },
341
463
 
342
- MeetingUtil.getPolicyFromLocusInfo = (locusInfo) =>
343
- locusInfo &&
344
- locusInfo.parsedLocus &&
345
- locusInfo.parsedLocus.info &&
346
- locusInfo.parsedLocus.info &&
347
- locusInfo.parsedLocus.info.policy;
464
+ handleAudioLogging: (audioStream?: LocalMicrophoneStream) => {
465
+ const LOG_HEADER = 'MeetingUtil#handleAudioLogging -->';
348
466
 
349
- MeetingUtil.getUserDisplayHintsFromLocusInfo = (locusInfo) =>
350
- locusInfo?.parsedLocus?.info?.userDisplayHints || [];
467
+ if (audioStream) {
468
+ const settings = audioStream.getSettings();
469
+ const {deviceId} = settings;
351
470
 
352
- MeetingUtil.canInviteNewParticipants = (displayHints) =>
353
- 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
+ },
354
475
 
355
- MeetingUtil.canAdmitParticipant = (displayHints) =>
356
- displayHints.includes(DISPLAY_HINTS.ROSTER_WAITING_TO_JOIN);
476
+ handleVideoLogging: (videoStream?: LocalCameraStream) => {
477
+ const LOG_HEADER = 'MeetingUtil#handleVideoLogging -->';
357
478
 
358
- MeetingUtil.canUserLock = (displayHints) =>
359
- displayHints.includes(DISPLAY_HINTS.LOCK_CONTROL_LOCK) &&
360
- displayHints.includes(DISPLAY_HINTS.LOCK_STATUS_UNLOCKED);
479
+ if (videoStream) {
480
+ const settings = videoStream.getSettings();
481
+ const {deviceId} = settings;
361
482
 
362
- MeetingUtil.canUserUnlock = (displayHints) =>
363
- displayHints.includes(DISPLAY_HINTS.LOCK_CONTROL_UNLOCK) &&
364
- 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
+ },
365
487
 
366
- MeetingUtil.canUserRaiseHand = (displayHints) => displayHints.includes(DISPLAY_HINTS.RAISE_HAND);
488
+ handleDeviceLogging: (devices = []) => {
489
+ const LOG_HEADER = 'MeetingUtil#handleDeviceLogging -->';
367
490
 
368
- MeetingUtil.canUserLowerAllHands = (displayHints) =>
369
- 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
+ },
370
496
 
371
- MeetingUtil.canUserLowerSomeoneElsesHand = (displayHints) =>
372
- 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
+ }
373
501
 
374
- MeetingUtil.bothLeaveAndEndMeetingAvailable = (displayHints) =>
375
- displayHints.includes(DISPLAY_HINTS.LEAVE_TRANSFER_HOST_END_MEETING) ||
376
- displayHints.includes(DISPLAY_HINTS.LEAVE_END_MEETING);
502
+ const endOptions = {
503
+ locusUrl: meeting.locusUrl,
504
+ };
377
505
 
378
- MeetingUtil.lockMeeting = (actions, request, locusUrl) => {
379
- if (actions && actions.canLock) {
380
- return request.lockMeeting({locusUrl, lock: true});
381
- }
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
+ );
382
513
 
383
- return Promise.reject(new PermissionError('Lock not allowed, due to joined property.'));
384
- };
514
+ return Promise.reject(err);
515
+ });
516
+ },
385
517
 
386
- MeetingUtil.unlockMeeting = (actions, request, locusUrl) => {
387
- if (actions && actions.canUnlock) {
388
- return request.lockMeeting({locusUrl, lock: false});
389
- }
518
+ canEnableClosedCaption: (displayHints) => displayHints.includes(DISPLAY_HINTS.CAPTION_START),
390
519
 
391
- return Promise.reject(new PermissionError('Unlock not allowed, due to joined property.'));
392
- };
520
+ isSaveTranscriptsEnabled: (displayHints) =>
521
+ displayHints.includes(DISPLAY_HINTS.SAVE_TRANSCRIPTS_ENABLED),
393
522
 
394
- MeetingUtil.handleAudioLogging = (audioTrack: LocalMicrophoneTrack | null) => {
395
- const LOG_HEADER = 'MeetingUtil#handleAudioLogging -->';
523
+ canStartTranscribing: (displayHints) =>
524
+ displayHints.includes(DISPLAY_HINTS.TRANSCRIPTION_CONTROL_START),
396
525
 
397
- if (audioTrack) {
398
- const settings = audioTrack.underlyingTrack.getSettings();
399
- const {deviceId} = settings;
526
+ canStopTranscribing: (displayHints) =>
527
+ displayHints.includes(DISPLAY_HINTS.TRANSCRIPTION_CONTROL_STOP),
400
528
 
401
- LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${deviceId}`);
402
- LoggerProxy.logger.log(LOG_HEADER, 'settings =', JSON.stringify(settings));
403
- }
404
- };
529
+ isClosedCaptionActive: (displayHints) =>
530
+ displayHints.includes(DISPLAY_HINTS.CAPTION_STATUS_ACTIVE),
405
531
 
406
- MeetingUtil.handleVideoLogging = (videoTrack: LocalCameraTrack | null) => {
407
- const LOG_HEADER = 'MeetingUtil#handleVideoLogging -->';
532
+ isWebexAssistantActive: (displayHints) =>
533
+ displayHints.includes(DISPLAY_HINTS.WEBEX_ASSISTANT_STATUS_ACTIVE),
408
534
 
409
- if (videoTrack) {
410
- const settings = videoTrack.underlyingTrack.getSettings();
411
- const {deviceId} = settings;
535
+ canViewCaptionPanel: (displayHints) => displayHints.includes(DISPLAY_HINTS.ENABLE_CAPTION_PANEL),
412
536
 
413
- LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${deviceId}`);
414
- LoggerProxy.logger.log(LOG_HEADER, 'settings =', JSON.stringify(settings));
415
- }
416
- };
537
+ isRealTimeTranslationEnabled: (displayHints) =>
538
+ displayHints.includes(DISPLAY_HINTS.DISPLAY_REAL_TIME_TRANSLATION),
417
539
 
418
- MeetingUtil.handleDeviceLogging = (devices = []) => {
419
- const LOG_HEADER = 'MeetingUtil#handleDeviceLogging -->';
540
+ canSelectSpokenLanguages: (displayHints) =>
541
+ displayHints.includes(DISPLAY_HINTS.DISPLAY_NON_ENGLISH_ASR),
420
542
 
421
- devices.forEach((device) => {
422
- LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${device.deviceId}`);
423
- LoggerProxy.logger.log(LOG_HEADER, 'settings', JSON.stringify(device));
424
- });
425
- };
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),
426
559
 
427
- MeetingUtil.endMeetingForAll = (meeting) => {
428
- if (meeting.meetingState === FULL_STATE.INACTIVE) {
429
- return Promise.reject(new MeetingNotActiveError());
430
- }
560
+ canShareWhiteBoard: (displayHints) => displayHints.includes(DISPLAY_HINTS.SHARE_WHITEBOARD),
431
561
 
432
- const endOptions = {
433
- locusUrl: meeting.locusUrl,
434
- };
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
+ }
435
574
 
436
- return meeting.meetingRequest
437
- .endMeetingForAll(endOptions)
438
- .then((response) => {
439
- if (response && response.body && response.body.locus) {
440
- 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;
441
608
  }
442
609
 
443
- return Promise.resolve();
444
- })
445
- .then(() => MeetingUtil.cleanUp(meeting))
446
- .catch((err) => {
447
- LoggerProxy.logger.error(
448
- `Meeting:util#endMeetingForAll An error occured while trying to end meeting for all with an id of ${meeting.id}, error: ${err}`
449
- );
610
+ const options = cloneDeep(originalOptions);
450
611
 
451
- return Promise.reject(err);
452
- });
453
- };
612
+ if (!options.body) {
613
+ options.body = {};
614
+ }
454
615
 
455
- MeetingUtil.canEnableClosedCaption = (displayHints) =>
456
- displayHints.includes(DISPLAY_HINTS.CAPTION_START);
616
+ MeetingUtil.addSequence(meeting, options.body);
457
617
 
458
- MeetingUtil.canStartTranscribing = (displayHints) =>
459
- displayHints.includes(DISPLAY_HINTS.TRANSCRIPTION_CONTROL_START);
618
+ return options;
619
+ };
460
620
 
461
- MeetingUtil.canStopTranscribing = (displayHints) =>
462
- displayHints.includes(DISPLAY_HINTS.TRANSCRIPTION_CONTROL_STOP);
621
+ return buildLocusDeltaRequestOptions;
622
+ },
463
623
 
464
- MeetingUtil.isClosedCaptionActive = (displayHints) =>
465
- displayHints.includes(DISPLAY_HINTS.CAPTION_STATUS_ACTIVE);
624
+ generateLocusDeltaRequest: (originalMeeting) => {
625
+ const meetingRef = new WeakRef(originalMeeting);
466
626
 
467
- MeetingUtil.isWebexAssistantActive = (displayHints) =>
468
- displayHints.includes(DISPLAY_HINTS.WEBEX_ASSISTANT_STATUS_ACTIVE);
627
+ const buildLocusDeltaRequestOptions =
628
+ MeetingUtil.generateBuildLocusDeltaRequestOptions(originalMeeting);
469
629
 
470
- MeetingUtil.canViewCaptionPanel = (displayHints) =>
471
- displayHints.includes(DISPLAY_HINTS.ENABLE_CAPTION_PANEL);
630
+ const locusDeltaRequest = (originalOptions) => {
631
+ const meeting = meetingRef.deref();
472
632
 
473
- MeetingUtil.isRealTimeTranslationEnabled = (displayHints) =>
474
- displayHints.includes(DISPLAY_HINTS.DISPLAY_REAL_TIME_TRANSLATION);
633
+ if (!meeting) {
634
+ return Promise.resolve();
635
+ }
475
636
 
476
- MeetingUtil.canSelectSpokenLanguages = (displayHints) =>
477
- displayHints.includes(DISPLAY_HINTS.DISPLAY_NON_ENGLISH_ASR);
637
+ const options = buildLocusDeltaRequestOptions(originalOptions);
478
638
 
479
- MeetingUtil.waitingForOthersToJoin = (displayHints) =>
480
- displayHints.includes(DISPLAY_HINTS.WAITING_FOR_OTHERS);
639
+ return meeting
640
+ .request(options)
641
+ .then((response) => MeetingUtil.updateLocusWithDelta(meeting, response));
642
+ };
481
643
 
482
- MeetingUtil.canEnableReactions = (originalValue, displayHints) => {
483
- if (displayHints.includes(DISPLAY_HINTS.ENABLE_REACTIONS)) {
484
- return true;
485
- }
486
- if (displayHints.includes(DISPLAY_HINTS.DISABLE_REACTIONS)) {
487
- return false;
488
- }
644
+ return locusDeltaRequest;
645
+ },
489
646
 
490
- return originalValue;
491
- };
647
+ selfSupportsFeature: (feature: SELF_POLICY, userPolicies: Record<SELF_POLICY, boolean>) => {
648
+ if (!userPolicies) {
649
+ return true;
650
+ }
492
651
 
493
- MeetingUtil.canSendReactions = (originalValue, displayHints) => {
494
- if (displayHints.includes(DISPLAY_HINTS.REACTIONS_ACTIVE)) {
495
- return true;
496
- }
497
- if (displayHints.includes(DISPLAY_HINTS.REACTIONS_INACTIVE)) {
498
- return false;
499
- }
652
+ return userPolicies[feature];
653
+ },
500
654
 
501
- 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
+ },
502
695
  };
503
696
 
504
697
  export default MeetingUtil;