@webex/plugin-meetings 3.0.0-beta.174 → 3.0.0-beta.176

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.
@@ -33,7 +33,6 @@ interface IAnnotationChannel {
33
33
  acceptRequest: (approval: any) => undefined | Promise<void>;
34
34
  declineRequest: (approval: any) => undefined | Promise<void>;
35
35
  closeAnnotation: (requestData: RequestData) => undefined | Promise<void>;
36
- changeAnnotationOptions: (remoteShareUrl: any, annotationInfo: any) => undefined | Promise<void>;
37
36
  approveAnnotation: (requestData: RequestData) => undefined | Promise<void>;
38
37
  cancelApproveAnnotation: (requestData: RequestData, approval: any) => undefined | Promise<void>;
39
38
  sendStrokeData: (strokeData: StrokeData) => void;
@@ -106,13 +106,6 @@ declare class AnnotationChannel extends WebexPlugin implements IAnnotationChanne
106
106
  * @returns {void}
107
107
  */
108
108
  sendStrokeData: (strokeData: StrokeData) => void;
109
- /**
110
- * Change annotation options
111
- * @param {remoteShareUrl} remoteShareUrl
112
- * @param {annotationInfo} annotationInfo
113
- * @returns {Promise}
114
- */
115
- changeAnnotationOptions(remoteShareUrl: any, annotationInfo: any): any;
116
109
  /**
117
110
  * private encrypted the strokes data
118
111
  * @param {string} encryptedContent
@@ -218,6 +218,7 @@ export declare const EVENT_TRIGGERS: {
218
218
  MEETING_BREAKOUTS_PRE_ASSIGNMENTS_UPDATE: string;
219
219
  MEETING_INTERPRETATION_UPDATE: string;
220
220
  MEETING_INTERPRETATION_SUPPORT_LANGUAGES_UPDATE: string;
221
+ MEETING_INTERPRETATION_HANDOFF_REQUESTS_ARRIVED: string;
221
222
  MEMBERS_UPDATE: string;
222
223
  MEMBERS_CLEAR: string;
223
224
  MEMBERS_CONTENT_UPDATE: string;
@@ -463,7 +464,15 @@ export declare const BREAKOUTS: {
463
464
  export declare const INTERPRETATION: {
464
465
  EVENTS: {
465
466
  SUPPORT_LANGUAGES_UPDATE: string;
467
+ HANDOFF_REQUESTS_ARRIVED: string;
466
468
  };
469
+ ACTION_TYPE: {
470
+ OFFERED: string;
471
+ ACCEPTED: string;
472
+ REQUESTED: string;
473
+ DECLINED: string;
474
+ };
475
+ RESOURCE_TYPE: string;
467
476
  };
468
477
  export declare const LOCUSINFO: {
469
478
  EVENTS: {
@@ -158,7 +158,11 @@ export declare enum ScreenShareFloorStatus {
158
158
  * @instance
159
159
  * @type {Object}
160
160
  * @property {Boolean} memberId id of the meeting member that started screen share
161
+ * @property {String} url of this content share
162
+ * @property {String} shareInstanceId of this content share
163
+ * @property {Object} annotation Info of this content share
161
164
  * @memberof Meeting
165
+ *
162
166
  */
163
167
  /**
164
168
  * Meeting Stopped Sharing Remote Event
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webex/plugin-meetings",
3
- "version": "3.0.0-beta.174",
3
+ "version": "3.0.0-beta.176",
4
4
  "description": "",
5
5
  "license": "Cisco EULA (https://www.cisco.com/c/en/us/products/end-user-license-agreement.html)",
6
6
  "contributors": [
@@ -32,12 +32,12 @@
32
32
  "build": "yarn run -T tsc --declaration true --declarationDir ./dist/types"
33
33
  },
34
34
  "devDependencies": {
35
- "@webex/plugin-meetings": "3.0.0-beta.174",
36
- "@webex/test-helper-chai": "3.0.0-beta.174",
37
- "@webex/test-helper-mocha": "3.0.0-beta.174",
38
- "@webex/test-helper-mock-webex": "3.0.0-beta.174",
39
- "@webex/test-helper-retry": "3.0.0-beta.174",
40
- "@webex/test-helper-test-users": "3.0.0-beta.174",
35
+ "@webex/plugin-meetings": "3.0.0-beta.176",
36
+ "@webex/test-helper-chai": "3.0.0-beta.176",
37
+ "@webex/test-helper-mocha": "3.0.0-beta.176",
38
+ "@webex/test-helper-mock-webex": "3.0.0-beta.176",
39
+ "@webex/test-helper-retry": "3.0.0-beta.176",
40
+ "@webex/test-helper-test-users": "3.0.0-beta.176",
41
41
  "chai": "^4.3.4",
42
42
  "chai-as-promised": "^7.1.1",
43
43
  "jsdom-global": "3.0.2",
@@ -46,19 +46,19 @@
46
46
  "typescript": "^4.7.4"
47
47
  },
48
48
  "dependencies": {
49
- "@webex/common": "3.0.0-beta.174",
49
+ "@webex/common": "3.0.0-beta.176",
50
50
  "@webex/internal-media-core": "1.39.1",
51
- "@webex/internal-plugin-conversation": "3.0.0-beta.174",
52
- "@webex/internal-plugin-device": "3.0.0-beta.174",
53
- "@webex/internal-plugin-llm": "3.0.0-beta.174",
54
- "@webex/internal-plugin-mercury": "3.0.0-beta.174",
55
- "@webex/internal-plugin-metrics": "3.0.0-beta.174",
56
- "@webex/internal-plugin-support": "3.0.0-beta.174",
57
- "@webex/internal-plugin-user": "3.0.0-beta.174",
58
- "@webex/media-helpers": "3.0.0-beta.174",
59
- "@webex/plugin-people": "3.0.0-beta.174",
60
- "@webex/plugin-rooms": "3.0.0-beta.174",
61
- "@webex/webex-core": "3.0.0-beta.174",
51
+ "@webex/internal-plugin-conversation": "3.0.0-beta.176",
52
+ "@webex/internal-plugin-device": "3.0.0-beta.176",
53
+ "@webex/internal-plugin-llm": "3.0.0-beta.176",
54
+ "@webex/internal-plugin-mercury": "3.0.0-beta.176",
55
+ "@webex/internal-plugin-metrics": "3.0.0-beta.176",
56
+ "@webex/internal-plugin-support": "3.0.0-beta.176",
57
+ "@webex/internal-plugin-user": "3.0.0-beta.176",
58
+ "@webex/media-helpers": "3.0.0-beta.176",
59
+ "@webex/plugin-people": "3.0.0-beta.176",
60
+ "@webex/plugin-rooms": "3.0.0-beta.176",
61
+ "@webex/webex-core": "3.0.0-beta.176",
62
62
  "ampersand-collection": "^2.0.2",
63
63
  "bowser": "^2.11.0",
64
64
  "btoa": "^1.2.1",
@@ -38,8 +38,6 @@ interface IAnnotationChannel {
38
38
  acceptRequest: (approval) => undefined | Promise<void>;
39
39
  declineRequest: (approval) => undefined | Promise<void>;
40
40
  closeAnnotation: (requestData: RequestData) => undefined | Promise<void>;
41
- // change annotation privilege
42
- changeAnnotationOptions: (remoteShareUrl, annotationInfo) => undefined | Promise<void>;
43
41
  // === below is for attendee
44
42
  approveAnnotation: (requestData: RequestData) => undefined | Promise<void>;
45
43
  cancelApproveAnnotation: (requestData: RequestData, approval) => undefined | Promise<void>;
@@ -280,21 +280,6 @@ class AnnotationChannel extends WebexPlugin implements IAnnotationChannel {
280
280
  );
281
281
  };
282
282
 
283
- /**
284
- * Change annotation options
285
- * @param {remoteShareUrl} remoteShareUrl
286
- * @param {annotationInfo} annotationInfo
287
- * @returns {Promise}
288
- */
289
- public changeAnnotationOptions(remoteShareUrl, annotationInfo) {
290
- // @ts-ignore
291
- return this.request({
292
- method: HTTP_VERBS.PATCH,
293
- url: remoteShareUrl,
294
- body: annotationInfo,
295
- });
296
- }
297
-
298
283
  /**
299
284
  * private encrypted the strokes data
300
285
  * @param {string} encryptedContent
package/src/constants.ts CHANGED
@@ -319,6 +319,7 @@ export const EVENT_TRIGGERS = {
319
319
  MEETING_BREAKOUTS_PRE_ASSIGNMENTS_UPDATE: 'meeting:breakouts:preAssignmentsUpdate',
320
320
  MEETING_INTERPRETATION_UPDATE: 'meeting:interpretation:update',
321
321
  MEETING_INTERPRETATION_SUPPORT_LANGUAGES_UPDATE: 'meeting:interpretation:supportLanguagesUpdate',
322
+ MEETING_INTERPRETATION_HANDOFF_REQUESTS_ARRIVED: 'meeting:interpretation:handoffRequestsArrived',
322
323
  MEMBERS_UPDATE: 'members:update',
323
324
  MEMBERS_CLEAR: 'members:clear',
324
325
  MEMBERS_CONTENT_UPDATE: 'members:content:update',
@@ -597,7 +598,15 @@ export const BREAKOUTS = {
597
598
  export const INTERPRETATION = {
598
599
  EVENTS: {
599
600
  SUPPORT_LANGUAGES_UPDATE: 'SUPPORT_LANGUAGES_UPDATE',
601
+ HANDOFF_REQUESTS_ARRIVED: 'HANDOFF_REQUESTS_ARRIVED',
600
602
  },
603
+ ACTION_TYPE: {
604
+ OFFERED: 'OFFERED',
605
+ ACCEPTED: 'ACCEPTED',
606
+ REQUESTED: 'REQUESTED',
607
+ DECLINED: 'DECLINED',
608
+ },
609
+ RESOURCE_TYPE: 'SiHandover',
601
610
  };
602
611
 
603
612
  export const LOCUSINFO = {
@@ -45,7 +45,16 @@ The following are methods available to the interpreters of a meeting.
45
45
  //Change direction of interpretation for an interpreter participant
46
46
  interpretation.changeDirection();
47
47
 
48
- //Handoff between interpreters, will implement them later
49
- interpretation.handoff(participantId)
48
+ //Handoff between interpreters, input paramerter participantId is the target to handoff
49
+ interpretation.handoffInterpreter(participantId);
50
+
51
+ //in-active interpreter request to handoff to self
52
+ interpretation.requestHandoff();
53
+
54
+ //accept the request of handoff, input paramter url is from last approval event which generate by server side
55
+ interpretation.acceptRequest(url);
56
+
57
+ //decline the request of handoff, input paramter url is from last approval event which generate by server side
58
+ interpretation.declineRequest(url);
50
59
 
51
60
  ```
@@ -18,6 +18,7 @@ const SimultaneousInterpretation = WebexPlugin.extend({
18
18
 
19
19
  props: {
20
20
  locusUrl: 'string', // appears current meeting's locus url
21
+ approvalUrl: 'string', // appears current meeting's approval url for handoff between interpreters
21
22
  originalLanguage: 'string', // appears current meeting's original language
22
23
  sourceLanguage: 'string', // appears self interpreter's source language
23
24
  targetLanguage: 'string', // appears self interpreter's target language
@@ -27,18 +28,18 @@ const SimultaneousInterpretation = WebexPlugin.extend({
27
28
  selfParticipantId: 'string', // appears the self participant id
28
29
  canManageInterpreters: 'boolean', // appears the ability to manage interpreters
29
30
  supportLanguages: 'array', // appears the support languages
30
- siEnabled: 'boolean', // appears the meeting enabled SI
31
+ hostSIEnabled: 'boolean', // appears the meeting host feature of SI enabled
31
32
  },
32
33
  derived: {
33
34
  shouldQuerySupportLanguages: {
34
35
  cache: false,
35
- deps: ['canManageInterpreters', 'siEnabled'],
36
+ deps: ['canManageInterpreters', 'hostSIEnabled'],
36
37
  /**
37
38
  * Returns should query support languages or not
38
39
  * @returns {boolean}
39
40
  */
40
41
  fn() {
41
- return !!(this.canManageInterpreters && this.siEnabled);
42
+ return !!(this.canManageInterpreters && this.hostSIEnabled);
42
43
  },
43
44
  },
44
45
  },
@@ -52,6 +53,7 @@ const SimultaneousInterpretation = WebexPlugin.extend({
52
53
  this.querySupportLanguages();
53
54
  }
54
55
  });
56
+ this.listenToHandoffRequests();
55
57
  },
56
58
 
57
59
  /**
@@ -69,6 +71,14 @@ const SimultaneousInterpretation = WebexPlugin.extend({
69
71
  locusUrlUpdate(locusUrl) {
70
72
  this.set('locusUrl', locusUrl);
71
73
  },
74
+ /**
75
+ * Update the approval url for handoff
76
+ * @param {string} approvalUrl // approval url
77
+ * @returns {void}
78
+ */
79
+ approvalUrlUpdate(approvalUrl) {
80
+ this.set('approvalUrl', approvalUrl);
81
+ },
72
82
  /**
73
83
  * Update whether self has capability to manage interpreters (only host can manage it)
74
84
  * @param {boolean} canManageInterpreters
@@ -77,13 +87,20 @@ const SimultaneousInterpretation = WebexPlugin.extend({
77
87
  updateCanManageInterpreters(canManageInterpreters) {
78
88
  this.set('canManageInterpreters', canManageInterpreters);
79
89
  },
90
+ /**
91
+ * Update whether the meeting's host si is enabled or not
92
+ * @param {boolean} hostSIEnabled
93
+ * @returns {void}
94
+ */
95
+ updateHostSIEnabled(hostSIEnabled) {
96
+ this.set('hostSIEnabled', hostSIEnabled);
97
+ },
80
98
  /**
81
99
  * Update the interpretation languages channels which user can choose to subscribe
82
100
  * @param {Object} interpretation
83
101
  * @returns {void}
84
102
  */
85
103
  updateInterpretation(interpretation) {
86
- this.set('siEnabled', !!interpretation);
87
104
  this.siLanguages.set(interpretation?.siLanguages || []);
88
105
  },
89
106
  /**
@@ -177,6 +194,125 @@ const SimultaneousInterpretation = WebexPlugin.extend({
177
194
  throw error;
178
195
  });
179
196
  },
197
+ /**
198
+ * Sets up a listener for handoff requests from mercury
199
+ * @returns {void}
200
+ */
201
+ listenToHandoffRequests() {
202
+ this.listenTo(this.webex.internal.mercury, 'event:locus.approval_request', (event) => {
203
+ if (event?.data?.approval?.resourceType === INTERPRETATION.RESOURCE_TYPE) {
204
+ const {receivers, initiator, actionType, url} = event.data.approval;
205
+ const receiverId = receivers?.[0]?.participantId;
206
+ const isReceiver = !!receiverId && receiverId === this.selfParticipantId;
207
+ const senderId = initiator?.participantId;
208
+ const isSender = !!senderId && senderId === this.selfParticipantId;
209
+ if (!isReceiver && !isSender) {
210
+ return;
211
+ }
212
+ this.trigger(INTERPRETATION.EVENTS.HANDOFF_REQUESTS_ARRIVED, {
213
+ actionType,
214
+ isReceiver,
215
+ isSender,
216
+ senderId,
217
+ receiverId,
218
+ url,
219
+ });
220
+ }
221
+ });
222
+ },
223
+ /**
224
+ * handoff the active interpreter role to another interpreter in same group, only the interpreter is allowed to call this api
225
+ * @param {string} participantId the participant id you want to hand off
226
+ * @returns {Promise}
227
+ */
228
+ handoffInterpreter(participantId) {
229
+ if (!participantId) {
230
+ return Promise.reject(new Error('Missing target participant id'));
231
+ }
232
+ if (!this.approvalUrl) {
233
+ return Promise.reject(new Error('Missing approval url'));
234
+ }
235
+
236
+ return this.request({
237
+ method: HTTP_VERBS.POST,
238
+ uri: this.approvalUrl,
239
+ body: {
240
+ actionType: INTERPRETATION.ACTION_TYPE.OFFERED,
241
+ resourceType: INTERPRETATION.RESOURCE_TYPE,
242
+ receivers: [
243
+ {
244
+ participantId,
245
+ },
246
+ ],
247
+ },
248
+ }).catch((error) => {
249
+ LoggerProxy.logger.error('Meeting:interpretation#handoffInterpreter failed', error);
250
+ throw error;
251
+ });
252
+ },
253
+ /**
254
+ * the in-active interpreter request to hand off the active role to self
255
+ * @returns {Promise}
256
+ */
257
+ requestHandoff() {
258
+ if (!this.approvalUrl) {
259
+ return Promise.reject(new Error('Missing approval url'));
260
+ }
261
+
262
+ return this.request({
263
+ method: HTTP_VERBS.POST,
264
+ uri: this.approvalUrl,
265
+ body: {
266
+ actionType: INTERPRETATION.ACTION_TYPE.REQUESTED,
267
+ resourceType: INTERPRETATION.RESOURCE_TYPE,
268
+ },
269
+ }).catch((error) => {
270
+ LoggerProxy.logger.error('Meeting:interpretation#requestHandoff failed', error);
271
+ throw error;
272
+ });
273
+ },
274
+ /**
275
+ * accept the request of handoff
276
+ * @param {String} url the url get from last approval event
277
+ * @returns {Promise}
278
+ */
279
+ acceptRequest(url) {
280
+ if (!url) {
281
+ return Promise.reject(new Error('Missing the url to accept'));
282
+ }
283
+
284
+ return this.request({
285
+ method: HTTP_VERBS.PUT,
286
+ uri: url,
287
+ body: {
288
+ actionType: INTERPRETATION.ACTION_TYPE.ACCEPTED,
289
+ },
290
+ }).catch((error) => {
291
+ LoggerProxy.logger.error('Meeting:interpretation#acceptRequest failed', error);
292
+ throw error;
293
+ });
294
+ },
295
+ /**
296
+ * decline the request of handoff
297
+ * @param {String} url the url get from last approval event
298
+ * @returns {Promise}
299
+ */
300
+ declineRequest(url) {
301
+ if (!url) {
302
+ return Promise.reject(new Error('Missing the url to decline'));
303
+ }
304
+
305
+ return this.request({
306
+ method: HTTP_VERBS.PUT,
307
+ uri: url,
308
+ body: {
309
+ actionType: INTERPRETATION.ACTION_TYPE.DECLINED,
310
+ },
311
+ }).catch((error) => {
312
+ LoggerProxy.logger.error('Meeting:interpretation#declineRequest failed', error);
313
+ throw error;
314
+ });
315
+ },
180
316
  });
181
317
 
182
318
  export default SimultaneousInterpretation;
@@ -295,7 +295,11 @@ export enum ScreenShareFloorStatus {
295
295
  * @instance
296
296
  * @type {Object}
297
297
  * @property {Boolean} memberId id of the meeting member that started screen share
298
+ * @property {String} url of this content share
299
+ * @property {String} shareInstanceId of this content share
300
+ * @property {Object} annotation Info of this content share
298
301
  * @memberof Meeting
302
+ *
299
303
  */
300
304
 
301
305
  /**
@@ -1608,6 +1612,21 @@ export default class Meeting extends StatelessWebexPlugin {
1608
1612
  EVENT_TRIGGERS.MEETING_INTERPRETATION_SUPPORT_LANGUAGES_UPDATE
1609
1613
  );
1610
1614
  });
1615
+
1616
+ this.simultaneousInterpretation.on(
1617
+ INTERPRETATION.EVENTS.HANDOFF_REQUESTS_ARRIVED,
1618
+ (payload) => {
1619
+ Trigger.trigger(
1620
+ this,
1621
+ {
1622
+ file: 'meeting/index',
1623
+ function: 'setUpInterpretationListener',
1624
+ },
1625
+ EVENT_TRIGGERS.MEETING_INTERPRETATION_HANDOFF_REQUESTS_ARRIVED,
1626
+ payload
1627
+ );
1628
+ }
1629
+ );
1611
1630
  }
1612
1631
 
1613
1632
  /**
@@ -2218,6 +2237,7 @@ export default class Meeting extends StatelessWebexPlugin {
2218
2237
  memberId: contentShare.beneficiaryId,
2219
2238
  url: contentShare.url,
2220
2239
  shareInstanceId: contentShare.shareInstanceId,
2240
+ annotationInfo: contentShare.annotation,
2221
2241
  }
2222
2242
  );
2223
2243
  };
@@ -2306,6 +2326,7 @@ export default class Meeting extends StatelessWebexPlugin {
2306
2326
  memberId: contentShare.beneficiaryId,
2307
2327
  url: contentShare.url,
2308
2328
  shareInstanceId: contentShare.shareInstanceId,
2329
+ annotationInfo: contentShare.annotation,
2309
2330
  }
2310
2331
  );
2311
2332
  this.members.locusMediaSharesUpdate(payload);
@@ -2374,6 +2395,7 @@ export default class Meeting extends StatelessWebexPlugin {
2374
2395
  this.recordingController.setSessionId(this.locusInfo?.fullState?.sessionId);
2375
2396
  this.breakouts.breakoutServiceUrlUpdate(payload?.services?.breakout?.url);
2376
2397
  this.annotation.approvalUrlUpdate(payload?.services?.approval?.url);
2398
+ this.simultaneousInterpretation.approvalUrlUpdate(payload?.services?.approval?.url);
2377
2399
  });
2378
2400
  }
2379
2401
 
@@ -3157,6 +3179,9 @@ export default class Meeting extends StatelessWebexPlugin {
3157
3179
  // Need to populate environment when sending CA event
3158
3180
  this.environment = locusMeetingObject?.info.channel || webexMeetingInfo?.channel;
3159
3181
  }
3182
+ this.simultaneousInterpretation.updateHostSIEnabled(
3183
+ !!webexMeetingInfo?.meetingSiteSetting?.enableHostInterpreterControlSI
3184
+ );
3160
3185
  }
3161
3186
 
3162
3187
  /**
@@ -343,24 +343,6 @@ describe('live-annotation', () => {
343
343
  });
344
344
  });
345
345
 
346
- describe('change annotation info by presenter', () => {
347
- it('makes change annotation options as expected', async() => {
348
- const options = { annotationInfo:{
349
- version: '1',
350
- policy: 'AnnotationNotAllowed',
351
- }};
352
-
353
- const remoteShareUrl = 'remoteShareUrl';
354
- const result = await annotationService.changeAnnotationOptions(remoteShareUrl,options);
355
- assert.calledOnceWithExactly(webex.request, {
356
- method: 'PATCH',
357
- url: 'remoteShareUrl',
358
- body: {annotationInfo: { version: '1', policy: 'AnnotationNotAllowed' }}
359
- });
360
- assert.equal(result, 'REQUEST_RETURN_VALUE')
361
- });
362
- });
363
-
364
346
 
365
347
 
366
348
  describe('close annotation', () => {