@webex/plugin-meetings 2.35.4 → 2.36.0

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.
@@ -0,0 +1,104 @@
1
+ import {Reaction, ReactionType, SkinTone, SkinToneType} from './reactions.type';
2
+
3
+ const Reactions: Record<ReactionType, Reaction> = {
4
+ smile: {
5
+ type: 'smile',
6
+ codepoints: '1F642',
7
+ shortcodes: ':slightly_smiling_face:'
8
+ },
9
+ sad: {
10
+ type: 'sad',
11
+ codepoints: '1F625',
12
+ shortcodes: ':sad_but_relieved_face:',
13
+ },
14
+ wow: {
15
+ type: 'wow',
16
+ codepoints: '1F62E',
17
+ shortcodes: ':open_mouth:',
18
+ },
19
+ haha: {
20
+ type: 'haha',
21
+ codepoints: '1F603',
22
+ shortcodes: ':smiley:'
23
+ },
24
+ celebrate: {
25
+ type: 'celebrate',
26
+ codepoints: '1F389',
27
+ shortcodes: ':party_popper:',
28
+ },
29
+ clap: {
30
+ type: 'clap',
31
+ codepoints: '1F44F',
32
+ shortcodes: ':clap:',
33
+ },
34
+ thumbs_up: {
35
+ type: 'thumb_up',
36
+ codepoints: '1F44D',
37
+ shortcodes: ':thumbsup:',
38
+ },
39
+ thumbs_down: {
40
+ type: 'thumb_down',
41
+ codepoints: '1F44E',
42
+ shortcodes: ':thumbsdown:',
43
+ },
44
+ heart: {
45
+ type: 'heart',
46
+ codepoints: '2764',
47
+ shortcodes: ':heart:',
48
+ },
49
+ fire: {
50
+ type: 'fire',
51
+ codepoints: '1F525',
52
+ shortcodes: ':fire:',
53
+ },
54
+ prayer: {
55
+ type: 'prayer',
56
+ codepoints: '1F64F',
57
+ shortcodes: ':pray:',
58
+ },
59
+ speed_up: {
60
+ type: 'speed_up',
61
+ codepoints: '1F407',
62
+ shortcodes: ':rabbit:',
63
+ },
64
+ slow_down: {
65
+ type: 'slow_down',
66
+ codepoints: '1F422',
67
+ shortcodes: ':turtle:',
68
+ }
69
+ };
70
+
71
+ const SkinTones: Record<SkinToneType, SkinTone> = {
72
+ normal: {
73
+ type: 'normal_skin_tone',
74
+ codepoints: '',
75
+ shortcodes: '',
76
+ },
77
+ light: {
78
+ type: 'light_skin_tone',
79
+ codepoints: '1F3FB',
80
+ shortcodes: ':skin-tone-2:',
81
+ },
82
+ medium_light: {
83
+ type: 'medium_light_skin_tone',
84
+ codepoints: '1F3FC',
85
+ shortcodes: ':skin-tone-3:',
86
+ },
87
+ medium: {
88
+ type: 'medium_skin_tone',
89
+ codepoints: '1F3FD',
90
+ shortcodes: ':skin-tone-4:',
91
+ },
92
+ medium_dark: {
93
+ type: 'medium_dark_skin_tone',
94
+ codepoints: '1F3FE',
95
+ shortcodes: ':skin-tone-5:',
96
+ },
97
+ dark: {
98
+ type: 'dark_skin_tone',
99
+ codepoints: '1F3FF',
100
+ shortcodes: ':skin-tone-6:',
101
+ }
102
+ };
103
+
104
+ export {Reactions, SkinTones};
@@ -0,0 +1,36 @@
1
+
2
+ export type EmoticonData = {
3
+ type: string;
4
+ codepoints?: string;
5
+ shortcodes?: string;
6
+ }
7
+
8
+ export type SkinTone = EmoticonData;
9
+ export type Reaction = ReactionData & {
10
+ tone?: SkinTone;
11
+ }
12
+
13
+ export enum ReactionType {
14
+ smile = 'smile',
15
+ sad = 'sad',
16
+ wow = 'wow',
17
+ haha = 'haha',
18
+ celebrate = 'celebrate',
19
+ clap = 'clap',
20
+ thumbs_up = 'thumbs_up',
21
+ thumbs_down = 'thumbs_down',
22
+ heart = 'heart',
23
+ fire = 'fire',
24
+ prayer = 'prayer',
25
+ speed_up = 'speed_up',
26
+ slow_down = 'slow_down',
27
+ }
28
+
29
+ export enum SkinToneType {
30
+ normal = 'normal',
31
+ light = 'light',
32
+ medium_light = 'medium_light',
33
+ medium = 'medium',
34
+ medium_dark = 'medium_dark',
35
+ dark = 'dark',
36
+ }
@@ -1420,9 +1420,9 @@ describe('plugin-meetings', () => {
1420
1420
  });
1421
1421
  });
1422
1422
  });
1423
- describe('#share', () => {
1424
- it('should have #share', () => {
1425
- assert.exists(meeting.share);
1423
+ describe('#requestScreenShareFloor', () => {
1424
+ it('should have #requestScreenShareFloor', () => {
1425
+ assert.exists(meeting.requestScreenShareFloor);
1426
1426
  });
1427
1427
  beforeEach(() => {
1428
1428
  meeting.locusInfo.mediaShares = [{name: 'content', url: url1}];
@@ -1430,7 +1430,7 @@ describe('plugin-meetings', () => {
1430
1430
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
1431
1431
  });
1432
1432
  it('should send the share', async () => {
1433
- const share = meeting.share();
1433
+ const share = meeting.requestScreenShareFloor();
1434
1434
 
1435
1435
  assert.exists(share.then);
1436
1436
  await share;
@@ -3457,22 +3457,34 @@ describe('plugin-meetings', () => {
3457
3457
  sandbox = null;
3458
3458
  });
3459
3459
 
3460
- describe('#stopFloorRequest', () => {
3461
- it('should have #stopFloorRequest', () => {
3462
- assert.exists(meeting.stopFloorRequest);
3460
+ describe('#releaseScreenShareFloor', () => {
3461
+ it('should have #releaseScreenShareFloor', () => {
3462
+ assert.exists(meeting.releaseScreenShareFloor);
3463
3463
  });
3464
3464
  beforeEach(() => {
3465
- meeting.locusInfo.mediaShares = [{name: 'content', url: url1}];
3465
+ meeting.selfId = 'some self id';
3466
+ meeting.locusInfo.mediaShares = [{name: 'content', url: url1, floor: {beneficiary: {id: meeting.selfId}}}];
3466
3467
  meeting.locusInfo.self = {url: url2};
3468
+ meeting.mediaProperties = {mediaDirection: {sendShare: true}};
3467
3469
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
3468
3470
  });
3469
- it('should call change meeting floor', async () => {
3470
- const share = meeting.share();
3471
+ it('should call changeMeetingFloor()', async () => {
3472
+ const share = meeting.releaseScreenShareFloor();
3471
3473
 
3472
3474
  assert.exists(share.then);
3473
3475
  await share;
3474
3476
  assert.calledOnce(meeting.meetingRequest.changeMeetingFloor);
3475
3477
  });
3478
+ it('should not call changeMeetingFloor() if someone else already has the floor', async () => {
3479
+ // change selfId so that it doesn't match the beneficiary id from meeting.locusInfo.mediaShares
3480
+ meeting.selfId = 'new self id';
3481
+
3482
+ const share = meeting.releaseScreenShareFloor();
3483
+
3484
+ assert.exists(share.then);
3485
+ await share;
3486
+ assert.notCalled(meeting.meetingRequest.changeMeetingFloor);
3487
+ });
3476
3488
  });
3477
3489
 
3478
3490
  describe('#setSipUri', () => {
@@ -4003,7 +4015,7 @@ describe('plugin-meetings', () => {
4003
4015
  if (newPayload.previous.content.beneficiaryId === USER_IDS.ME) {
4004
4016
  eventTrigger.share.push({
4005
4017
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,
4006
- functionName: 'stopFloorRequest'
4018
+ functionName: 'localShare'
4007
4019
  });
4008
4020
  }
4009
4021
  else if (newPayload.current.content.beneficiaryId === USER_IDS.ME) {
@@ -4054,7 +4066,7 @@ describe('plugin-meetings', () => {
4054
4066
  if (newPayload.current.content.beneficiaryId === USER_IDS.ME) {
4055
4067
  eventTrigger.share.push({
4056
4068
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,
4057
- functionName: 'stopFloorRequest'
4069
+ functionName: 'localShare'
4058
4070
  });
4059
4071
  }
4060
4072
  else {
@@ -4073,7 +4085,7 @@ describe('plugin-meetings', () => {
4073
4085
  if (newPayload.previous.content.beneficiaryId === USER_IDS.ME) {
4074
4086
  eventTrigger.share.push({
4075
4087
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,
4076
- functionName: 'stopFloorRequest'
4088
+ functionName: 'localShare'
4077
4089
  });
4078
4090
  }
4079
4091
  else if (newPayload.current.content.beneficiaryId === USER_IDS.ME) {
@@ -4116,7 +4128,7 @@ describe('plugin-meetings', () => {
4116
4128
  if (beneficiaryId === USER_IDS.ME) {
4117
4129
  eventTrigger.share.push({
4118
4130
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,
4119
- functionName: 'stopFloorRequest'
4131
+ functionName: 'localShare'
4120
4132
  });
4121
4133
  }
4122
4134
  else {
@@ -4582,6 +4594,93 @@ describe('plugin-meetings', () => {
4582
4594
  meeting.stopKeepAlive();
4583
4595
  });
4584
4596
  });
4597
+
4598
+ describe('#sendReaction', () => {
4599
+ it('should have #sendReaction', () => {
4600
+ assert.exists(meeting.sendReaction);
4601
+ });
4602
+
4603
+ beforeEach(() => {
4604
+ meeting.meetingRequest.sendReaction = sinon.stub().returns(Promise.resolve());
4605
+ });
4606
+
4607
+ it('should send reaction with the right data and return a promise', async () => {
4608
+ meeting.locusInfo.controls = {reactions: {reactionChannelUrl: 'Fake URL'}};
4609
+
4610
+ const reactionPromise = meeting.sendReaction('thumbs_down', 'light');
4611
+
4612
+ assert.exists(reactionPromise.then);
4613
+ await reactionPromise;
4614
+ assert.calledOnceWithExactly(meeting.meetingRequest.sendReaction, {
4615
+ reactionChannelUrl: 'Fake URL',
4616
+ reaction: {
4617
+ type: 'thumb_down',
4618
+ codepoints: '1F44E',
4619
+ shortcodes: ':thumbsdown:',
4620
+ tone: {
4621
+ type: 'light_skin_tone',
4622
+ codepoints: '1F3FB',
4623
+ shortcodes: ':skin-tone-2:'
4624
+ }
4625
+ },
4626
+ participantId: meeting.members.selfId,
4627
+ });
4628
+ });
4629
+
4630
+ it('should fail sending a reaction if data channel is undefined', async () => {
4631
+ meeting.locusInfo.controls = {reactions: {reactionChannelUrl: undefined}};
4632
+
4633
+ await assert.isRejected(meeting.sendReaction('thumbs_down', 'light'), Error, 'Error sending reaction, service url not found.');
4634
+
4635
+ assert.notCalled(meeting.meetingRequest.sendReaction);
4636
+ });
4637
+
4638
+ it('should fail sending a reaction if reactionType is invalid ', async () => {
4639
+ meeting.locusInfo.controls = {reactions: {reactionChannelUrl: 'Fake URL'}};
4640
+
4641
+ await assert.isRejected(meeting.sendReaction('invalid_reaction', 'light'), Error, 'invalid_reaction is not a valid reaction.');
4642
+
4643
+ assert.notCalled(meeting.meetingRequest.sendReaction);
4644
+ });
4645
+
4646
+ it('should send a reaction with default skin tone if provided skinToneType is invalid ', async () => {
4647
+ meeting.locusInfo.controls = {reactions: {reactionChannelUrl: 'Fake URL'}};
4648
+
4649
+ const reactionPromise = meeting.sendReaction('thumbs_down', 'invalid_skin_tone');
4650
+
4651
+ assert.exists(reactionPromise.then);
4652
+ await reactionPromise;
4653
+ assert.calledOnceWithExactly(meeting.meetingRequest.sendReaction, {
4654
+ reactionChannelUrl: 'Fake URL',
4655
+ reaction: {
4656
+ type: 'thumb_down',
4657
+ codepoints: '1F44E',
4658
+ shortcodes: ':thumbsdown:',
4659
+ tone: {type: 'normal_skin_tone', codepoints: '', shortcodes: ''}
4660
+ },
4661
+ participantId: meeting.members.selfId,
4662
+ });
4663
+ });
4664
+
4665
+ it('should send a reaction with default skin tone if none provided', async () => {
4666
+ meeting.locusInfo.controls = {reactions: {reactionChannelUrl: 'Fake URL'}};
4667
+
4668
+ const reactionPromise = meeting.sendReaction('thumbs_down');
4669
+
4670
+ assert.exists(reactionPromise.then);
4671
+ await reactionPromise;
4672
+ assert.calledOnceWithExactly(meeting.meetingRequest.sendReaction, {
4673
+ reactionChannelUrl: 'Fake URL',
4674
+ reaction: {
4675
+ type: 'thumb_down',
4676
+ codepoints: '1F44E',
4677
+ shortcodes: ':thumbsdown:',
4678
+ tone: {type: 'normal_skin_tone', codepoints: '', shortcodes: ''}
4679
+ },
4680
+ participantId: meeting.members.selfId,
4681
+ });
4682
+ });
4683
+ });
4585
4684
  });
4586
4685
  });
4587
4686
  });
@@ -1,7 +1,6 @@
1
1
  import sinon from 'sinon';
2
2
  import {assert} from '@webex/test-helper-chai';
3
3
  import MockWebex from '@webex/test-helper-mock-webex';
4
-
5
4
  import Meetings from '@webex/plugin-meetings';
6
5
  import MeetingRequest from '@webex/plugin-meetings/src/meeting/request';
7
6
 
@@ -278,5 +277,30 @@ describe('plugin-meetings', () => {
278
277
  assert.equal(requestParams.uri, keepAliveUrl);
279
278
  });
280
279
  });
280
+
281
+ describe('#sendReaction', () => {
282
+ it('sends request to sendReaction', async () => {
283
+ const reactionChannelUrl = 'reactionChannelUrl';
284
+ const participantId = 'participantId';
285
+ const reaction = {
286
+ type: 'thumb_down',
287
+ codepoints: '1F44E',
288
+ shortcodes: ':thumbsdown:',
289
+ tone: {type: 'normal_skin_tone', codepoints: '', shortcodes: ''}
290
+ };
291
+
292
+ await meetingsRequest.sendReaction({
293
+ reactionChannelUrl,
294
+ reaction,
295
+ participantId
296
+ });
297
+ const requestParams = meetingsRequest.request.getCall(0).args[0];
298
+
299
+ assert.equal(requestParams.method, 'POST');
300
+ assert.equal(requestParams.uri, reactionChannelUrl);
301
+ assert.equal(requestParams.body.sender.participantId, participantId);
302
+ assert.equal(requestParams.body.reaction, reaction);
303
+ });
304
+ });
281
305
  });
282
306
  });