@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.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +12 -2
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/collection.js +23 -0
- package/dist/interpretation/collection.js.map +1 -0
- package/dist/interpretation/index.js +214 -0
- package/dist/interpretation/index.js.map +1 -0
- package/dist/interpretation/siLanguage.js +25 -0
- package/dist/interpretation/siLanguage.js.map +1 -0
- package/dist/locus-info/controlsUtils.js +1 -0
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +19 -0
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +20 -11
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/meeting/index.js +402 -354
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/member/index.js +2 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +11 -0
- package/dist/member/util.js.map +1 -1
- package/dist/types/constants.d.ts +9 -0
- package/dist/types/interpretation/collection.d.ts +5 -0
- package/dist/types/interpretation/index.d.ts +5 -0
- package/dist/types/interpretation/siLanguage.d.ts +5 -0
- package/dist/types/meeting/index.d.ts +8 -0
- package/dist/types/member/index.d.ts +1 -0
- package/package.json +19 -19
- package/src/constants.ts +10 -0
- package/src/interpretation/README.md +51 -0
- package/src/interpretation/collection.ts +19 -0
- package/src/interpretation/index.ts +182 -0
- package/src/interpretation/siLanguage.ts +18 -0
- package/src/locus-info/controlsUtils.ts +2 -0
- package/src/locus-info/index.ts +29 -0
- package/src/locus-info/selfUtils.ts +6 -0
- package/src/meeting/index.ts +62 -1
- package/src/meeting/util.ts +1 -0
- package/src/member/index.ts +2 -0
- package/src/member/util.ts +14 -0
- package/test/unit/spec/interpretation/collection.ts +15 -0
- package/test/unit/spec/interpretation/index.ts +329 -0
- package/test/unit/spec/interpretation/siLanguage.ts +26 -0
- package/test/unit/spec/locus-info/controlsUtils.js +20 -0
- package/test/unit/spec/locus-info/index.js +64 -0
- package/test/unit/spec/locus-info/selfConstant.js +10 -0
- package/test/unit/spec/locus-info/selfUtils.js +26 -0
- package/test/unit/spec/meeting/index.js +62 -1
- package/test/unit/spec/meeting/utils.js +2 -0
- package/test/unit/spec/member/index.js +11 -4
- 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),
|
package/src/locus-info/index.ts
CHANGED
|
@@ -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 &&
|
package/src/meeting/index.ts
CHANGED
|
@@ -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
|
{
|
package/src/meeting/util.ts
CHANGED
|
@@ -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
|
package/src/member/index.ts
CHANGED
|
@@ -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);
|
package/src/member/util.ts
CHANGED
|
@@ -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
|
+
});
|