@webex/plugin-meetings 3.0.0-bnr.5 → 3.0.0-stream-classes.1

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