@webex/plugin-meetings 3.0.0-beta.2 → 3.0.0-beta.20

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 (365) hide show
  1. package/UPGRADING.md +9 -9
  2. package/browsers.js +19 -24
  3. package/dist/breakouts/breakout.js +116 -0
  4. package/dist/breakouts/breakout.js.map +1 -0
  5. package/dist/breakouts/collection.js +23 -0
  6. package/dist/breakouts/collection.js.map +1 -0
  7. package/dist/breakouts/index.js +226 -0
  8. package/dist/breakouts/index.js.map +1 -0
  9. package/dist/common/browser-detection.js +1 -20
  10. package/dist/common/browser-detection.js.map +1 -1
  11. package/dist/common/collection.js +5 -20
  12. package/dist/common/collection.js.map +1 -1
  13. package/dist/common/config.js +0 -7
  14. package/dist/common/config.js.map +1 -1
  15. package/dist/common/errors/captcha-error.js +10 -24
  16. package/dist/common/errors/captcha-error.js.map +1 -1
  17. package/dist/common/errors/intent-to-join.js +11 -24
  18. package/dist/common/errors/intent-to-join.js.map +1 -1
  19. package/dist/common/errors/join-meeting.js +12 -25
  20. package/dist/common/errors/join-meeting.js.map +1 -1
  21. package/dist/common/errors/media.js +10 -24
  22. package/dist/common/errors/media.js.map +1 -1
  23. package/dist/common/errors/parameter.js +5 -33
  24. package/dist/common/errors/parameter.js.map +1 -1
  25. package/dist/common/errors/password-error.js +10 -24
  26. package/dist/common/errors/password-error.js.map +1 -1
  27. package/dist/common/errors/permission.js +9 -23
  28. package/dist/common/errors/permission.js.map +1 -1
  29. package/dist/common/errors/reconnection-in-progress.js +0 -17
  30. package/dist/common/errors/reconnection-in-progress.js.map +1 -1
  31. package/dist/common/errors/reconnection.js +10 -24
  32. package/dist/common/errors/reconnection.js.map +1 -1
  33. package/dist/common/errors/stats.js +10 -24
  34. package/dist/common/errors/stats.js.map +1 -1
  35. package/dist/common/errors/webex-errors.js +6 -41
  36. package/dist/common/errors/webex-errors.js.map +1 -1
  37. package/dist/common/errors/webex-meetings-error.js +5 -25
  38. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  39. package/dist/common/events/events-scope.js +0 -22
  40. package/dist/common/events/events-scope.js.map +1 -1
  41. package/dist/common/events/events.js +0 -23
  42. package/dist/common/events/events.js.map +1 -1
  43. package/dist/common/events/trigger-proxy.js +0 -12
  44. package/dist/common/events/trigger-proxy.js.map +1 -1
  45. package/dist/common/events/util.js +0 -15
  46. package/dist/common/events/util.js.map +1 -1
  47. package/dist/common/logs/logger-config.js +0 -4
  48. package/dist/common/logs/logger-config.js.map +1 -1
  49. package/dist/common/logs/logger-proxy.js +1 -8
  50. package/dist/common/logs/logger-proxy.js.map +1 -1
  51. package/dist/common/logs/request.js +37 -60
  52. package/dist/common/logs/request.js.map +1 -1
  53. package/dist/common/queue.js +4 -14
  54. package/dist/common/queue.js.map +1 -1
  55. package/dist/config.js +6 -6
  56. package/dist/config.js.map +1 -1
  57. package/dist/constants.js +88 -46
  58. package/dist/constants.js.map +1 -1
  59. package/dist/index.js +1 -17
  60. package/dist/index.js.map +1 -1
  61. package/dist/locus-info/controlsUtils.js +12 -29
  62. package/dist/locus-info/controlsUtils.js.map +1 -1
  63. package/dist/locus-info/embeddedAppsUtils.js +3 -26
  64. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  65. package/dist/locus-info/fullState.js +0 -15
  66. package/dist/locus-info/fullState.js.map +1 -1
  67. package/dist/locus-info/hostUtils.js +4 -12
  68. package/dist/locus-info/hostUtils.js.map +1 -1
  69. package/dist/locus-info/index.js +184 -190
  70. package/dist/locus-info/index.js.map +1 -1
  71. package/dist/locus-info/infoUtils.js +3 -37
  72. package/dist/locus-info/infoUtils.js.map +1 -1
  73. package/dist/locus-info/mediaSharesUtils.js +12 -38
  74. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  75. package/dist/locus-info/parser.js +92 -118
  76. package/dist/locus-info/parser.js.map +1 -1
  77. package/dist/locus-info/selfUtils.js +34 -91
  78. package/dist/locus-info/selfUtils.js.map +1 -1
  79. package/dist/media/index.js +67 -111
  80. package/dist/media/index.js.map +1 -1
  81. package/dist/media/properties.js +80 -114
  82. package/dist/media/properties.js.map +1 -1
  83. package/dist/media/util.js +2 -9
  84. package/dist/media/util.js.map +1 -1
  85. package/dist/mediaQualityMetrics/config.js +10 -12
  86. package/dist/mediaQualityMetrics/config.js.map +1 -1
  87. package/dist/meeting/effectsState.js +125 -190
  88. package/dist/meeting/effectsState.js.map +1 -1
  89. package/dist/meeting/in-meeting-actions.js +5 -14
  90. package/dist/meeting/in-meeting-actions.js.map +1 -1
  91. package/dist/meeting/index.js +1692 -1925
  92. package/dist/meeting/index.js.map +1 -1
  93. package/dist/meeting/muteState.js +36 -77
  94. package/dist/meeting/muteState.js.map +1 -1
  95. package/dist/meeting/request.js +224 -230
  96. package/dist/meeting/request.js.map +1 -1
  97. package/dist/meeting/request.type.js +7 -0
  98. package/dist/meeting/request.type.js.map +1 -0
  99. package/dist/meeting/state.js +21 -31
  100. package/dist/meeting/state.js.map +1 -1
  101. package/dist/meeting/util.js +43 -215
  102. package/dist/meeting/util.js.map +1 -1
  103. package/dist/meeting-info/collection.js +6 -25
  104. package/dist/meeting-info/collection.js.map +1 -1
  105. package/dist/meeting-info/index.js +14 -32
  106. package/dist/meeting-info/index.js.map +1 -1
  107. package/dist/meeting-info/meeting-info-v2.js +193 -268
  108. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  109. package/dist/meeting-info/request.js +3 -15
  110. package/dist/meeting-info/request.js.map +1 -1
  111. package/dist/meeting-info/util.js +98 -183
  112. package/dist/meeting-info/util.js.map +1 -1
  113. package/dist/meeting-info/utilv2.js +137 -228
  114. package/dist/meeting-info/utilv2.js.map +1 -1
  115. package/dist/meetings/collection.js +5 -20
  116. package/dist/meetings/collection.js.map +1 -1
  117. package/dist/meetings/index.js +490 -560
  118. package/dist/meetings/index.js.map +1 -1
  119. package/dist/meetings/request.js +24 -41
  120. package/dist/meetings/request.js.map +1 -1
  121. package/dist/meetings/util.js +99 -155
  122. package/dist/meetings/util.js.map +1 -1
  123. package/dist/member/index.js +78 -86
  124. package/dist/member/index.js.map +1 -1
  125. package/dist/member/util.js +31 -68
  126. package/dist/member/util.js.map +1 -1
  127. package/dist/members/collection.js +3 -12
  128. package/dist/members/collection.js.map +1 -1
  129. package/dist/members/index.js +93 -200
  130. package/dist/members/index.js.map +1 -1
  131. package/dist/members/request.js +16 -39
  132. package/dist/members/request.js.map +1 -1
  133. package/dist/members/util.js +9 -38
  134. package/dist/members/util.js.map +1 -1
  135. package/dist/metrics/config.js +0 -2
  136. package/dist/metrics/config.js.map +1 -1
  137. package/dist/metrics/constants.js +1 -2
  138. package/dist/metrics/constants.js.map +1 -1
  139. package/dist/metrics/index.js +55 -135
  140. package/dist/metrics/index.js.map +1 -1
  141. package/dist/multistream/mediaRequestManager.js +57 -32
  142. package/dist/multistream/mediaRequestManager.js.map +1 -1
  143. package/dist/multistream/multistreamMedia.js +15 -21
  144. package/dist/multistream/multistreamMedia.js.map +1 -1
  145. package/dist/multistream/receiveSlot.js +10 -50
  146. package/dist/multistream/receiveSlot.js.map +1 -1
  147. package/dist/multistream/receiveSlotManager.js +45 -82
  148. package/dist/multistream/receiveSlotManager.js.map +1 -1
  149. package/dist/multistream/remoteMedia.js +18 -58
  150. package/dist/multistream/remoteMedia.js.map +1 -1
  151. package/dist/multistream/remoteMediaGroup.js +6 -40
  152. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  153. package/dist/multistream/remoteMediaManager.js +362 -416
  154. package/dist/multistream/remoteMediaManager.js.map +1 -1
  155. package/dist/networkQualityMonitor/index.js +36 -57
  156. package/dist/networkQualityMonitor/index.js.map +1 -1
  157. package/dist/personal-meeting-room/index.js +21 -45
  158. package/dist/personal-meeting-room/index.js.map +1 -1
  159. package/dist/personal-meeting-room/request.js +1 -31
  160. package/dist/personal-meeting-room/request.js.map +1 -1
  161. package/dist/personal-meeting-room/util.js +0 -13
  162. package/dist/personal-meeting-room/util.js.map +1 -1
  163. package/dist/reachability/index.js +138 -182
  164. package/dist/reachability/index.js.map +1 -1
  165. package/dist/reachability/request.js +3 -18
  166. package/dist/reachability/request.js.map +1 -1
  167. package/dist/reactions/constants.js +13 -0
  168. package/dist/reactions/constants.js.map +1 -0
  169. package/dist/reactions/reactions.js +109 -0
  170. package/dist/reactions/reactions.js.map +1 -0
  171. package/dist/reactions/reactions.type.js +36 -0
  172. package/dist/reactions/reactions.type.js.map +1 -0
  173. package/dist/reconnection-manager/index.js +322 -455
  174. package/dist/reconnection-manager/index.js.map +1 -1
  175. package/dist/recording-controller/enums.js +17 -0
  176. package/dist/recording-controller/enums.js.map +1 -0
  177. package/dist/recording-controller/index.js +343 -0
  178. package/dist/recording-controller/index.js.map +1 -0
  179. package/dist/recording-controller/util.js +63 -0
  180. package/dist/recording-controller/util.js.map +1 -0
  181. package/dist/roap/index.js +39 -64
  182. package/dist/roap/index.js.map +1 -1
  183. package/dist/roap/request.js +94 -113
  184. package/dist/roap/request.js.map +1 -1
  185. package/dist/roap/turnDiscovery.js +85 -94
  186. package/dist/roap/turnDiscovery.js.map +1 -1
  187. package/dist/statsAnalyzer/global.js +0 -2
  188. package/dist/statsAnalyzer/global.js.map +1 -1
  189. package/dist/statsAnalyzer/index.js +85 -175
  190. package/dist/statsAnalyzer/index.js.map +1 -1
  191. package/dist/statsAnalyzer/mqaUtil.js +72 -53
  192. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  193. package/dist/transcription/index.js +22 -47
  194. package/dist/transcription/index.js.map +1 -1
  195. package/internal-README.md +7 -6
  196. package/package.json +25 -20
  197. package/src/breakouts/README.md +190 -0
  198. package/src/breakouts/breakout.ts +110 -0
  199. package/src/breakouts/collection.ts +19 -0
  200. package/src/breakouts/index.ts +225 -0
  201. package/src/common/{browser-detection.js → browser-detection.ts} +9 -6
  202. package/src/common/collection.ts +9 -7
  203. package/src/common/{config.js → config.ts} +1 -1
  204. package/src/common/errors/{captcha-error.js → captcha-error.ts} +11 -7
  205. package/src/common/errors/{intent-to-join.js → intent-to-join.ts} +12 -7
  206. package/src/common/errors/{join-meeting.js → join-meeting.ts} +17 -8
  207. package/src/common/errors/{media.js → media.ts} +11 -7
  208. package/src/common/errors/parameter.ts +11 -7
  209. package/src/common/errors/{password-error.js → password-error.ts} +11 -7
  210. package/src/common/errors/{permission.js → permission.ts} +10 -6
  211. package/src/common/errors/{reconnection-in-progress.js → reconnection-in-progress.ts} +0 -0
  212. package/src/common/errors/{reconnection.js → reconnection.ts} +11 -7
  213. package/src/common/errors/{stats.js → stats.ts} +11 -7
  214. package/src/common/errors/{webex-errors.js → webex-errors.ts} +8 -7
  215. package/src/common/errors/{webex-meetings-error.js → webex-meetings-error.ts} +4 -2
  216. package/src/common/events/{events-scope.js → events-scope.ts} +6 -2
  217. package/src/common/events/{events.js → events.ts} +5 -1
  218. package/src/common/events/{trigger-proxy.js → trigger-proxy.ts} +9 -5
  219. package/src/common/events/{util.js → util.ts} +2 -3
  220. package/src/common/logs/{logger-config.js → logger-config.ts} +1 -2
  221. package/src/common/logs/logger-proxy.ts +44 -0
  222. package/src/common/logs/{request.js → request.ts} +22 -9
  223. package/src/common/queue.ts +1 -2
  224. package/src/{config.js → config.ts} +17 -12
  225. package/src/constants.ts +40 -1
  226. package/src/index.js +1 -1
  227. package/src/locus-info/controlsUtils.ts +114 -0
  228. package/src/locus-info/{embeddedAppsUtils.js → embeddedAppsUtils.ts} +5 -6
  229. package/src/locus-info/{fullState.js → fullState.ts} +16 -12
  230. package/src/locus-info/{hostUtils.js → hostUtils.ts} +9 -8
  231. package/src/locus-info/{index.js → index.ts} +148 -64
  232. package/src/locus-info/{infoUtils.js → infoUtils.ts} +19 -8
  233. package/src/locus-info/{mediaSharesUtils.js → mediaSharesUtils.ts} +17 -17
  234. package/src/locus-info/{parser.js → parser.ts} +67 -79
  235. package/src/locus-info/{selfUtils.js → selfUtils.ts} +123 -68
  236. package/src/media/{index.js → index.ts} +181 -131
  237. package/src/media/{properties.js → properties.ts} +47 -28
  238. package/src/media/{util.js → util.ts} +2 -2
  239. package/src/mediaQualityMetrics/{config.js → config.ts} +46 -46
  240. package/src/meeting/{effectsState.js → effectsState.ts} +47 -41
  241. package/src/meeting/in-meeting-actions.ts +15 -3
  242. package/src/meeting/{index.js → index.ts} +2263 -1427
  243. package/src/meeting/{muteState.js → muteState.ts} +78 -42
  244. package/src/meeting/{request.js → request.ts} +292 -142
  245. package/src/meeting/request.type.ts +13 -0
  246. package/src/meeting/{state.js → state.ts} +50 -35
  247. package/src/meeting/{util.js → util.ts} +112 -115
  248. package/src/meeting-info/{collection.js → collection.ts} +6 -2
  249. package/src/meeting-info/{index.js → index.ts} +42 -36
  250. package/src/meeting-info/meeting-info-v2.ts +273 -0
  251. package/src/meeting-info/{request.js → request.ts} +14 -4
  252. package/src/meeting-info/{util.js → util.ts} +60 -51
  253. package/src/meeting-info/{utilv2.js → utilv2.ts} +65 -58
  254. package/src/meetings/{collection.js → collection.ts} +6 -3
  255. package/src/meetings/index.ts +1159 -0
  256. package/src/meetings/{request.js → request.ts} +32 -25
  257. package/src/meetings/{util.js → util.ts} +34 -32
  258. package/src/member/{index.js → index.ts} +102 -56
  259. package/src/member/{util.js → util.ts} +52 -25
  260. package/src/members/{collection.js → collection.ts} +2 -2
  261. package/src/members/{index.js → index.ts} +219 -142
  262. package/src/members/{request.js → request.ts} +60 -16
  263. package/src/members/{util.js → util.ts} +50 -48
  264. package/src/metrics/{config.js → config.ts} +254 -83
  265. package/src/metrics/{constants.js → constants.ts} +0 -2
  266. package/src/metrics/{index.js → index.ts} +106 -74
  267. package/src/multistream/mediaRequestManager.ts +81 -15
  268. package/src/multistream/multistreamMedia.ts +5 -0
  269. package/src/multistream/receiveSlot.ts +18 -12
  270. package/src/multistream/receiveSlotManager.ts +23 -21
  271. package/src/multistream/remoteMedia.ts +15 -5
  272. package/src/multistream/remoteMediaGroup.ts +4 -3
  273. package/src/multistream/remoteMediaManager.ts +153 -37
  274. package/src/networkQualityMonitor/{index.js → index.ts} +37 -25
  275. package/src/personal-meeting-room/{index.js → index.ts} +28 -19
  276. package/src/personal-meeting-room/{request.js → request.ts} +13 -4
  277. package/src/personal-meeting-room/{util.js → util.ts} +4 -4
  278. package/src/reachability/{index.js → index.ts} +99 -83
  279. package/src/reachability/request.ts +39 -33
  280. package/src/reactions/constants.ts +4 -0
  281. package/src/reactions/reactions.ts +104 -0
  282. package/src/reactions/reactions.type.ts +62 -0
  283. package/src/reconnection-manager/{index.js → index.ts} +195 -102
  284. package/src/recording-controller/enums.ts +8 -0
  285. package/src/recording-controller/index.ts +315 -0
  286. package/src/recording-controller/util.ts +58 -0
  287. package/src/roap/{index.js → index.ts} +73 -56
  288. package/src/roap/request.ts +157 -0
  289. package/src/roap/turnDiscovery.ts +77 -37
  290. package/src/statsAnalyzer/{global.js → global.ts} +30 -33
  291. package/src/statsAnalyzer/{index.js → index.ts} +468 -192
  292. package/src/statsAnalyzer/mqaUtil.ts +290 -0
  293. package/src/transcription/{index.js → index.ts} +46 -39
  294. package/test/integration/spec/journey.js +664 -463
  295. package/test/integration/spec/space-meeting.js +320 -206
  296. package/test/integration/spec/transcription.js +7 -8
  297. package/test/unit/spec/breakouts/breakout.ts +119 -0
  298. package/test/unit/spec/breakouts/collection.ts +15 -0
  299. package/test/unit/spec/breakouts/index.ts +293 -0
  300. package/test/unit/spec/common/browser-detection.js +9 -28
  301. package/test/unit/spec/fixture/locus.js +92 -90
  302. package/test/unit/spec/locus-info/controlsUtils.js +25 -5
  303. package/test/unit/spec/locus-info/embeddedAppsUtils.js +8 -6
  304. package/test/unit/spec/locus-info/index.js +104 -2
  305. package/test/unit/spec/locus-info/infoUtils.js +41 -32
  306. package/test/unit/spec/locus-info/lib/BasicSeqCmp.json +88 -430
  307. package/test/unit/spec/locus-info/lib/SeqCmp.json +513 -685
  308. package/test/unit/spec/locus-info/parser.js +3 -9
  309. package/test/unit/spec/locus-info/selfConstant.js +97 -103
  310. package/test/unit/spec/locus-info/selfUtils.js +105 -12
  311. package/test/unit/spec/media/index.ts +31 -47
  312. package/test/unit/spec/media/properties.ts +9 -9
  313. package/test/unit/spec/meeting/effectsState.js +39 -45
  314. package/test/unit/spec/meeting/in-meeting-actions.ts +5 -2
  315. package/test/unit/spec/meeting/index.js +2017 -742
  316. package/test/unit/spec/meeting/muteState.js +42 -33
  317. package/test/unit/spec/meeting/request.js +115 -44
  318. package/test/unit/spec/meeting/utils.js +104 -171
  319. package/test/unit/spec/meeting-info/meetinginfov2.js +100 -73
  320. package/test/unit/spec/meeting-info/request.js +7 -9
  321. package/test/unit/spec/meeting-info/util.js +11 -12
  322. package/test/unit/spec/meeting-info/utilv2.js +110 -74
  323. package/test/unit/spec/meetings/collection.js +1 -1
  324. package/test/unit/spec/meetings/index.js +439 -257
  325. package/test/unit/spec/meetings/utils.js +14 -12
  326. package/test/unit/spec/member/index.js +0 -1
  327. package/test/unit/spec/member/util.js +31 -7
  328. package/test/unit/spec/members/index.js +104 -54
  329. package/test/unit/spec/members/request.js +29 -20
  330. package/test/unit/spec/members/utils.js +8 -5
  331. package/test/unit/spec/metrics/index.js +16 -21
  332. package/test/unit/spec/multistream/mediaRequestManager.ts +316 -50
  333. package/test/unit/spec/multistream/receiveSlot.ts +6 -6
  334. package/test/unit/spec/multistream/receiveSlotManager.ts +13 -13
  335. package/test/unit/spec/multistream/remoteMedia.ts +10 -2
  336. package/test/unit/spec/multistream/remoteMediaGroup.ts +5 -5
  337. package/test/unit/spec/multistream/remoteMediaManager.ts +412 -65
  338. package/test/unit/spec/networkQualityMonitor/index.js +21 -15
  339. package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +2 -7
  340. package/test/unit/spec/reachability/index.ts +58 -26
  341. package/test/unit/spec/reconnection-manager/index.js +102 -9
  342. package/test/unit/spec/recording-controller/index.js +231 -0
  343. package/test/unit/spec/recording-controller/util.js +102 -0
  344. package/test/unit/spec/roap/index.ts +2 -1
  345. package/test/unit/spec/roap/request.ts +114 -0
  346. package/test/unit/spec/roap/turnDiscovery.ts +64 -45
  347. package/test/unit/spec/stats-analyzer/index.js +27 -22
  348. package/test/utils/cmr.js +44 -42
  349. package/test/utils/testUtils.js +83 -74
  350. package/test/utils/webex-config.js +18 -18
  351. package/test/utils/webex-test-users.js +54 -50
  352. package/tsconfig.json +6 -0
  353. package/dist/media/internal-media-core-wrapper.js +0 -22
  354. package/dist/media/internal-media-core-wrapper.js.map +0 -1
  355. package/dist/peer-connection-manager/util.js +0 -124
  356. package/dist/peer-connection-manager/util.js.map +0 -1
  357. package/src/common/logs/logger-proxy.js +0 -33
  358. package/src/locus-info/controlsUtils.js +0 -102
  359. package/src/media/internal-media-core-wrapper.ts +0 -9
  360. package/src/meeting-info/meeting-info-v2.js +0 -255
  361. package/src/meetings/index.js +0 -1015
  362. package/src/peer-connection-manager/util.ts +0 -117
  363. package/src/roap/request.js +0 -127
  364. package/src/statsAnalyzer/mqaUtil.js +0 -173
  365. package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +0 -389
@@ -0,0 +1,1159 @@
1
+ /* eslint no-shadow: ["error", { "allow": ["eventType"] }] */
2
+
3
+ import '@webex/internal-plugin-mercury';
4
+ import '@webex/internal-plugin-conversation';
5
+ // @ts-ignore
6
+ import {WebexPlugin} from '@webex/webex-core';
7
+ import {setLogger} from '@webex/internal-media-core';
8
+
9
+ import 'webrtc-adapter';
10
+
11
+ import Metrics from '../metrics';
12
+ import {trigger, eventType} from '../metrics/config';
13
+ import LoggerConfig from '../common/logs/logger-config';
14
+ import StaticConfig from '../common/config';
15
+ import LoggerProxy from '../common/logs/logger-proxy';
16
+ import LoggerRequest from '../common/logs/request';
17
+ import Trigger from '../common/events/trigger-proxy';
18
+ import Media from '../media';
19
+ import MeetingUtil from '../meeting/util';
20
+ import {
21
+ MEETINGS,
22
+ EVENTS,
23
+ EVENT_TRIGGERS,
24
+ READY,
25
+ LOCUSEVENT,
26
+ LOCUS_URL,
27
+ MAX_RANDOM_DELAY_FOR_MEETING_INFO,
28
+ ROAP,
29
+ ONLINE,
30
+ OFFLINE,
31
+ _MEETING_,
32
+ _JOIN_,
33
+ _LOCUS_ID_,
34
+ _INCOMING_,
35
+ LOCUS,
36
+ CORRELATION_ID,
37
+ SIP_URI,
38
+ _LEFT_,
39
+ _ID_,
40
+ MEETING_REMOVED_REASON,
41
+ _CONVERSATION_URL_,
42
+ CONVERSATION_URL,
43
+ } from '../constants';
44
+ import BEHAVIORAL_METRICS from '../metrics/constants';
45
+ import MeetingInfo from '../meeting-info';
46
+ import MeetingInfoV2 from '../meeting-info/meeting-info-v2';
47
+ import Meeting from '../meeting';
48
+ import PersonalMeetingRoom from '../personal-meeting-room';
49
+ import Reachability from '../reachability';
50
+ import Request from './request';
51
+ import PasswordError from '../common/errors/password-error';
52
+ import CaptchaError from '../common/errors/captcha-error';
53
+
54
+ import MeetingCollection from './collection';
55
+ import MeetingsUtil from './util';
56
+
57
+ let mediaLogger;
58
+
59
+ class MediaLogger {
60
+ info(...args) {
61
+ LoggerProxy.logger.info(...args);
62
+ }
63
+
64
+ log(...args) {
65
+ LoggerProxy.logger.log(...args);
66
+ }
67
+
68
+ error(...args) {
69
+ LoggerProxy.logger.error(...args);
70
+ }
71
+
72
+ warn(...args) {
73
+ LoggerProxy.logger.warn(...args);
74
+ }
75
+
76
+ trace(...args) {
77
+ LoggerProxy.logger.trace(...args);
78
+ }
79
+
80
+ debug(...args) {
81
+ LoggerProxy.logger.debug(...args);
82
+ }
83
+ }
84
+ /**
85
+ * Meetings Ready Event
86
+ * Emitted when the meetings instance on webex is ready
87
+ * @event meetings:ready
88
+ * @instance
89
+ * @memberof Meetings
90
+ */
91
+
92
+ /**
93
+ * Meetings Network Disconnected Event
94
+ * Emitted when the meetings instance is disconnected from
95
+ * the internal mercury server
96
+ * @event network:disconnected
97
+ * @instance
98
+ * @memberof Meetings
99
+ */
100
+
101
+ /**
102
+ * Meetings Registered Event
103
+ * Emitted when the meetings instance has been registered and listening
104
+ * @event meetings:registered
105
+ * @instance
106
+ * @memberof Meetings
107
+ */
108
+
109
+ /**
110
+ * Meeting Removed Event
111
+ * Emitted when a meeting was removed from the cache of meetings
112
+ * @event meeting:removed
113
+ * @instance
114
+ * @type {Object}
115
+ * @property {String} meetingId the removed meeting
116
+ * @property {Object} response the server response
117
+ * @property {String} type what type of meeting it was
118
+ * @memberof Meetings
119
+ */
120
+
121
+ /**
122
+ * Meeting Added Event
123
+ * Emitted when a meeting was added to the cache of meetings
124
+ * @event meeting:added
125
+ * @instance
126
+ * @type {Object}
127
+ * @property {String} meetingId the added meeting
128
+ * @property {String} type what type of meeting it was
129
+ * @memberof Meetings
130
+ */
131
+
132
+ /**
133
+ * Maintain a cache of meetings and sync with services.
134
+ * @class
135
+ */
136
+ export default class Meetings extends WebexPlugin {
137
+ loggerRequest: any;
138
+ media: any;
139
+ meetingCollection: any;
140
+ personalMeetingRoom: any;
141
+ preferredWebexSite: any;
142
+ reachability: any;
143
+ registered: any;
144
+ request: any;
145
+ geoHintInfo: any;
146
+ meetingInfo: any;
147
+
148
+ namespace = MEETINGS;
149
+
150
+ /**
151
+ * Initializes the Meetings Plugin
152
+ * @constructor
153
+ * @public
154
+ * @memberof Meetings
155
+ */
156
+ constructor(...args) {
157
+ super(...args);
158
+
159
+ /**
160
+ * The Meetings request to interact with server
161
+ * @instance
162
+ * @type {Object}
163
+ * @private
164
+ * @memberof Meetings
165
+ */
166
+ // @ts-ignore
167
+ this.request = new Request({}, {parent: this.webex});
168
+ /**
169
+ * Log upload request helper
170
+ * @instance
171
+ * @type {Object}
172
+ * @private
173
+ * @memberof Meetings
174
+ */
175
+ // @ts-ignore
176
+ this.loggerRequest = new LoggerRequest({webex: this.webex});
177
+ this.meetingCollection = new MeetingCollection();
178
+ /**
179
+ * The PersonalMeetingRoom object to interact with server
180
+ * @instance
181
+ * @type {Object}
182
+ * @public
183
+ * @memberof Meetings
184
+ */
185
+ this.personalMeetingRoom = null;
186
+ /**
187
+ * The Reachability object to interact with server, starts as null until {@link Meeting#setReachability} is called
188
+ * starts as null
189
+ * @instance
190
+ * @type {Object}
191
+ * @private
192
+ * @memberof Meetings
193
+ */
194
+ this.reachability = null;
195
+
196
+ /**
197
+ * If the meetings plugin has been registered and listening via {@link Meetings#register}
198
+ * @instance
199
+ * @type {Boolean}
200
+ * @public
201
+ * @memberof Meetings
202
+ */
203
+ this.registered = false;
204
+
205
+ /**
206
+ * This values indicates the preferred webex site the user will start there meeting, getsits value from {@link Meetings#register}
207
+ * @instance
208
+ * @type {String}
209
+ * @private
210
+ * @memberof Meetings
211
+ */
212
+ this.preferredWebexSite = '';
213
+
214
+ /**
215
+ * The public interface for the internal Media util files. These are helpful to expose outside the context
216
+ * of a meeting so that a user can access media without creating a meeting instance.
217
+ * @instance
218
+ * @type {Object}
219
+ * @private
220
+ * @memberof Meetings
221
+ */
222
+ this.media = {
223
+ getUserMedia: Media.getUserMedia,
224
+ getSupportedDevice: Media.getSupportedDevice,
225
+ };
226
+
227
+ this.onReady();
228
+ }
229
+
230
+ /**
231
+ * handle locus events and takes meeting actions with them as they come in
232
+ * @param {Object} data a locus event
233
+ * @param {String} data.locusUrl
234
+ * @param {Object} data.locus
235
+ * @param {Boolean} useRandomDelayForInfo whether a random delay should be added to fetching meeting info
236
+ * @param {String} data.eventType
237
+ * @returns {undefined}
238
+ * @private
239
+ * @memberof Meetings
240
+ */
241
+ private handleLocusEvent(data: {locusUrl: string; locus: any}, useRandomDelayForInfo = false) {
242
+ let meeting = null;
243
+
244
+ // getting meeting by correlationId. This will happen for the new event
245
+ // Either the locus
246
+ // TODO : Add check for the callBack Address
247
+ meeting =
248
+ this.meetingCollection.getByKey(LOCUS_URL, data.locusUrl) ||
249
+ // @ts-ignore
250
+ this.meetingCollection.getByKey(
251
+ CORRELATION_ID,
252
+ // @ts-ignore
253
+ MeetingsUtil.checkForCorrelationId(this.webex.internal.device.url, data.locus)
254
+ ) ||
255
+ this.meetingCollection.getByKey(
256
+ SIP_URI,
257
+ data.locus.self &&
258
+ data.locus.self.callbackInfo &&
259
+ data.locus.self.callbackInfo.callbackAddress
260
+ ) ||
261
+ (data.locus.info?.isUnifiedSpaceMeeting
262
+ ? undefined
263
+ : this.meetingCollection.getByKey(CONVERSATION_URL, data.locus.conversationUrl));
264
+
265
+ // Special case when locus has got replaced, This only happend once if a replace locus exists
266
+ // https://sqbu-github.cisco.com/WebExSquared/locus/wiki/Locus-changing-mid-call
267
+
268
+ if (!meeting && data.locus?.replaces?.length > 0) {
269
+ // Always the last element in the replace is the active one
270
+ meeting = this.meetingCollection.getByKey(
271
+ LOCUS_URL,
272
+ data.locus.replaces[data.locus.replaces.length - 1].locusUrl
273
+ );
274
+ }
275
+
276
+ if (!meeting) {
277
+ // TODO: create meeting when we get a meeting object
278
+ // const checkForEnded = (locus) => {
279
+ // TODO: you already ended the meeting but you got an event later
280
+ // Mainly for 1:1 Callsor meeting
281
+ // Happens mainly after refresh
282
+
283
+ // 1:1 Meeting
284
+ // 1) You ended a call before but you got a mercury event
285
+ // Make sure end the call and cleanup the meeting only if the mercury
286
+ // event says so
287
+ // 2) Maintain lastSync time in the meetings object which helps to compare
288
+ // If the meeting came befor or after the sync . ANy meeting start time before the sync time is invalid
289
+
290
+ // For space Meeting
291
+ // Check the locus object and see who has joined
292
+
293
+ // };
294
+ // rather then locus object change to locus url
295
+
296
+ if (
297
+ data.locus &&
298
+ data.locus.fullState &&
299
+ data.locus.fullState.state === LOCUS.STATE.INACTIVE
300
+ ) {
301
+ // just ignore the event as its already ended and not active
302
+ LoggerProxy.logger.warn(
303
+ 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
304
+ );
305
+
306
+ return;
307
+ }
308
+
309
+ // When its wireless share or guest and user leaves the meeting we dont have to keep the meeting object
310
+ // Any future events will be neglected
311
+
312
+ if (
313
+ data.locus &&
314
+ data.locus.self &&
315
+ data.locus.self.state === _LEFT_ &&
316
+ data.locus.self.removed === true
317
+ ) {
318
+ // just ignore the event as its already ended and not active
319
+ LoggerProxy.logger.warn(
320
+ 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
321
+ );
322
+
323
+ return;
324
+ }
325
+
326
+ this.create(data.locus, _LOCUS_ID_, useRandomDelayForInfo)
327
+ .then((newMeeting) => {
328
+ meeting = newMeeting;
329
+
330
+ // It's a new meeting so initialize the locus data
331
+ meeting.locusInfo.initialSetup(data.locus);
332
+ })
333
+ .catch((e) => {
334
+ LoggerProxy.logger.error(e);
335
+ })
336
+ .finally(() => {
337
+ // There will be cases where locus event comes in gets created and deleted because its a 1:1 and meeting gets deleted
338
+ // because the other user left so before sending 'added' event make sure it exists in the collection
339
+
340
+ if (this.getMeetingByType(_ID_, meeting.id)) {
341
+ Metrics.postEvent({
342
+ event: eventType.REMOTE_STARTED,
343
+ meeting,
344
+ data: {trigger: trigger.MERCURY_EVENT},
345
+ });
346
+ Trigger.trigger(
347
+ this,
348
+ {
349
+ file: 'meetings',
350
+ function: 'handleLocusEvent',
351
+ },
352
+ EVENT_TRIGGERS.MEETING_ADDED,
353
+ {
354
+ meeting,
355
+ type: meeting.type === _MEETING_ ? _JOIN_ : _INCOMING_,
356
+ }
357
+ );
358
+ } else {
359
+ // Meeting got added but was not found in the collection. It might have got destroyed
360
+ LoggerProxy.logger.warn(
361
+ 'Meetings:index#handleLocusEvent --> Created and destroyed meeting object before sending an event'
362
+ );
363
+ }
364
+ });
365
+ } else {
366
+ meeting.locusInfo.parse(meeting, data);
367
+ }
368
+ }
369
+
370
+ /**
371
+ * handles locus events through mercury that are not roap
372
+ * @param {Object} envelope
373
+ * @param {Object} envelope.data
374
+ * @param {String} envelope.data.eventType
375
+ * @returns {undefined}
376
+ * @private
377
+ * @memberof Meetings
378
+ */
379
+ private handleLocusMercury(envelope: {data: any}) {
380
+ const {data} = envelope;
381
+ // eslint-disable-next-line @typescript-eslint/no-shadow
382
+ const {eventType} = data;
383
+
384
+ if (eventType && eventType !== LOCUSEVENT.MESSAGE_ROAP) {
385
+ this.handleLocusEvent(data, true);
386
+ }
387
+ }
388
+
389
+ /**
390
+ * handles mecury offline event
391
+ * @returns {undefined}
392
+ * @private
393
+ * @memberof Meetings
394
+ */
395
+ private handleMercuryOffline() {
396
+ Trigger.trigger(
397
+ this,
398
+ {
399
+ file: 'meetings/index',
400
+ function: 'handleMercuryOffline',
401
+ },
402
+ EVENT_TRIGGERS.MEETINGS_NETWORK_DISCONNECTED
403
+ );
404
+ }
405
+
406
+ /**
407
+ * registers for locus and roap mercury events
408
+ * @returns {undefined}
409
+ * @private
410
+ * @memberof Meetings
411
+ */
412
+ private listenForEvents() {
413
+ // @ts-ignore
414
+ this.webex.internal.mercury.on(LOCUSEVENT.LOCUS_MERCURY, (envelope) => {
415
+ this.handleLocusMercury(envelope);
416
+ });
417
+ // @ts-ignore
418
+ this.webex.internal.mercury.on(ROAP.ROAP_MERCURY, (envelope) => {
419
+ MeetingsUtil.handleRoapMercury(envelope, this.meetingCollection);
420
+ });
421
+
422
+ // @ts-ignore
423
+ this.webex.internal.mercury.on(ONLINE, () => {
424
+ this.syncMeetings();
425
+ });
426
+
427
+ // @ts-ignore
428
+ this.webex.internal.mercury.on(OFFLINE, () => {
429
+ this.handleMercuryOffline();
430
+ });
431
+ }
432
+
433
+ /**
434
+ * stops listening for locus and roap mercury events
435
+ * @returns {undefined}
436
+ * @private
437
+ * @memberof Meetings
438
+ */
439
+ private stopListeningForEvents() {
440
+ // @ts-ignore
441
+ this.webex.internal.mercury.off(LOCUSEVENT.LOCUS_MERCURY);
442
+ // @ts-ignore
443
+ this.webex.internal.mercury.off(ROAP.ROAP_MERCURY);
444
+ // @ts-ignore
445
+ this.webex.internal.mercury.off(ONLINE);
446
+ }
447
+
448
+ /**
449
+ * @returns {undefined}
450
+ * @private
451
+ * @memberof Meetings
452
+ */
453
+ private onReady() {
454
+ // @ts-ignore
455
+ this.webex.once(READY, () => {
456
+ // @ts-ignore
457
+ StaticConfig.set(this.config);
458
+ // @ts-ignore
459
+ LoggerConfig.set(this.config.logging);
460
+ // @ts-ignore
461
+ LoggerProxy.set(this.webex.logger);
462
+
463
+ mediaLogger = new MediaLogger();
464
+ setLogger(mediaLogger);
465
+
466
+ /**
467
+ * The MeetingInfo object to interact with server
468
+ * @instance
469
+ * @type {Object}
470
+ * @private
471
+ * @memberof Meetings
472
+ */
473
+ // @ts-ignore
474
+ this.meetingInfo = this.config.experimental.enableUnifiedMeetings
475
+ ? // @ts-ignore
476
+ new MeetingInfoV2(this.webex)
477
+ : // @ts-ignore
478
+ new MeetingInfo(this.webex);
479
+ // @ts-ignore
480
+ this.personalMeetingRoom = new PersonalMeetingRoom(
481
+ {meetingInfo: this.meetingInfo},
482
+ // @ts-ignore
483
+ {parent: this.webex}
484
+ );
485
+
486
+ Trigger.trigger(
487
+ this,
488
+ {
489
+ file: 'meetings',
490
+ function: 'onReady',
491
+ },
492
+ EVENT_TRIGGERS.MEETINGS_READY
493
+ );
494
+
495
+ MeetingsUtil.checkH264Support({disableNotifications: true});
496
+ // @ts-ignore
497
+ Metrics.initialSetup(this.meetingCollection, this.webex);
498
+ });
499
+ }
500
+
501
+ /**
502
+ * API to toggle unified meetings
503
+ * @param {Boolean} changeState
504
+ * @private
505
+ * @memberof Meetings
506
+ * @returns {undefined}
507
+ */
508
+ private _toggleUnifiedMeetings(changeState: boolean) {
509
+ if (typeof changeState !== 'boolean') {
510
+ return;
511
+ }
512
+ // @ts-ignore
513
+ if (this.config?.experimental?.enableUnifiedMeetings !== changeState) {
514
+ // @ts-ignore
515
+ this.config.experimental.enableUnifiedMeetings = changeState;
516
+ // @ts-ignore
517
+ this.meetingInfo = changeState ? new MeetingInfoV2(this.webex) : new MeetingInfo(this.webex);
518
+ }
519
+ }
520
+
521
+ /**
522
+ * API to enable or disable TURN discovery
523
+ * @param {Boolean} enable
524
+ * @private
525
+ * @memberof Meetings
526
+ * @returns {undefined}
527
+ */
528
+ private _toggleTurnDiscovery(enable: boolean) {
529
+ if (typeof enable !== 'boolean') {
530
+ return;
531
+ }
532
+ // @ts-ignore
533
+ this.config.experimental.enableTurnDiscovery = enable;
534
+ }
535
+
536
+ /**
537
+ * API to toggle starting adhoc meeting
538
+ * @param {Boolean} changeState
539
+ * @private
540
+ * @memberof Meetings
541
+ * @returns {undefined}
542
+ */
543
+ private _toggleAdhocMeetings(changeState: boolean) {
544
+ if (typeof changeState !== 'boolean') {
545
+ return;
546
+ }
547
+ // @ts-ignore
548
+ if (this.config?.experimental?.enableAdhocMeetings !== changeState) {
549
+ // @ts-ignore
550
+ this.config.experimental.enableAdhocMeetings = changeState;
551
+ }
552
+ }
553
+
554
+ /**
555
+ * Explicitly sets up the meetings plugin by registering
556
+ * the device, connecting to mercury, and listening for locus events.
557
+ *
558
+ * @returns {Promise}
559
+ * @public
560
+ * @memberof Meetings
561
+ */
562
+ public register() {
563
+ // @ts-ignore
564
+ if (!this.webex.canAuthorize) {
565
+ LoggerProxy.logger.error(
566
+ 'Meetings:index#register --> ERROR, Unable to register, SDK cannot authorize'
567
+ );
568
+
569
+ return Promise.reject(new Error('SDK cannot authorize'));
570
+ }
571
+
572
+ if (this.registered) {
573
+ LoggerProxy.logger.info(
574
+ 'Meetings:index#register --> INFO, Meetings plugin already registered'
575
+ );
576
+
577
+ return Promise.resolve();
578
+ }
579
+
580
+ return Promise.all([
581
+ this.fetchUserPreferredWebexSite(),
582
+ this.getGeoHint(),
583
+ this.startReachability().catch((error) => {
584
+ LoggerProxy.logger.error(`Meetings:index#register --> GDM error, ${error.message}`);
585
+ }),
586
+ // @ts-ignore
587
+ this.webex.internal.device
588
+ .register()
589
+ // @ts-ignore
590
+ .then(() =>
591
+ LoggerProxy.logger.info(
592
+ // @ts-ignore
593
+ `Meetings:index#register --> INFO, Device registered ${this.webex.internal.device.url}`
594
+ )
595
+ )
596
+ // @ts-ignore
597
+ .then(() => this.webex.internal.mercury.connect()),
598
+ MeetingsUtil.checkH264Support.call(this),
599
+ ])
600
+ .then(() => {
601
+ this.listenForEvents();
602
+ Trigger.trigger(
603
+ this,
604
+ {
605
+ file: 'meetings',
606
+ function: 'register',
607
+ },
608
+ EVENT_TRIGGERS.MEETINGS_REGISTERED
609
+ );
610
+ this.registered = true;
611
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_SUCCESS);
612
+ })
613
+ .catch((error) => {
614
+ LoggerProxy.logger.error(
615
+ `Meetings:index#register --> ERROR, Unable to register, ${error.message}`
616
+ );
617
+
618
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_FAILED, {
619
+ reason: error.message,
620
+ stack: error.stack,
621
+ });
622
+
623
+ return Promise.reject(error);
624
+ });
625
+ }
626
+
627
+ /**
628
+ * Explicitly tears down the meetings plugin by deregistering
629
+ * the device, disconnecting from mercury, and stops listening to locus events
630
+ *
631
+ * @returns {Promise}
632
+ * @public
633
+ * @memberof Meetings
634
+ */
635
+ unregister() {
636
+ if (!this.registered) {
637
+ LoggerProxy.logger.info(
638
+ 'Meetings:index#unregister --> INFO, Meetings plugin already unregistered'
639
+ );
640
+
641
+ return Promise.resolve();
642
+ }
643
+
644
+ this.stopListeningForEvents();
645
+
646
+ return (
647
+ // @ts-ignore
648
+ this.webex.internal.mercury
649
+ .disconnect()
650
+ // @ts-ignore
651
+ .then(() => this.webex.internal.device.unregister())
652
+ .then(() => {
653
+ Trigger.trigger(
654
+ this,
655
+ {
656
+ file: 'meetings',
657
+ function: 'unregister',
658
+ },
659
+ EVENT_TRIGGERS.MEETINGS_UNREGISTERED
660
+ );
661
+ this.registered = false;
662
+ })
663
+ );
664
+ }
665
+
666
+ /**
667
+ * Uploads logs to the webex services for tracking
668
+ * @param {Object} [options={}]
669
+ * @param {String} [options.callStart] Call Start Time
670
+ * @param {String} [options.feedbackId] ID used for tracking
671
+ * @param {String} [options.locusId]
672
+ * @param {String} [options.correlationId]
673
+ * @param {String} [options.meetingId] webex meeting ID
674
+ * @param {String} [options.userId] userId
675
+ * @param {String} [options.orgId] org id
676
+ * @returns {String} feedback ID logs were submitted under
677
+ */
678
+ uploadLogs(
679
+ options: {
680
+ callStart?: string;
681
+ feedbackId?: string;
682
+ locusId?: string;
683
+ correlationId?: string;
684
+ meetingId?: string;
685
+ userId?: string;
686
+ orgId?: string;
687
+ } = {}
688
+ ) {
689
+ LoggerProxy.logger.info('Meetings:index#uploadLogs --> uploading logs');
690
+
691
+ return this.loggerRequest
692
+ .uploadLogs(options)
693
+ .then((uploadResult) => {
694
+ LoggerProxy.logger.info(
695
+ 'Meetings:index#uploadLogs --> Upload logs for meeting completed.',
696
+ uploadResult
697
+ );
698
+ Trigger.trigger(
699
+ this,
700
+ {
701
+ file: 'meetings',
702
+ function: 'uploadLogs',
703
+ },
704
+ EVENT_TRIGGERS.MEETING_LOG_UPLOAD_SUCCESS,
705
+ {
706
+ meetingId: options.meetingId,
707
+ details: uploadResult,
708
+ }
709
+ );
710
+
711
+ return uploadResult;
712
+ })
713
+ .catch((uploadError) => {
714
+ LoggerProxy.logger.error(
715
+ 'Meetings:index#uploadLogs --> Unable to upload logs for meeting',
716
+ uploadError
717
+ );
718
+ Trigger.trigger(
719
+ this,
720
+ {
721
+ file: 'meetings',
722
+ function: 'uploadLogs',
723
+ },
724
+ EVENT_TRIGGERS.MEETING_LOG_UPLOAD_FAILURE,
725
+ {
726
+ meetingId: options.meetingId,
727
+ reason: uploadError,
728
+ }
729
+ );
730
+
731
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.UPLOAD_LOGS_FAILURE, {
732
+ // @ts-ignore - seems like typo
733
+ meetingId: options.meetingsId,
734
+ reason: uploadError.message,
735
+ stack: uploadError.stack,
736
+ code: uploadError.code,
737
+ });
738
+ });
739
+ }
740
+
741
+ /**
742
+ * initializes the reachability instance for Meetings
743
+ * @returns {undefined}
744
+ * @public
745
+ * @memberof Meetings
746
+ */
747
+ setReachability() {
748
+ // @ts-ignore
749
+ this.reachability = new Reachability(this.webex);
750
+ }
751
+
752
+ /**
753
+ * gets the reachability instance for Meetings
754
+ * @returns {Reachability}
755
+ * @public
756
+ * @memberof Meetings
757
+ */
758
+ getReachability() {
759
+ return this.reachability;
760
+ }
761
+
762
+ /**
763
+ * initializes and starts gathering reachability for Meetings
764
+ * @returns {Promise}
765
+ * @public
766
+ * @memberof Meetings
767
+ */
768
+ startReachability() {
769
+ if (!this.reachability) {
770
+ this.setReachability();
771
+ }
772
+
773
+ return this.getReachability().gatherReachability();
774
+ }
775
+
776
+ /**
777
+ * Get geoHint for info for meetings
778
+ * @returns {Promise}
779
+ * @private
780
+ * @memberof Meetings
781
+ */
782
+ getGeoHint() {
783
+ return this.request.fetchGeoHint().then((res) => {
784
+ this.geoHintInfo = res;
785
+ });
786
+ }
787
+
788
+ /**
789
+ * Fetch user preferred webex site information
790
+ * This also has other infomation about the user
791
+ * @returns {Promise}
792
+ * @private
793
+ * @memberof Meetings
794
+ */
795
+ fetchUserPreferredWebexSite() {
796
+ return this.request.getMeetingPreferences().then((res) => {
797
+ if (res) {
798
+ this.preferredWebexSite = MeetingsUtil.parseDefaultSiteFromMeetingPreferences(res);
799
+ }
800
+ });
801
+ }
802
+
803
+ /**
804
+ * gets the personal meeting room instance, for saved PMR values for this user
805
+ * @returns {PersonalMeetingRoom}
806
+ * @public
807
+ * @memberof Meetings
808
+ */
809
+
810
+ getPersonalMeetingRoom() {
811
+ return this.personalMeetingRoom;
812
+ }
813
+
814
+ /**
815
+ * @param {Meeting} meeting
816
+ * @param {Object} reason
817
+ * @param {String} type
818
+ * @returns {Undefined}
819
+ * @private
820
+ * @memberof Meetings
821
+ */
822
+ private destroy(meeting: Meeting, reason: object) {
823
+ MeetingUtil.cleanUp(meeting);
824
+ this.meetingCollection.delete(meeting.id);
825
+ Trigger.trigger(
826
+ this,
827
+ {
828
+ file: 'meetings',
829
+ function: 'destroy',
830
+ },
831
+ EVENT_TRIGGERS.MEETING_REMOVED,
832
+ {
833
+ meetingId: meeting.id,
834
+ reason,
835
+ }
836
+ );
837
+ }
838
+
839
+ /**
840
+ * Create a meeting.
841
+ * @param {string} destination - sipURL, spaceId, phonenumber, or locus object}
842
+ * @param {string} [type] - the optional specified type, such as locusId
843
+ * @param {Boolean} useRandomDelayForInfo - whether a random delay should be added to fetching meeting info
844
+ * @returns {Promise<Meeting>} A new Meeting.
845
+ * @public
846
+ * @memberof Meetings
847
+ */
848
+ public create(destination: string, type: string = null, useRandomDelayForInfo = false) {
849
+ // TODO: type should be from a dictionary
850
+
851
+ // Validate meeting information based on the provided destination and
852
+ // type. This must be performed prior to determining if the meeting is
853
+ // found in the collection, as we mutate the destination for hydra person
854
+ // id values.
855
+ return (
856
+ this.meetingInfo
857
+ .fetchInfoOptions(destination, type)
858
+ // Catch a failure to fetch info options.
859
+ .catch((error) => {
860
+ LoggerProxy.logger.info(
861
+ `Meetings:index#create --> INFO, unable to determine info options: ${error.message}`
862
+ );
863
+ })
864
+ .then((options: any = {}) => {
865
+ // Normalize the destination.
866
+ const targetDest = options.destination || destination;
867
+
868
+ // check for the conversation URL then sip Url
869
+ let meeting = null;
870
+
871
+ if (type === _CONVERSATION_URL_ || options.type === _CONVERSATION_URL_) {
872
+ const foundMeeting = this.meetingCollection.getByKey(CONVERSATION_URL, targetDest);
873
+
874
+ if (foundMeeting) {
875
+ const foundMeetingIsNotCalendarMeeting = !foundMeeting.locusInfo.scheduledMeeting;
876
+
877
+ // If the found meeting is not a calendar meeting, return that meeting.
878
+ // This allows for the creation of instant-meetings when calendar meetings are present.
879
+ if (foundMeetingIsNotCalendarMeeting) {
880
+ meeting = foundMeeting;
881
+ }
882
+ }
883
+ }
884
+
885
+ // Attempt to collect the meeting if it exists.
886
+ if (!meeting) {
887
+ meeting = this.meetingCollection.getByKey(SIP_URI, targetDest);
888
+ }
889
+
890
+ // Validate if a meeting was found.
891
+ if (!meeting) {
892
+ // Create a meeting based on the normalized destination and type.
893
+ return this.createMeeting(targetDest, type, useRandomDelayForInfo).then(
894
+ (createdMeeting: any) => {
895
+ // If the meeting was successfully created.
896
+ if (createdMeeting && createdMeeting.on) {
897
+ // Create a destruction event for the meeting.
898
+ createdMeeting.on(EVENTS.DESTROY_MEETING, (payload) => {
899
+ // @ts-ignore
900
+ if (this.config.autoUploadLogs) {
901
+ this.uploadLogs({
902
+ callStart: createdMeeting.locusInfo?.fullState?.lastActive,
903
+ correlationId: createdMeeting.correlationId,
904
+ feedbackId: createdMeeting.correlationId,
905
+ locusId: createdMeeting.locusId,
906
+ meetingId: createdMeeting.locusInfo?.info?.webExMeetingId,
907
+ }).then(() => this.destroy(createdMeeting, payload.reason));
908
+ } else {
909
+ this.destroy(createdMeeting, payload.reason);
910
+ }
911
+ });
912
+
913
+ createdMeeting.on(EVENTS.REQUEST_UPLOAD_LOGS, (meetingInstance) => {
914
+ // @ts-ignore
915
+ if (this.config.autoUploadLogs) {
916
+ this.uploadLogs({
917
+ callStart: meetingInstance?.locusInfo?.fullState?.lastActive,
918
+ correlationId: meetingInstance.correlationId,
919
+ feedbackId: meetingInstance.correlationId,
920
+ locusId: meetingInstance.locusId,
921
+ meetingId: meetingInstance.locusInfo?.info?.webExMeetingId,
922
+ });
923
+ }
924
+ });
925
+ } else {
926
+ LoggerProxy.logger.error(
927
+ `Meetings:index#create --> ERROR, meeting does not have on method, will not be destroyed, meeting cleanup impossible for meeting: ${meeting}`
928
+ );
929
+ }
930
+
931
+ // Return the newly created meeting.
932
+ return Promise.resolve(createdMeeting);
933
+ }
934
+ );
935
+ }
936
+
937
+ // Return the existing meeting.
938
+ return Promise.resolve(meeting);
939
+ })
940
+ );
941
+ }
942
+
943
+ /**
944
+ * @param {String} destination see create()
945
+ * @param {String} type see create()
946
+ * @param {Boolean} useRandomDelayForInfo whether a random delay should be added to fetching meeting info
947
+ * @returns {Promise} a new meeting instance complete with meeting info and destination
948
+ * @private
949
+ * @memberof Meetings
950
+ */
951
+ private async createMeeting(
952
+ destination: any,
953
+ type: string = null,
954
+ useRandomDelayForInfo = false
955
+ ) {
956
+ const meeting = new Meeting(
957
+ {
958
+ // @ts-ignore
959
+ userId: this.webex.internal.device.userId,
960
+ // @ts-ignore
961
+ deviceUrl: this.webex.internal.device.url,
962
+ // @ts-ignore
963
+ orgId: this.webex.internal.device.orgId,
964
+ locus: type === _LOCUS_ID_ ? destination : null, // pass the locus object if present
965
+ meetingInfoProvider: this.meetingInfo,
966
+ destination,
967
+ destinationType: type,
968
+ },
969
+ {
970
+ // @ts-ignore
971
+ parent: this.webex,
972
+ }
973
+ );
974
+
975
+ this.meetingCollection.set(meeting);
976
+
977
+ try {
978
+ // if no participant has joined the scheduled meeting (meaning meeting is not active) and we get a locusEvent,
979
+ // it means the meeting will start in 5-6 min. In that case, we want to fetchMeetingInfo
980
+ // between 5 and 2 min (random between 3 minutes) before the meeting starts
981
+ // to avoid a spike in traffic to the wbxappi service
982
+ let waitingTime = 0;
983
+
984
+ if (destination.meeting) {
985
+ const {startTime} = destination.meeting;
986
+ const startTimeDate = new Date(startTime);
987
+ const startTimeDatestamp = startTimeDate.getTime();
988
+ const timeToStart = startTimeDatestamp - Date.now();
989
+ const maxWaitingTime = Math.max(
990
+ Math.min(timeToStart, MAX_RANDOM_DELAY_FOR_MEETING_INFO),
991
+ 0
992
+ );
993
+
994
+ waitingTime = Math.round(Math.random() * maxWaitingTime);
995
+ }
996
+ const isMeetingActive = !!destination.fullState?.active;
997
+ // @ts-ignore
998
+ const {enableUnifiedMeetings} = this.config.experimental;
999
+
1000
+ if (enableUnifiedMeetings && !isMeetingActive && useRandomDelayForInfo && waitingTime > 0) {
1001
+ meeting.fetchMeetingInfoTimeoutId = setTimeout(
1002
+ () => meeting.fetchMeetingInfo({}),
1003
+ waitingTime
1004
+ );
1005
+ meeting.parseMeetingInfo(undefined, destination);
1006
+ } else {
1007
+ await meeting.fetchMeetingInfo({});
1008
+ }
1009
+ } catch (err) {
1010
+ if (!(err instanceof CaptchaError) && !(err instanceof PasswordError)) {
1011
+ // if there is no meeting info we assume its a 1:1 call or wireless share
1012
+ LoggerProxy.logger.info(
1013
+ `Meetings:index#createMeeting --> Info Unable to fetch meeting info for ${destination}.`
1014
+ );
1015
+ LoggerProxy.logger.info(
1016
+ 'Meetings:index#createMeeting --> Info assuming this destination is a 1:1 or wireless share'
1017
+ );
1018
+ }
1019
+ LoggerProxy.logger.debug(
1020
+ `Meetings:index#createMeeting --> Debug ${err} fetching /meetingInfo for creation.`
1021
+ );
1022
+ } finally {
1023
+ // For type LOCUS_ID we need to parse the locus object to get the information
1024
+ // about the caller and callee
1025
+ // Meeting Added event will be created in `handleLocusEvent`
1026
+ if (type !== _LOCUS_ID_) {
1027
+ if (!meeting.sipUri) {
1028
+ meeting.setSipUri(destination);
1029
+ }
1030
+
1031
+ // TODO: check if we have to move this to parser
1032
+ const meetingAddedType = MeetingsUtil.getMeetingAddedType(type);
1033
+
1034
+ // We typically shouldn't need to trigger both and event and return a promise.
1035
+ // Is this a special case? We want to make the public API usage as simple as possible.
1036
+ Trigger.trigger(
1037
+ this,
1038
+ {
1039
+ file: 'meetings',
1040
+ function: 'createMeeting',
1041
+ },
1042
+ EVENT_TRIGGERS.MEETING_ADDED,
1043
+ {
1044
+ meeting,
1045
+ type: meetingAddedType,
1046
+ }
1047
+ );
1048
+ }
1049
+ }
1050
+
1051
+ return meeting;
1052
+
1053
+ // Create the meeting calling the necessary service endpoints.
1054
+
1055
+ // Internally, there are many more destinations:
1056
+ //
1057
+ // - locusID
1058
+ // - meetingURL
1059
+ // - globalMeetingID, e.g, *00*meetingID
1060
+ // - meetingID
1061
+ // - meetingURL
1062
+ // - PSTN
1063
+ // - phone number
1064
+ //
1065
+ // Our job is to determine the appropriate one
1066
+ // and its corresponding service so that developers
1067
+ // need only sipURL or spaceID to get a meeting
1068
+ // and its ID, but have the option to use createWithType()
1069
+ // and specify those types to get meetingInfo
1070
+ }
1071
+
1072
+ /**
1073
+ * get a specifc meeting given it's type matched to the value, i.e., locus url
1074
+ * @param {String} type
1075
+ * @param {Object} value
1076
+ * @returns {Meeting}
1077
+ * @public
1078
+ * @memberof Meetings
1079
+ */
1080
+ public getMeetingByType(type: string, value: object) {
1081
+ return this.meetingCollection.getByKey(type, value);
1082
+ }
1083
+
1084
+ /**
1085
+ * Get all meetings.
1086
+ * @param {object} options
1087
+ * @param {object} options.startDate - get meetings after this start date
1088
+ * @param {object} options.endDate - get meetings before this end date
1089
+ * @returns {Object} All currently active meetings.
1090
+ * @public
1091
+ * @memberof Meetings
1092
+ */
1093
+ public getAllMeetings(
1094
+ options: {
1095
+ startDate: object;
1096
+ endDate: object;
1097
+ } = {} as any
1098
+ ) {
1099
+ // Options may include other parameters to filter this collection
1100
+ // of meetings.
1101
+ return this.meetingCollection.getAll(options);
1102
+ }
1103
+
1104
+ /**
1105
+ * syncs all the meeting from server
1106
+ * @returns {undefined}
1107
+ * @public
1108
+ * @memberof Meetings
1109
+ */
1110
+ public syncMeetings() {
1111
+ return this.request.getActiveMeetings().then((locusArray) => {
1112
+ const activeLocusUrl = [];
1113
+
1114
+ if (locusArray?.loci && locusArray.loci.length > 0) {
1115
+ locusArray.loci.forEach((locus) => {
1116
+ activeLocusUrl.push(locus.url);
1117
+ this.handleLocusEvent({
1118
+ locus,
1119
+ locusUrl: locus.url,
1120
+ });
1121
+ });
1122
+ }
1123
+ const meetingsCollection = this.meetingCollection.getAll();
1124
+
1125
+ if (Object.keys(meetingsCollection).length > 0) {
1126
+ // Some time the mercury event is missed after mercury reconnect
1127
+ // if sync returns no locus then clear all the meetings
1128
+ for (const meeting of Object.values(meetingsCollection)) {
1129
+ // @ts-ignore
1130
+ if (!activeLocusUrl.includes(meeting.locusUrl)) {
1131
+ // destroy function also uploads logs
1132
+ // @ts-ignore
1133
+ this.destroy(meeting, MEETING_REMOVED_REASON.NO_MEETINGS_TO_SYNC);
1134
+ }
1135
+ }
1136
+ }
1137
+ });
1138
+ }
1139
+
1140
+ /**
1141
+ * Get all scheduled meetings.
1142
+ * @param {object} options
1143
+ * @param {object} options.startDate - get meetings after this start date
1144
+ * @param {object} options.endDate - get meetings before this end date
1145
+ * @returns {Object} All scheduled meetings.
1146
+ * @memberof Meetings
1147
+ */
1148
+ getScheduledMeetings() {
1149
+ return this.meetingCollection.getAll({scheduled: true});
1150
+ }
1151
+
1152
+ /**
1153
+ * Get the logger instance for plugin-meetings
1154
+ * @returns {Logger}
1155
+ */
1156
+ getLogger() {
1157
+ return LoggerProxy.get();
1158
+ }
1159
+ }