@webex/plugin-meetings 3.0.0-beta.16 → 3.0.0-beta.18

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 (156) hide show
  1. package/dist/breakouts/breakout.js +116 -0
  2. package/dist/breakouts/breakout.js.map +1 -0
  3. package/dist/breakouts/collection.js +23 -0
  4. package/dist/breakouts/collection.js.map +1 -0
  5. package/dist/breakouts/index.js +226 -0
  6. package/dist/breakouts/index.js.map +1 -0
  7. package/dist/config.js +4 -1
  8. package/dist/config.js.map +1 -1
  9. package/dist/constants.js +43 -6
  10. package/dist/constants.js.map +1 -1
  11. package/dist/locus-info/controlsUtils.js +2 -1
  12. package/dist/locus-info/controlsUtils.js.map +1 -1
  13. package/dist/locus-info/index.js +48 -0
  14. package/dist/locus-info/index.js.map +1 -1
  15. package/dist/locus-info/parser.js +1 -0
  16. package/dist/locus-info/parser.js.map +1 -1
  17. package/dist/locus-info/selfUtils.js +19 -11
  18. package/dist/locus-info/selfUtils.js.map +1 -1
  19. package/dist/media/index.js +3 -3
  20. package/dist/media/index.js.map +1 -1
  21. package/dist/media/properties.js +4 -4
  22. package/dist/media/properties.js.map +1 -1
  23. package/dist/meeting/in-meeting-actions.js +5 -1
  24. package/dist/meeting/in-meeting-actions.js.map +1 -1
  25. package/dist/meeting/index.js +652 -459
  26. package/dist/meeting/index.js.map +1 -1
  27. package/dist/meeting/request.js +25 -44
  28. package/dist/meeting/request.js.map +1 -1
  29. package/dist/meeting/request.type.js.map +1 -1
  30. package/dist/meeting/util.js +22 -57
  31. package/dist/meeting/util.js.map +1 -1
  32. package/dist/meeting-info/meeting-info-v2.js +2 -0
  33. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  34. package/dist/meetings/index.js +28 -18
  35. package/dist/meetings/index.js.map +1 -1
  36. package/dist/meetings/request.js +14 -12
  37. package/dist/meetings/request.js.map +1 -1
  38. package/dist/member/index.js +9 -0
  39. package/dist/member/index.js.map +1 -1
  40. package/dist/member/util.js +14 -1
  41. package/dist/member/util.js.map +1 -1
  42. package/dist/members/index.js +8 -6
  43. package/dist/members/index.js.map +1 -1
  44. package/dist/members/request.js +3 -1
  45. package/dist/members/request.js.map +1 -1
  46. package/dist/multistream/mediaRequestManager.js +46 -6
  47. package/dist/multistream/mediaRequestManager.js.map +1 -1
  48. package/dist/multistream/multistreamMedia.js +4 -0
  49. package/dist/multistream/multistreamMedia.js.map +1 -1
  50. package/dist/multistream/receiveSlot.js +3 -3
  51. package/dist/multistream/receiveSlot.js.map +1 -1
  52. package/dist/multistream/receiveSlotManager.js +8 -6
  53. package/dist/multistream/receiveSlotManager.js.map +1 -1
  54. package/dist/multistream/remoteMedia.js.map +1 -1
  55. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  56. package/dist/multistream/remoteMediaManager.js +168 -63
  57. package/dist/multistream/remoteMediaManager.js.map +1 -1
  58. package/dist/reachability/index.js +63 -51
  59. package/dist/reachability/index.js.map +1 -1
  60. package/dist/reactions/constants.js +13 -0
  61. package/dist/reactions/constants.js.map +1 -0
  62. package/dist/reactions/reactions.type.js.map +1 -1
  63. package/dist/reconnection-manager/index.js +25 -12
  64. package/dist/reconnection-manager/index.js.map +1 -1
  65. package/dist/recording-controller/enums.js +17 -0
  66. package/dist/recording-controller/enums.js.map +1 -0
  67. package/dist/recording-controller/index.js +343 -0
  68. package/dist/recording-controller/index.js.map +1 -0
  69. package/dist/recording-controller/util.js +63 -0
  70. package/dist/recording-controller/util.js.map +1 -0
  71. package/dist/roap/request.js +88 -68
  72. package/dist/roap/request.js.map +1 -1
  73. package/dist/roap/turnDiscovery.js +72 -47
  74. package/dist/roap/turnDiscovery.js.map +1 -1
  75. package/dist/statsAnalyzer/index.js +3 -3
  76. package/dist/statsAnalyzer/index.js.map +1 -1
  77. package/dist/statsAnalyzer/mqaUtil.js +18 -6
  78. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  79. package/package.json +24 -19
  80. package/src/breakouts/README.md +190 -0
  81. package/src/breakouts/breakout.ts +110 -0
  82. package/src/breakouts/collection.ts +19 -0
  83. package/src/breakouts/index.ts +225 -0
  84. package/src/config.ts +4 -1
  85. package/src/constants.ts +39 -1
  86. package/src/locus-info/controlsUtils.ts +2 -0
  87. package/src/locus-info/index.ts +59 -1
  88. package/src/locus-info/parser.ts +1 -0
  89. package/src/locus-info/selfUtils.ts +8 -0
  90. package/src/media/index.ts +1 -2
  91. package/src/media/properties.ts +6 -9
  92. package/src/meeting/in-meeting-actions.ts +8 -0
  93. package/src/meeting/index.ts +360 -111
  94. package/src/meeting/request.ts +9 -31
  95. package/src/meeting/request.type.ts +2 -0
  96. package/src/meeting/util.ts +25 -60
  97. package/src/meeting-info/meeting-info-v2.ts +2 -0
  98. package/src/meetings/index.ts +10 -5
  99. package/src/meetings/request.ts +1 -1
  100. package/src/member/index.ts +9 -0
  101. package/src/member/util.ts +14 -1
  102. package/src/members/index.ts +1 -0
  103. package/src/members/request.ts +1 -0
  104. package/src/multistream/mediaRequestManager.ts +79 -15
  105. package/src/multistream/multistreamMedia.ts +4 -0
  106. package/src/multistream/receiveSlot.ts +17 -12
  107. package/src/multistream/receiveSlotManager.ts +22 -21
  108. package/src/multistream/remoteMedia.ts +1 -1
  109. package/src/multistream/remoteMediaGroup.ts +2 -2
  110. package/src/multistream/remoteMediaManager.ts +150 -37
  111. package/src/reachability/index.ts +16 -13
  112. package/src/reactions/constants.ts +4 -0
  113. package/src/reactions/reactions.type.ts +25 -0
  114. package/src/reconnection-manager/index.ts +18 -9
  115. package/src/recording-controller/enums.ts +8 -0
  116. package/src/recording-controller/index.ts +315 -0
  117. package/src/recording-controller/util.ts +58 -0
  118. package/src/roap/request.ts +78 -73
  119. package/src/roap/turnDiscovery.ts +8 -6
  120. package/src/statsAnalyzer/index.ts +4 -4
  121. package/src/statsAnalyzer/mqaUtil.ts +6 -0
  122. package/test/unit/spec/breakouts/breakout.ts +119 -0
  123. package/test/unit/spec/breakouts/collection.ts +15 -0
  124. package/test/unit/spec/breakouts/index.ts +293 -0
  125. package/test/unit/spec/locus-info/controlsUtils.js +20 -0
  126. package/test/unit/spec/locus-info/index.js +103 -0
  127. package/test/unit/spec/locus-info/selfConstant.js +25 -0
  128. package/test/unit/spec/locus-info/selfUtils.js +84 -0
  129. package/test/unit/spec/media/index.ts +1 -1
  130. package/test/unit/spec/media/properties.ts +9 -9
  131. package/test/unit/spec/meeting/effectsState.js +5 -1
  132. package/test/unit/spec/meeting/in-meeting-actions.ts +5 -1
  133. package/test/unit/spec/meeting/index.js +241 -50
  134. package/test/unit/spec/meeting/request.js +17 -0
  135. package/test/unit/spec/meeting/utils.js +28 -122
  136. package/test/unit/spec/meetings/index.js +1 -0
  137. package/test/unit/spec/member/util.js +26 -1
  138. package/test/unit/spec/multistream/mediaRequestManager.ts +312 -50
  139. package/test/unit/spec/multistream/receiveSlot.ts +6 -6
  140. package/test/unit/spec/multistream/receiveSlotManager.ts +13 -13
  141. package/test/unit/spec/multistream/remoteMedia.ts +2 -2
  142. package/test/unit/spec/multistream/remoteMediaGroup.ts +5 -5
  143. package/test/unit/spec/multistream/remoteMediaManager.ts +354 -65
  144. package/test/unit/spec/reachability/index.ts +58 -24
  145. package/test/unit/spec/reconnection-manager/index.js +42 -13
  146. package/test/unit/spec/recording-controller/index.js +231 -0
  147. package/test/unit/spec/recording-controller/util.js +102 -0
  148. package/test/unit/spec/roap/index.ts +2 -1
  149. package/test/unit/spec/roap/request.ts +114 -0
  150. package/test/unit/spec/roap/turnDiscovery.ts +45 -29
  151. package/test/unit/spec/stats-analyzer/index.js +2 -2
  152. package/test/utils/webex-test-users.js +1 -0
  153. package/tsconfig.json +6 -0
  154. package/dist/media/internal-media-core-wrapper.js +0 -18
  155. package/dist/media/internal-media-core-wrapper.js.map +0 -1
  156. package/src/media/internal-media-core-wrapper.ts +0 -9
@@ -0,0 +1,225 @@
1
+ /*!
2
+ * Copyright (c) 2015-2023 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+ import {WebexPlugin} from '@webex/webex-core';
5
+ import {debounce, forEach} from 'lodash';
6
+ import LoggerProxy from '../common/logs/logger-proxy';
7
+
8
+ import {BREAKOUTS, MEETINGS} from '../constants';
9
+
10
+ import Breakout from './breakout';
11
+ import BreakoutCollection from './collection';
12
+
13
+ /**
14
+ * @class Breakouts
15
+ */
16
+ const Breakouts = WebexPlugin.extend({
17
+ namespace: MEETINGS,
18
+
19
+ collections: {
20
+ breakouts: BreakoutCollection,
21
+ },
22
+
23
+ props: {
24
+ allowBackToMain: 'boolean', // only present when in a breakout session
25
+ delayCloseTime: 'number', // appears once breakouts start
26
+ enableBreakoutSession: 'boolean', // appears from the moment you enable breakouts
27
+ groupId: 'string', // appears from the moment you enable breakouts
28
+ name: 'string', // only present when in a breakout session
29
+ sessionId: 'string', // appears from the moment you enable breakouts
30
+ sessionType: 'string', // appears from the moment you enable breakouts
31
+ startTime: 'string', // appears once breakouts start
32
+ status: 'string', // only present when in a breakout session
33
+ url: 'string', // appears from the moment you enable breakouts
34
+ locusUrl: 'string', // the current locus url
35
+ },
36
+
37
+ children: {
38
+ currentBreakoutSession: Breakout,
39
+ },
40
+
41
+ derived: {
42
+ isInMainSession: {
43
+ deps: ['sessionType'],
44
+ /**
45
+ * Returns true if the user is in the main session
46
+ * @returns {boolean}
47
+ */
48
+ fn() {
49
+ return this.sessionType === BREAKOUTS.SESSION_TYPES.MAIN;
50
+ },
51
+ },
52
+ },
53
+
54
+ /**
55
+ * initialize for the breakouts
56
+ * @returns {void}
57
+ */
58
+ initialize() {
59
+ this.listenTo(this, 'change:status', () => {
60
+ if (this.status === BREAKOUTS.STATUS.CLOSING) {
61
+ this.trigger(BREAKOUTS.EVENTS.BREAKOUTS_CLOSING);
62
+ }
63
+ });
64
+ this.debouncedQueryRosters = debounce(this.queryRosters, 10, {
65
+ leading: true,
66
+ trailing: false,
67
+ });
68
+ this.listenTo(this.breakouts, 'add', () => {
69
+ this.debouncedQueryRosters();
70
+ });
71
+ this.listenToBroadcastMessages();
72
+ this.listenToBreakoutRosters();
73
+ },
74
+
75
+ /**
76
+ * Calls this to clean up listeners
77
+ * @returns {void}
78
+ */
79
+ cleanUp() {
80
+ this.stopListening();
81
+ },
82
+
83
+ /**
84
+ * Update the current locus url of the meeting
85
+ * @param {string} locusUrl // locus url
86
+ * @returns {void}
87
+ */
88
+ locusUrlUpdate(locusUrl) {
89
+ this.set('locusUrl', locusUrl);
90
+ },
91
+
92
+ /**
93
+ * The initial roster lists need to be queried because you don't
94
+ * get a breakout.roster event when you join the meeting
95
+ * @returns {void}
96
+ */
97
+ queryRosters() {
98
+ this.webex
99
+ .request({uri: `${this.url}/roster`, qs: {locusUrl: btoa(this.locusUrl)}})
100
+ .then((result) => {
101
+ const {
102
+ body: {rosters},
103
+ } = result;
104
+
105
+ rosters.forEach(({locus}) => {
106
+ this.handleRosterUpdate(locus);
107
+ });
108
+
109
+ this.trigger(BREAKOUTS.EVENTS.MEMBERS_UPDATE);
110
+ })
111
+ .catch((error) => {
112
+ LoggerProxy.logger.error('Meeting:breakouts#queryRosters failed', error);
113
+ });
114
+ },
115
+
116
+ /**
117
+ *
118
+ * @param {Object} locus // locus object
119
+ * @returns {void}
120
+ */
121
+ handleRosterUpdate(locus) {
122
+ const sessionId = locus.controls?.breakout?.sessionId;
123
+
124
+ const session = this.breakouts.get(sessionId);
125
+
126
+ if (!session) {
127
+ return;
128
+ }
129
+
130
+ session.parseRoster(locus);
131
+ },
132
+
133
+ /**
134
+ * Sets up listener for broadcast messages sent to the breakout session
135
+ * @returns {void}
136
+ */
137
+ listenToBroadcastMessages() {
138
+ this.listenTo(this.webex.internal.llm, 'event:breakout.message', (event) => {
139
+ const {
140
+ data: {senderUserId, sentTime, message},
141
+ } = event;
142
+
143
+ this.trigger(BREAKOUTS.EVENTS.MESSAGE, {
144
+ senderUserId,
145
+ sentTime,
146
+ message,
147
+ // FIXME: This is only the current sessionId
148
+ // We'd need to check that the dataChannelUrl is still the same
149
+ // to guarantee that this message was sent to this session
150
+ sessionId: this.currentBreakoutSession.sessionId,
151
+ });
152
+ });
153
+ },
154
+
155
+ /**
156
+ * Sets up a listener for roster messags from mecury
157
+ * @returns {void}
158
+ */
159
+ listenToBreakoutRosters() {
160
+ this.listenTo(this.webex.internal.mercury, 'event:breakout.roster', (event) => {
161
+ this.handleRosterUpdate(event.data.locus);
162
+ this.trigger(BREAKOUTS.EVENTS.MEMBERS_UPDATE);
163
+ });
164
+ },
165
+
166
+ /**
167
+ * Updates the information about the current breakout
168
+ * @param {Object} params
169
+ * @returns {void}
170
+ */
171
+ updateBreakout(params) {
172
+ this.set(params);
173
+
174
+ this.set('currentBreakoutSession', {
175
+ sessionId: params.sessionId,
176
+ groupId: params.groupId,
177
+ name: params.name,
178
+ current: true,
179
+ sessionType: params.sessionType,
180
+ url: params.url,
181
+ [BREAKOUTS.SESSION_STATES.ACTIVE]: false,
182
+ [BREAKOUTS.SESSION_STATES.ALLOWED]: false,
183
+ [BREAKOUTS.SESSION_STATES.ALLOWED]: false,
184
+ [BREAKOUTS.SESSION_STATES.ASSIGNED_CURRENT]: false,
185
+ [BREAKOUTS.SESSION_STATES.REQUESTED]: false,
186
+ });
187
+ },
188
+
189
+ /**
190
+ * Updates the information about available breakouts
191
+ * @param {Object} payload
192
+ * @returns {void}
193
+ */
194
+ updateBreakoutSessions(payload) {
195
+ const breakouts = {};
196
+
197
+ if (payload.breakoutSessions) {
198
+ forEach(BREAKOUTS.SESSION_STATES, (state) => {
199
+ forEach(payload.breakoutSessions[state], (breakout) => {
200
+ const {sessionId} = breakout;
201
+
202
+ if (!breakouts[sessionId]) {
203
+ breakouts[sessionId] = breakout;
204
+ breakouts[sessionId][BREAKOUTS.SESSION_STATES.ACTIVE] = false;
205
+ breakouts[sessionId][BREAKOUTS.SESSION_STATES.ASSIGNED] = false;
206
+ breakouts[sessionId][BREAKOUTS.SESSION_STATES.ALLOWED] = false;
207
+ breakouts[sessionId][BREAKOUTS.SESSION_STATES.ASSIGNED_CURRENT] = false;
208
+ breakouts[sessionId][BREAKOUTS.SESSION_STATES.REQUESTED] = false;
209
+ }
210
+
211
+ breakouts[sessionId][state] = true;
212
+ });
213
+ });
214
+ }
215
+
216
+ forEach(breakouts, (breakout: typeof Breakout) => {
217
+ // eslint-disable-next-line no-param-reassign
218
+ breakout.url = this.url;
219
+ });
220
+
221
+ this.breakouts.set(Object.values(breakouts));
222
+ },
223
+ });
224
+
225
+ export default Breakouts;
package/src/config.ts CHANGED
@@ -93,7 +93,10 @@ export default {
93
93
  enableMediaNegotiatedEvent: false,
94
94
  enableUnifiedMeetings: false,
95
95
  enableAdhocMeetings: false,
96
- enableTurnDiscovery: false,
96
+ enableTurnDiscovery: true,
97
+ },
98
+ degradationPreferences: {
99
+ maxMacroblocksLimit: 32400,
97
100
  },
98
101
  },
99
102
  };
package/src/constants.ts CHANGED
@@ -288,6 +288,7 @@ export const EVENT_TRIGGERS = {
288
288
  MEETING_STOPPED_RECORDING: 'meeting:recording:stopped',
289
289
  MEETING_STARTED_RECEIVING_TRANSCRIPTION: 'meeting:receiveTranscription:started',
290
290
  MEETING_STOPPED_RECEIVING_TRANSCRIPTION: 'meeting:receiveTranscription:stopped',
291
+ MEETING_RECEIVE_REACTIONS: 'meeting:receiveReactions',
291
292
  MEETING_PAUSED_RECORDING: 'meeting:recording:paused',
292
293
  MEETING_RESUMED_RECORDING: 'meeting:recording:resumed',
293
294
  MEETING_ADDED: 'meeting:added',
@@ -304,6 +305,9 @@ export const EVENT_TRIGGERS = {
304
305
  MEETING_SELF_IS_SHARING_BLOCKED: 'meeting:self:isSharingBlocked',
305
306
  MEETING_CONTROLS_LAYOUT_UPDATE: 'meeting:layout:update',
306
307
  MEETING_ENTRY_EXIT_TONE_UPDATE: 'meeting:entryExitTone:update',
308
+ MEETING_BREAKOUTS_UPDATE: 'meeting:breakouts:update',
309
+ MEETING_BREAKOUTS_CLOSING: 'meeting:breakouts:closing',
310
+ MEETING_BREAKOUTS_MESSAGE: 'meeting:breakouts:message',
307
311
  MEMBERS_UPDATE: 'members:update',
308
312
  MEMBERS_CONTENT_UPDATE: 'members:content:update',
309
313
  MEMBERS_HOST_UPDATE: 'members:host:update',
@@ -331,7 +335,7 @@ export const EVENT_TRIGGERS = {
331
335
  REMOTE_VIDEO_SOURCE_COUNT_CHANGED: 'media:remoteVideoSourceCountChanged',
332
336
  REMOTE_AUDIO_SOURCE_COUNT_CHANGED: 'media:remoteAudioSourceCountChanged',
333
337
  REMOTE_MEDIA_AUDIO_CREATED: 'media:remoteAudio:created',
334
- REMOTE_MEDIA_SCREEN_SHARE_AUDIO_CREATED: 'media:remoteScrenShareAudio:created',
338
+ REMOTE_MEDIA_SCREEN_SHARE_AUDIO_CREATED: 'media:remoteScreenShareAudio:created',
335
339
  REMOTE_MEDIA_VIDEO_LAYOUT_CHANGED: 'media:remoteVideo:layoutChanged',
336
340
  };
337
341
 
@@ -514,11 +518,34 @@ export const LOCUS = {
514
518
  SYNCDEBUG: 'sync_debug',
515
519
  };
516
520
 
521
+ export const BREAKOUTS = {
522
+ STATUS: {
523
+ CLOSING: 'CLOSING',
524
+ },
525
+ EVENTS: {
526
+ BREAKOUTS_CLOSING: 'BREAKOUTS_CLOSING',
527
+ MESSAGE: 'MESSAGE',
528
+ MEMBERS_UPDATE: 'MEMBERS_UPDATE',
529
+ },
530
+ SESSION_TYPES: {
531
+ MAIN: 'MAIN',
532
+ },
533
+ SESSION_STATES: {
534
+ ACTIVE: 'active',
535
+ ASSIGNED: 'assigned',
536
+ ALLOWED: 'allowed',
537
+ ASSIGNED_CURRENT: 'assignedCurrent',
538
+ REQUESTED: 'requested',
539
+ },
540
+ BREAKOUTS_SUPPORTED: 'BREAKOUTS_SUPPORTED',
541
+ };
542
+
517
543
  export const LOCUSINFO = {
518
544
  EVENTS: {
519
545
  CONTROLS_MEETING_LAYOUT_UPDATED: 'CONTROLS_MEETING_LAYOUT_UPDATED',
520
546
  CONTROLS_RECORDING_UPDATED: 'CONTROLS_RECORDING_UPDATED',
521
547
  CONTROLS_MEETING_TRANSCRIBE_UPDATED: 'CONTROLS_MEETING_TRANSCRIBE_UPDATED',
548
+ CONTROLS_MEETING_BREAKOUT_UPDATED: 'CONTROLS_MEETING_BREAKOUT_UPDATED',
522
549
  CONTROLS_MEETING_CONTAINER_UPDATED: 'CONTROLS_MEETING_CONTAINER_UPDATED',
523
550
  CONTROLS_ENTRY_EXIT_TONE_UPDATED: 'CONTROLS_ENTRY_EXIT_TONE_UPDATED',
524
551
  SELF_UNADMITTED_GUEST: 'SELF_UNADMITTED_GUEST',
@@ -540,6 +567,9 @@ export const LOCUSINFO = {
540
567
  EMBEDDED_APPS_UPDATED: 'EMBEDDED_APPS_UPDATED',
541
568
  SELF_CANNOT_VIEW_PARTICIPANT_LIST_CHANGE: 'SELF_CANNOT_VIEW_PARTICIPANT_LIST_CHANGE',
542
569
  SELF_IS_SHARING_BLOCKED_CHANGE: 'SELF_IS_SHARING_BLOCKED_CHANGE',
570
+ SELF_MEETING_BREAKOUTS_CHANGED: 'SELF_MEETING_BREAKOUTS_CHANGED',
571
+ MEDIA_INACTIVITY: 'MEDIA_INACTIVITY',
572
+ LINKS_SERVICES: 'LINKS_SERVICES',
543
573
  },
544
574
  };
545
575
 
@@ -552,6 +582,9 @@ export const LOCUSEVENT = {
552
582
  // delta events
553
583
  DIFFERENCE: 'locus.difference',
554
584
 
585
+ // Breakout sessions
586
+ BREAKOUT_ROSTER: 'breakout.roster',
587
+
555
588
  // screen sharing
556
589
  FLOOR_GRANTED: 'locus.floor_granted',
557
590
  FLOOR_RELEASED: 'locus.floor_released',
@@ -724,6 +757,10 @@ export const DISPLAY_HINTS = {
724
757
  TRANSCRIPTION_CONTROL_STOP: 'TRANSCRIPTION_CONTROL_STOP',
725
758
  WEBEX_ASSISTANT_STATUS_ACTIVE: 'WEBEX_ASSISTANT_STATUS_ACTIVE',
726
759
  WAITING_FOR_OTHERS: 'WAITING_FOR_OTHERS',
760
+ ENABLE_REACTIONS: 'ENABLE_REACTIONS',
761
+ DISABLE_REACTIONS: 'DISABLE_REACTIONS',
762
+ REACTIONS_ACTIVE: 'REACTIONS_ACTIVE',
763
+ REACTIONS_INACTIVE: 'REACTIONS_INACTIVE',
727
764
  };
728
765
 
729
766
  export const SELF_ROLES = {
@@ -814,6 +851,7 @@ export const RESOURCE = {
814
851
 
815
852
  export const REACHABILITY = {
816
853
  localStorage: 'reachability.result',
854
+ namespace: 'Reachability',
817
855
  };
818
856
 
819
857
  export const ROAP = {
@@ -92,6 +92,8 @@ ControlsUtils.getControls = (oldControls: any, newControls: any) => {
92
92
  !isEqual(previous?.entryExitTone, current?.entryExitTone) &&
93
93
  (previous?.entryExitTone || current?.entryExitTone)
94
94
  ),
95
+
96
+ hasBreakoutChanged: !isEqual(previous?.breakout, current?.breakout),
95
97
  },
96
98
  };
97
99
  };
@@ -62,6 +62,7 @@ export default class LocusInfo extends EventsScope {
62
62
  mediaShares: any;
63
63
  replace: any;
64
64
  url: any;
65
+ services: any;
65
66
 
66
67
  constructor(updateMeeting, webex, meetingId) {
67
68
  super();
@@ -185,6 +186,7 @@ export default class LocusInfo extends EventsScope {
185
186
  this.updateSelf(locus.self, locus.participants);
186
187
  this.updateHostInfo(locus.host);
187
188
  this.updateMediaShares(locus.mediaShares);
189
+ this.updateServices(locus.links?.services);
188
190
  }
189
191
 
190
192
  /**
@@ -337,7 +339,14 @@ export default class LocusInfo extends EventsScope {
337
339
  * @returns {undefined}
338
340
  * @memberof LocusInfo
339
341
  */
340
- updateLocusInfo(locus: any) {
342
+ updateLocusInfo(locus) {
343
+ if (locus.self?.reason === 'MOVED' && locus.self?.state === 'LEFT') {
344
+ // When moved to a breakout session locus sends a message for the previous locus
345
+ // indicating that we have been moved. It isn't helpful to continue parsing this
346
+ // as it gets interpreted as if we have left the call
347
+ return;
348
+ }
349
+
341
350
  this.updateControls(locus.controls);
342
351
  this.updateConversationUrl(locus.conversationUrl, locus.info);
343
352
  this.updateCreated(locus.created);
@@ -355,6 +364,7 @@ export default class LocusInfo extends EventsScope {
355
364
  this.updateMemberShip(locus.membership);
356
365
  this.updateIdentifiers(locus.identities);
357
366
  this.updateEmbeddedApps(locus.embeddedApps);
367
+ this.updateServices(locus.links?.services);
358
368
  this.compareAndUpdate();
359
369
  // update which required to compare different objects from locus
360
370
  }
@@ -674,6 +684,7 @@ export default class LocusInfo extends EventsScope {
674
684
  hasMeetingContainerChanged,
675
685
  hasTranscribeChanged,
676
686
  hasEntryExitToneChanged,
687
+ hasBreakoutChanged,
677
688
  },
678
689
  current,
679
690
  } = ControlsUtils.getControls(this.controls, controls);
@@ -737,6 +748,21 @@ export default class LocusInfo extends EventsScope {
737
748
  );
738
749
  }
739
750
 
751
+ if (hasBreakoutChanged) {
752
+ const {breakout} = current;
753
+
754
+ this.emitScoped(
755
+ {
756
+ file: 'locus-info',
757
+ function: 'updateControls',
758
+ },
759
+ LOCUSINFO.EVENTS.CONTROLS_MEETING_BREAKOUT_UPDATED,
760
+ {
761
+ breakout,
762
+ }
763
+ );
764
+ }
765
+
740
766
  if (hasEntryExitToneChanged) {
741
767
  const {entryExitTone} = current;
742
768
 
@@ -789,6 +815,27 @@ export default class LocusInfo extends EventsScope {
789
815
  }
790
816
  }
791
817
 
818
+ /**
819
+ * @param {Object} services
820
+ * @returns {undefined}
821
+ * @memberof LocusInfo
822
+ */
823
+ updateServices(services: Record<'breakout' | 'record', {url: string}>) {
824
+ if (services && !isEqual(this.services, services)) {
825
+ this.services = services;
826
+ this.emitScoped(
827
+ {
828
+ file: 'locus-info',
829
+ function: 'updateServices',
830
+ },
831
+ LOCUSINFO.EVENTS.LINKS_SERVICES,
832
+ {
833
+ services,
834
+ }
835
+ );
836
+ }
837
+ }
838
+
792
839
  /**
793
840
  * @param {Object} fullState
794
841
  * @returns {undefined}
@@ -1041,6 +1088,17 @@ export default class LocusInfo extends EventsScope {
1041
1088
  );
1042
1089
  }
1043
1090
 
1091
+ if (parsedSelves.updates.breakoutsChanged) {
1092
+ this.emitScoped(
1093
+ {
1094
+ file: 'locus-info',
1095
+ function: 'updateSelf',
1096
+ },
1097
+ LOCUSINFO.EVENTS.SELF_MEETING_BREAKOUTS_CHANGED,
1098
+ {breakoutSessions: parsedSelves.current.breakoutSessions}
1099
+ );
1100
+ }
1101
+
1044
1102
  if (parsedSelves.updates.isMediaInactiveOrReleased) {
1045
1103
  this.emitScoped(
1046
1104
  {
@@ -397,6 +397,7 @@ export default class Parser {
397
397
  const {isLoci} = Parser;
398
398
  // @ts-ignore
399
399
  const setStatus = (status) => {
400
+ // @ts-ignore
400
401
  this.status = status;
401
402
  };
402
403
 
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable default-param-last */
2
2
 
3
+ import {isEqual} from 'lodash';
3
4
  import {
4
5
  _IDLE_,
5
6
  _JOINED_,
@@ -60,12 +61,15 @@ SelfUtils.parse = (self: any, deviceId: string) => {
60
61
  layout: SelfUtils.getLayout(self),
61
62
  canNotViewTheParticipantList: SelfUtils.canNotViewTheParticipantList(self),
62
63
  isSharingBlocked: SelfUtils.isSharingBlocked(self),
64
+ breakoutSessions: SelfUtils.getBreakouts(self),
63
65
  };
64
66
  }
65
67
 
66
68
  return null;
67
69
  };
68
70
 
71
+ SelfUtils.getBreakouts = (self) => self?.controls?.breakout?.sessions;
72
+
69
73
  SelfUtils.getLayout = (self) =>
70
74
  Array.isArray(self?.controls?.layouts) ? self.controls.layouts[0].type : undefined;
71
75
 
@@ -114,6 +118,7 @@ SelfUtils.getSelves = (oldSelf, newSelf, deviceId) => {
114
118
  updates.canNotViewTheParticipantListChanged =
115
119
  previous?.canNotViewTheParticipantList !== current.canNotViewTheParticipantList;
116
120
  updates.isSharingBlockedChanged = previous?.isSharingBlocked !== current.isSharingBlocked;
121
+ updates.breakoutsChanged = SelfUtils.breakoutsChanged(previous, current);
117
122
 
118
123
  return {
119
124
  previous,
@@ -139,6 +144,9 @@ SelfUtils.isJoined = (self: any) => self?.state === _JOINED_;
139
144
  SelfUtils.layoutChanged = (previous: any, current: any) =>
140
145
  current?.layout && previous?.layout !== current?.layout;
141
146
 
147
+ SelfUtils.breakoutsChanged = (previous, current) =>
148
+ !isEqual(previous?.breakoutSessions, current?.breakoutSessions);
149
+
142
150
  SelfUtils.isMediaInactive = (previous, current) => {
143
151
  if (
144
152
  previous &&
@@ -3,6 +3,7 @@
3
3
  */
4
4
  /* globals navigator */
5
5
 
6
+ import {RoapMediaConnection, MultistreamRoapMediaConnection} from '@webex/internal-media-core';
6
7
  import LoggerProxy from '../common/logs/logger-proxy';
7
8
  import {AUDIO_INPUT, VIDEO_INPUT, MEDIA_TRACK_CONSTRAINT} from '../constants';
8
9
  import Config from '../config';
@@ -10,8 +11,6 @@ import StaticConfig from '../common/config';
10
11
  import MediaError from '../common/errors/media';
11
12
  import BrowserDetection from '../common/browser-detection';
12
13
 
13
- import {RoapMediaConnection, MultistreamRoapMediaConnection} from './internal-media-core-wrapper';
14
-
15
14
  const {isBrowser} = BrowserDetection();
16
15
 
17
16
  /**
@@ -1,4 +1,4 @@
1
- import {MediaConnection as MC} from '@webex/internal-media-core';
1
+ import {ConnectionState, Event} from '@webex/internal-media-core';
2
2
 
3
3
  import {MEETINGS, PC_BAIL_TIMEOUT, QUALITY_LEVELS} from '../constants';
4
4
  import LoggerProxy from '../common/logs/logger-proxy';
@@ -214,13 +214,13 @@ export default class MediaProperties {
214
214
  */
215
215
  waitForMediaConnectionConnected() {
216
216
  const isConnected = () =>
217
- this.webrtcMediaConnection.getConnectionState() === MC.ConnectionState.Connected;
217
+ this.webrtcMediaConnection.getConnectionState() === ConnectionState.Connected;
218
218
 
219
219
  if (isConnected()) {
220
220
  return Promise.resolve();
221
221
  }
222
222
 
223
- return new Promise((resolve, reject) => {
223
+ return new Promise<void>((resolve, reject) => {
224
224
  let timer;
225
225
 
226
226
  const connectionStateListener = () => {
@@ -230,20 +230,17 @@ export default class MediaProperties {
230
230
 
231
231
  if (isConnected()) {
232
232
  clearTimeout(timer);
233
- this.webrtcMediaConnection.off(
234
- MC.Event.CONNECTION_STATE_CHANGED,
235
- connectionStateListener
236
- );
233
+ this.webrtcMediaConnection.off(Event.CONNECTION_STATE_CHANGED, connectionStateListener);
237
234
  resolve();
238
235
  }
239
236
  };
240
237
 
241
238
  timer = setTimeout(() => {
242
- this.webrtcMediaConnection.off(MC.Event.CONNECTION_STATE_CHANGED, connectionStateListener);
239
+ this.webrtcMediaConnection.off(Event.CONNECTION_STATE_CHANGED, connectionStateListener);
243
240
  reject();
244
241
  }, PC_BAIL_TIMEOUT);
245
242
 
246
- this.webrtcMediaConnection.on(MC.Event.CONNECTION_STATE_CHANGED, connectionStateListener);
243
+ this.webrtcMediaConnection.on(Event.CONNECTION_STATE_CHANGED, connectionStateListener);
247
244
  });
248
245
  }
249
246
 
@@ -31,6 +31,8 @@ interface IInMeetingActions {
31
31
  isRealTimeTranslationEnabled?: boolean;
32
32
  canSelectSpokenLanguages?: boolean;
33
33
  waitingForOthersToJoin?: boolean;
34
+ canEnableReactions?: boolean;
35
+ canSendReactions?: boolean;
34
36
  }
35
37
 
36
38
  /**
@@ -83,6 +85,10 @@ export default class InMeetingActions implements IInMeetingActions {
83
85
 
84
86
  waitingForOthersToJoin = null;
85
87
 
88
+ canEnableReactions = null;
89
+
90
+ canSendReactions = null;
91
+
86
92
  /**
87
93
  * Returns all meeting action options
88
94
  * @returns {Object}
@@ -110,6 +116,8 @@ export default class InMeetingActions implements IInMeetingActions {
110
116
  isRealTimeTranslationEnabled: this.isRealTimeTranslationEnabled,
111
117
  canSelectSpokenLanguages: this.canSelectSpokenLanguages,
112
118
  waitingForOthersToJoin: this.waitingForOthersToJoin,
119
+ canEnableReactions: this.canEnableReactions,
120
+ canSendReactions: this.canSendReactions,
113
121
  });
114
122
 
115
123
  /**