@webex/plugin-meetings 3.0.0-beta.160 → 3.0.0-beta.161

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 (54) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +12 -2
  4. package/dist/constants.js.map +1 -1
  5. package/dist/interpretation/collection.js +23 -0
  6. package/dist/interpretation/collection.js.map +1 -0
  7. package/dist/interpretation/index.js +214 -0
  8. package/dist/interpretation/index.js.map +1 -0
  9. package/dist/interpretation/siLanguage.js +25 -0
  10. package/dist/interpretation/siLanguage.js.map +1 -0
  11. package/dist/locus-info/controlsUtils.js +1 -0
  12. package/dist/locus-info/controlsUtils.js.map +1 -1
  13. package/dist/locus-info/index.js +19 -0
  14. package/dist/locus-info/index.js.map +1 -1
  15. package/dist/locus-info/selfUtils.js +20 -11
  16. package/dist/locus-info/selfUtils.js.map +1 -1
  17. package/dist/meeting/index.js +402 -354
  18. package/dist/meeting/index.js.map +1 -1
  19. package/dist/meeting/util.js +1 -0
  20. package/dist/meeting/util.js.map +1 -1
  21. package/dist/member/index.js +2 -0
  22. package/dist/member/index.js.map +1 -1
  23. package/dist/member/util.js +11 -0
  24. package/dist/member/util.js.map +1 -1
  25. package/dist/types/constants.d.ts +9 -0
  26. package/dist/types/interpretation/collection.d.ts +5 -0
  27. package/dist/types/interpretation/index.d.ts +5 -0
  28. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  29. package/dist/types/meeting/index.d.ts +8 -0
  30. package/dist/types/member/index.d.ts +1 -0
  31. package/package.json +19 -19
  32. package/src/constants.ts +10 -0
  33. package/src/interpretation/README.md +51 -0
  34. package/src/interpretation/collection.ts +19 -0
  35. package/src/interpretation/index.ts +182 -0
  36. package/src/interpretation/siLanguage.ts +18 -0
  37. package/src/locus-info/controlsUtils.ts +2 -0
  38. package/src/locus-info/index.ts +29 -0
  39. package/src/locus-info/selfUtils.ts +6 -0
  40. package/src/meeting/index.ts +62 -1
  41. package/src/meeting/util.ts +1 -0
  42. package/src/member/index.ts +2 -0
  43. package/src/member/util.ts +14 -0
  44. package/test/unit/spec/interpretation/collection.ts +15 -0
  45. package/test/unit/spec/interpretation/index.ts +329 -0
  46. package/test/unit/spec/interpretation/siLanguage.ts +26 -0
  47. package/test/unit/spec/locus-info/controlsUtils.js +20 -0
  48. package/test/unit/spec/locus-info/index.js +64 -0
  49. package/test/unit/spec/locus-info/selfConstant.js +10 -0
  50. package/test/unit/spec/locus-info/selfUtils.js +26 -0
  51. package/test/unit/spec/meeting/index.js +62 -1
  52. package/test/unit/spec/meeting/utils.js +2 -0
  53. package/test/unit/spec/member/index.js +11 -4
  54. package/test/unit/spec/member/util.js +24 -0
@@ -0,0 +1,51 @@
1
+ # Simultaneous Interpretation
2
+
3
+ Simultaneous Interpretation (SI) feature provides support for interpretation of in-meeting audio. The host will specify the target languages at schedule time and assign language pairs to the desired interpreters. Each interpreter will use their client to indicate the language spoken at any point in time. For example, Alice might be translating between English and French. When an English speaker is talking in the meeting, Alice will use her client to indicate she is speaking French. When a French speaker is talking, Alice will indicate that she is speaking English. The host’s client will display the status of each interpreter including the current interpretation direction.
4
+
5
+ ### Structure
6
+ SI languages are available in the siLanguages collection. List the languages which current meeting support to do simultaneous interpretation. Can subscribe the language's voice channel which you want to listen from the list.
7
+
8
+ ```javascript
9
+ interpretation.siLanguages;
10
+ ```
11
+ ### Attendee functionality
12
+ ```javascript
13
+ //subscribe this si language's voice channel
14
+ siLanguage.subscribe();
15
+
16
+ //unsubscribe this si language's voice channel
17
+ siLanguage.unsubscribe();
18
+ ```
19
+ ### Host functionality
20
+ The following are methods available to the host of a meeting.
21
+
22
+ ```javascript
23
+ //get the support list of interpretation languages. only host is allowed to call it
24
+ interpretation.getSupportLanguages();
25
+
26
+ //get the interpreters list of the meeting
27
+ interpretation.getInterpreters();
28
+
29
+ //update the interpreters list, input parameter is an array of interpreters
30
+ interpretation.updateInterpreters([{
31
+ sourceLanguage : 'fr-FR',
32
+ targetLanguage : 'zh-ZH',
33
+ usingResource : {
34
+ id : 'a96747e2-1fc6-41d3-9ac7-512dd9478b6e'
35
+ },
36
+ order : 0
37
+ },]);
38
+ ```
39
+
40
+ ### Interpreter functionality
41
+
42
+ The following are methods available to the interpreters of a meeting.
43
+
44
+ ```javascript
45
+ //Change direction of interpretation for an interpreter participant
46
+ interpretation.changeDirection();
47
+
48
+ //Handoff between interpreters, will implement them later
49
+ interpretation.handoff(participantId)
50
+
51
+ ```
@@ -0,0 +1,19 @@
1
+ /*!
2
+ * Copyright (c) 2015-2023 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ import AmpCollection from 'ampersand-collection';
6
+
7
+ import {MEETINGS} from '../constants';
8
+
9
+ import SILanguage from './siLanguage';
10
+
11
+ const SILanguageCollection = AmpCollection.extend({
12
+ model: SILanguage,
13
+
14
+ namespace: MEETINGS,
15
+
16
+ mainIndex: 'languageName',
17
+ });
18
+
19
+ export default SILanguageCollection;
@@ -0,0 +1,182 @@
1
+ /*!
2
+ * Copyright (c) 2015-2023 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+ import {WebexPlugin} from '@webex/webex-core';
5
+ import LoggerProxy from '../common/logs/logger-proxy';
6
+ import {HTTP_VERBS, INTERPRETATION, MEETINGS} from '../constants';
7
+
8
+ import SILanguageCollection from './collection';
9
+
10
+ /**
11
+ * @class SimultaneousInterpretation
12
+ */
13
+ const SimultaneousInterpretation = WebexPlugin.extend({
14
+ namespace: MEETINGS,
15
+ collections: {
16
+ siLanguages: SILanguageCollection,
17
+ },
18
+
19
+ props: {
20
+ locusUrl: 'string', // appears current meeting's locus url
21
+ originalLanguage: 'string', // appears current meeting's original language
22
+ sourceLanguage: 'string', // appears self interpreter's source language
23
+ targetLanguage: 'string', // appears self interpreter's target language
24
+ receiveLanguage: 'string', // appears self's receive language
25
+ order: 'number', // appears the order of self as interpreter
26
+ isActive: 'boolean', // appears self is interpreter and is active
27
+ selfParticipantId: 'string', // appears the self participant id
28
+ canManageInterpreters: 'boolean', // appears the ability to manage interpreters
29
+ supportLanguages: 'array', // appears the support languages
30
+ siEnabled: 'boolean', // appears the meeting enabled SI
31
+ },
32
+ derived: {
33
+ shouldQuerySupportLanguages: {
34
+ cache: false,
35
+ deps: ['canManageInterpreters', 'siEnabled'],
36
+ /**
37
+ * Returns should query support languages or not
38
+ * @returns {boolean}
39
+ */
40
+ fn() {
41
+ return !!(this.canManageInterpreters && this.siEnabled);
42
+ },
43
+ },
44
+ },
45
+ /**
46
+ * initialize for interpretation
47
+ * @returns {void}
48
+ */
49
+ initialize() {
50
+ this.listenTo(this, 'change:shouldQuerySupportLanguages', () => {
51
+ if (this.canManageInterpreters && !this.supportLanguages) {
52
+ this.querySupportLanguages();
53
+ }
54
+ });
55
+ },
56
+
57
+ /**
58
+ * Calls this to clean up listeners
59
+ * @returns {void}
60
+ */
61
+ cleanUp() {
62
+ this.stopListening();
63
+ },
64
+ /**
65
+ * Update the current locus url of the meeting
66
+ * @param {string} locusUrl // locus url
67
+ * @returns {void}
68
+ */
69
+ locusUrlUpdate(locusUrl) {
70
+ this.set('locusUrl', locusUrl);
71
+ },
72
+ /**
73
+ * Update whether self has capability to manage interpreters (only host can manage it)
74
+ * @param {boolean} canManageInterpreters
75
+ * @returns {void}
76
+ */
77
+ updateCanManageInterpreters(canManageInterpreters) {
78
+ this.set('canManageInterpreters', canManageInterpreters);
79
+ },
80
+ /**
81
+ * Update the interpretation languages channels which user can choose to subscribe
82
+ * @param {Object} interpretation
83
+ * @returns {void}
84
+ */
85
+ updateInterpretation(interpretation) {
86
+ this.set('siEnabled', !!interpretation);
87
+ this.siLanguages.set(interpretation?.siLanguages || []);
88
+ },
89
+ /**
90
+ * Update self's interpretation information (self is interpreter)
91
+ * @param {Object} interpretation
92
+ * @param {String} selfParticipantId
93
+ * @returns {void}
94
+ */
95
+ updateSelfInterpretation({interpretation, selfParticipantId}) {
96
+ const {originalLanguage, sourceLanguage, order, isActive, targetLanguage, receiveLanguage} =
97
+ interpretation || {};
98
+ this.set({originalLanguage, sourceLanguage, order, isActive, targetLanguage, receiveLanguage});
99
+ this.set('selfParticipantId', selfParticipantId);
100
+ },
101
+ /**
102
+ * query interpretation languages
103
+ * @returns {Promise}
104
+ */
105
+ querySupportLanguages() {
106
+ return this.request({
107
+ method: HTTP_VERBS.GET,
108
+ uri: `${this.locusUrl}/languages/interpretation`,
109
+ })
110
+ .then((result) => {
111
+ this.set('supportLanguages', result.body?.siLanguages);
112
+ this.trigger(INTERPRETATION.EVENTS.SUPPORT_LANGUAGES_UPDATE);
113
+ })
114
+ .catch((error) => {
115
+ LoggerProxy.logger.error('Meeting:interpretation#querySupportLanguages failed', error);
116
+ throw error;
117
+ });
118
+ },
119
+ /**
120
+ * get interpreters of the meeting
121
+ * @returns {Promise}
122
+ */
123
+ getInterpreters() {
124
+ return this.request({
125
+ method: HTTP_VERBS.GET,
126
+ uri: `${this.locusUrl}/interpretation/interpreters`,
127
+ }).catch((error) => {
128
+ LoggerProxy.logger.error('Meeting:interpretation#getInterpreters failed', error);
129
+ throw error;
130
+ });
131
+ },
132
+ /**
133
+ * update interpreters of the meeting
134
+ * @param {Array} interpreters
135
+ * @returns {Promise}
136
+ */
137
+ updateInterpreters(interpreters) {
138
+ return this.request({
139
+ method: HTTP_VERBS.PATCH,
140
+ uri: `${this.locusUrl}/controls`,
141
+ body: {
142
+ interpretation: {
143
+ interpreters,
144
+ },
145
+ },
146
+ }).catch((error) => {
147
+ LoggerProxy.logger.error('Meeting:interpretation#updateInterpreters failed', error);
148
+ throw error;
149
+ });
150
+ },
151
+ /**
152
+ * Change direction of interpretation for an interpreter participant
153
+ * @returns {Promise}
154
+ */
155
+ changeDirection() {
156
+ if (!this.sourceLanguage || !this.targetLanguage) {
157
+ return Promise.reject(new Error('Missing sourceLanguage or targetLanguage'));
158
+ }
159
+
160
+ if (!this.selfParticipantId) {
161
+ return Promise.reject(new Error('Missing self participant id'));
162
+ }
163
+
164
+ return this.request({
165
+ method: HTTP_VERBS.PATCH,
166
+ uri: `${this.locusUrl}/participant/${this.selfParticipantId}/controls`,
167
+ body: {
168
+ interpretation: {
169
+ sourceLanguage: this.targetLanguage,
170
+ targetLanguage: this.sourceLanguage,
171
+ isActive: this.isActive,
172
+ order: this.order,
173
+ },
174
+ },
175
+ }).catch((error) => {
176
+ LoggerProxy.logger.error('Meeting:interpretation#changeDirection failed', error);
177
+ throw error;
178
+ });
179
+ },
180
+ });
181
+
182
+ export default SimultaneousInterpretation;
@@ -0,0 +1,18 @@
1
+ /*!
2
+ * Copyright (c) 2015-2023 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ import {WebexPlugin} from '@webex/webex-core';
6
+ import {MEETINGS} from '../constants';
7
+
8
+ const SILanguage = WebexPlugin.extend({
9
+ idAttribute: 'languageName',
10
+
11
+ namespace: MEETINGS,
12
+ props: {
13
+ languageCode: 'number',
14
+ languageName: 'string',
15
+ },
16
+ });
17
+
18
+ export default SILanguage;
@@ -151,6 +151,8 @@ ControlsUtils.getControls = (oldControls: any, newControls: any) => {
151
151
 
152
152
  hasBreakoutChanged: !isEqual(previous?.breakout, current?.breakout),
153
153
 
154
+ hasInterpretationChanged: !isEqual(previous?.interpretation, current?.interpretation),
155
+
154
156
  hasVideoEnabledChanged:
155
157
  newControls.video?.enabled !== undefined &&
156
158
  !isEqual(previous?.videoEnabled, current?.videoEnabled),
@@ -708,6 +708,7 @@ export default class LocusInfo extends EventsScope {
708
708
  hasViewTheParticipantListChanged,
709
709
  hasRaiseHandChanged,
710
710
  hasVideoChanged,
711
+ hasInterpretationChanged,
711
712
  },
712
713
  current,
713
714
  } = ControlsUtils.getControls(this.controls, controls);
@@ -845,6 +846,20 @@ export default class LocusInfo extends EventsScope {
845
846
  );
846
847
  }
847
848
 
849
+ if (hasInterpretationChanged) {
850
+ const {interpretation} = current;
851
+ this.emitScoped(
852
+ {
853
+ file: 'locus-info',
854
+ function: 'updateControls',
855
+ },
856
+ LOCUSINFO.EVENTS.CONTROLS_MEETING_INTERPRETATION_UPDATED,
857
+ {
858
+ interpretation,
859
+ }
860
+ );
861
+ }
862
+
848
863
  if (hasEntryExitToneChanged) {
849
864
  const {entryExitTone} = current;
850
865
 
@@ -1205,6 +1220,20 @@ export default class LocusInfo extends EventsScope {
1205
1220
  );
1206
1221
  }
1207
1222
 
1223
+ if (parsedSelves.updates.interpretationChanged) {
1224
+ this.emitScoped(
1225
+ {
1226
+ file: 'locus-info',
1227
+ function: 'updateSelf',
1228
+ },
1229
+ LOCUSINFO.EVENTS.SELF_MEETING_INTERPRETATION_CHANGED,
1230
+ {
1231
+ interpretation: parsedSelves.current.interpretation,
1232
+ selfParticipantId: parsedSelves.current.selfId,
1233
+ }
1234
+ );
1235
+ }
1236
+
1208
1237
  if (parsedSelves.updates.isMediaInactiveOrReleased) {
1209
1238
  this.emitScoped(
1210
1239
  {
@@ -65,6 +65,7 @@ SelfUtils.parse = (self: any, deviceId: string) => {
65
65
  isSharingBlocked: SelfUtils.isSharingBlocked(self),
66
66
  breakoutSessions: SelfUtils.getBreakoutSessions(self),
67
67
  breakout: SelfUtils.getBreakout(self),
68
+ interpretation: SelfUtils.getInterpretation(self),
68
69
  };
69
70
  }
70
71
 
@@ -73,6 +74,7 @@ SelfUtils.parse = (self: any, deviceId: string) => {
73
74
 
74
75
  SelfUtils.getBreakoutSessions = (self) => self?.controls?.breakout?.sessions;
75
76
  SelfUtils.getBreakout = (self) => self?.controls?.breakout;
77
+ SelfUtils.getInterpretation = (self) => self?.controls?.interpretation;
76
78
 
77
79
  SelfUtils.getLayout = (self) =>
78
80
  Array.isArray(self?.controls?.layouts) ? self.controls.layouts[0].type : undefined;
@@ -125,6 +127,7 @@ SelfUtils.getSelves = (oldSelf, newSelf, deviceId) => {
125
127
  previous?.canNotViewTheParticipantList !== current.canNotViewTheParticipantList;
126
128
  updates.isSharingBlockedChanged = previous?.isSharingBlocked !== current.isSharingBlocked;
127
129
  updates.breakoutsChanged = SelfUtils.breakoutsChanged(previous, current);
130
+ updates.interpretationChanged = SelfUtils.interpretationChanged(previous, current);
128
131
 
129
132
  return {
130
133
  previous,
@@ -153,6 +156,9 @@ SelfUtils.layoutChanged = (previous: any, current: any) =>
153
156
  SelfUtils.breakoutsChanged = (previous, current) =>
154
157
  !isEqual(previous?.breakoutSessions, current?.breakoutSessions) && !!current?.breakout;
155
158
 
159
+ SelfUtils.interpretationChanged = (previous, current) =>
160
+ !isEqual(previous?.interpretation, current?.interpretation) && !!current?.interpretation;
161
+
156
162
  SelfUtils.isMediaInactive = (previous, current) => {
157
163
  if (
158
164
  previous &&
@@ -88,6 +88,7 @@ import {
88
88
  VIDEO,
89
89
  HTTP_VERBS,
90
90
  SELF_ROLES,
91
+ INTERPRETATION,
91
92
  } from '../constants';
92
93
  import BEHAVIORAL_METRICS from '../metrics/constants';
93
94
  import ParameterError from '../common/errors/parameter';
@@ -112,6 +113,7 @@ import {
112
113
  RelayEvent,
113
114
  } from '../reactions/reactions.type';
114
115
  import Breakouts from '../breakouts';
116
+ import SimultaneousInterpretation from '../interpretation';
115
117
  import Annotation from '../annotation';
116
118
 
117
119
  import InMeetingActions from './in-meeting-actions';
@@ -437,6 +439,7 @@ export default class Meeting extends StatelessWebexPlugin {
437
439
  attrs: any;
438
440
  audio: any;
439
441
  breakouts: any;
442
+ simultaneousInterpretation: any;
440
443
  annotation: any;
441
444
  conversationUrl: string;
442
445
  correlationId: string;
@@ -626,6 +629,14 @@ export default class Meeting extends StatelessWebexPlugin {
626
629
  */
627
630
  // @ts-ignore
628
631
  this.breakouts = new Breakouts({meetingId: this.id}, {parent: this.webex});
632
+ /**
633
+ * @instance
634
+ * @type {SimultaneousInterpretation}
635
+ * @public
636
+ * @memberof Meeting
637
+ */
638
+ // @ts-ignore
639
+ this.simultaneousInterpretation = new SimultaneousInterpretation({}, {parent: this.webex});
629
640
  /**
630
641
  * @instance
631
642
  * @type {Annotation}
@@ -1468,6 +1479,7 @@ export default class Meeting extends StatelessWebexPlugin {
1468
1479
  this.setUpLocusInfoAssignHostListener();
1469
1480
  this.setUpLocusInfoMediaInactiveListener();
1470
1481
  this.setUpBreakoutsListener();
1482
+ this.setUpInterpretationListener();
1471
1483
  }
1472
1484
 
1473
1485
  /**
@@ -1559,6 +1571,25 @@ export default class Meeting extends StatelessWebexPlugin {
1559
1571
  });
1560
1572
  }
1561
1573
 
1574
+ /**
1575
+ * Set up the listeners for interpretation
1576
+ * @returns {undefined}
1577
+ * @private
1578
+ * @memberof Meeting
1579
+ */
1580
+ private setUpInterpretationListener() {
1581
+ this.simultaneousInterpretation.on(INTERPRETATION.EVENTS.SUPPORT_LANGUAGES_UPDATE, () => {
1582
+ Trigger.trigger(
1583
+ this,
1584
+ {
1585
+ file: 'meeting/index',
1586
+ function: 'setUpInterpretationListener',
1587
+ },
1588
+ EVENT_TRIGGERS.MEETING_INTERPRETATION_SUPPORT_LANGUAGES_UPDATE
1589
+ );
1590
+ });
1591
+ }
1592
+
1562
1593
  /**
1563
1594
  * Set up the locus info listener for meetings disconnected due to inactivity
1564
1595
  * @returns {undefined}
@@ -2131,6 +2162,21 @@ export default class Meeting extends StatelessWebexPlugin {
2131
2162
  );
2132
2163
  });
2133
2164
 
2165
+ this.locusInfo.on(
2166
+ LOCUSINFO.EVENTS.CONTROLS_MEETING_INTERPRETATION_UPDATED,
2167
+ ({interpretation}) => {
2168
+ this.simultaneousInterpretation.updateInterpretation(interpretation);
2169
+ Trigger.trigger(
2170
+ this,
2171
+ {
2172
+ file: 'meeting/index',
2173
+ function: 'setupLocusControlsListener',
2174
+ },
2175
+ EVENT_TRIGGERS.MEETING_INTERPRETATION_UPDATE
2176
+ );
2177
+ }
2178
+ );
2179
+
2134
2180
  this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_JOIN_BREAKOUT_FROM_MAIN, ({mainLocusUrl}) => {
2135
2181
  this.meetingRequest.getLocusStatusByUrl(mainLocusUrl).catch((error) => {
2136
2182
  // clear main session cache when attendee join into breakout and forbidden to get locus from main locus url,
@@ -2479,6 +2525,7 @@ export default class Meeting extends StatelessWebexPlugin {
2479
2525
  this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_URL, (payload) => {
2480
2526
  this.members.locusUrlUpdate(payload);
2481
2527
  this.breakouts.locusUrlUpdate(payload);
2528
+ this.simultaneousInterpretation.locusUrlUpdate(payload);
2482
2529
  this.annotation.locusUrlUpdate(payload);
2483
2530
  this.locusUrl = payload;
2484
2531
  this.locusId = this.locusUrl?.split('/').pop();
@@ -2944,12 +2991,26 @@ export default class Meeting extends StatelessWebexPlugin {
2944
2991
  );
2945
2992
  });
2946
2993
 
2994
+ this.locusInfo.on(LOCUSINFO.EVENTS.SELF_MEETING_INTERPRETATION_CHANGED, (payload) => {
2995
+ this.simultaneousInterpretation.updateSelfInterpretation(payload);
2996
+ Trigger.trigger(
2997
+ this,
2998
+ {
2999
+ file: 'meeting/index',
3000
+ function: 'setUpLocusInfoSelfListener',
3001
+ },
3002
+ EVENT_TRIGGERS.MEETING_INTERPRETATION_UPDATE
3003
+ );
3004
+ });
3005
+
2947
3006
  this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ROLES_CHANGED, (payload) => {
2948
3007
  const isModeratorOrCohost =
2949
3008
  payload.newRoles?.includes(SELF_ROLES.MODERATOR) ||
2950
3009
  payload.newRoles?.includes(SELF_ROLES.COHOST);
2951
3010
  this.breakouts.updateCanManageBreakouts(isModeratorOrCohost);
2952
-
3011
+ this.simultaneousInterpretation.updateCanManageInterpreters(
3012
+ payload.newRoles?.includes(SELF_ROLES.MODERATOR)
3013
+ );
2953
3014
  Trigger.trigger(
2954
3015
  this,
2955
3016
  {
@@ -129,6 +129,7 @@ const MeetingUtil = {
129
129
 
130
130
  cleanUp: (meeting) => {
131
131
  meeting.breakouts.cleanUp();
132
+ meeting.simultaneousInterpretation.cleanUp();
132
133
 
133
134
  // make sure we send last metrics before we close the peerconnection
134
135
  const stopStatsAnalyzer = meeting.statsAnalyzer
@@ -34,6 +34,7 @@ export default class Member {
34
34
  participant: any;
35
35
  status: any;
36
36
  supportsBreakouts: boolean;
37
+ supportsInterpretation: boolean;
37
38
  supportLiveAnnotation: boolean;
38
39
  type: any;
39
40
  namespace = MEETINGS;
@@ -269,6 +270,7 @@ export default class Member {
269
270
  this.isVideoMuted = MemberUtil.isVideoMuted(participant);
270
271
  this.isHandRaised = MemberUtil.isHandRaised(participant);
271
272
  this.supportsBreakouts = MemberUtil.isBreakoutsSupported(participant);
273
+ this.supportsInterpretation = MemberUtil.isInterpretationSupported(participant);
272
274
  this.supportLiveAnnotation = MemberUtil.isLiveAnnotationSupported(participant);
273
275
  this.isGuest = MemberUtil.isGuest(participant);
274
276
  this.isUser = MemberUtil.isUser(participant);
@@ -196,6 +196,20 @@ MemberUtil.isBreakoutsSupported = (participant) => {
196
196
  return !participant.doesNotSupportBreakouts;
197
197
  };
198
198
 
199
+ /**
200
+ * @param {Object} participant the locus participant
201
+ * @returns {Boolean}
202
+ */
203
+ MemberUtil.isInterpretationSupported = (participant) => {
204
+ if (!participant) {
205
+ throw new ParameterError(
206
+ 'Interpretation support could not be processed, participant is undefined.'
207
+ );
208
+ }
209
+
210
+ return !participant.doesNotSupportSiInterpreter;
211
+ };
212
+
199
213
  /**
200
214
  * @param {Object} participant the locus participant
201
215
  * @returns {Boolean}
@@ -0,0 +1,15 @@
1
+ import {assert} from '@webex/test-helper-chai';
2
+ import SILanguage from '@webex/plugin-meetings/src/interpretation/siLanguage';
3
+ import SILanguageCollection from '@webex/plugin-meetings/src/interpretation/collection';
4
+
5
+ describe('plugin-meetings', () => {
6
+ describe('SILanguageCollection', () => {
7
+ it('the siLanguages collection is as expected', () => {
8
+ const collection = new SILanguageCollection();
9
+
10
+ assert.equal(collection.model, SILanguage);
11
+ assert.equal(collection.namespace, 'Meetings');
12
+ assert.equal(collection.mainIndex, 'languageName');
13
+ });
14
+ });
15
+ });