@webex/plugin-meetings 3.9.0-next.14 → 3.9.0-next.15
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/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +11 -4
- package/dist/locus-info/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/members/collection.js +13 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +39 -18
- package/dist/members/index.js.map +1 -1
- package/dist/types/locus-info/index.d.ts +2 -1
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/members/collection.d.ts +6 -0
- package/dist/types/members/index.d.ts +10 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +1 -1
- package/src/locus-info/index.ts +16 -4
- package/src/member/types.ts +1 -0
- package/src/members/collection.ts +11 -0
- package/src/members/index.ts +29 -3
- package/test/unit/spec/locus-info/index.js +11 -8
- package/test/unit/spec/members/collection.js +120 -0
- package/test/unit/spec/members/index.js +42 -1
package/src/locus-info/index.ts
CHANGED
@@ -294,7 +294,7 @@ export default class LocusInfo extends EventsScope {
|
|
294
294
|
this.updateLocusCache(locus);
|
295
295
|
// above section only updates the locusInfo object
|
296
296
|
// The below section makes sure it updates the locusInfo as well as updates the meeting object
|
297
|
-
this.updateParticipants(locus.participants);
|
297
|
+
this.updateParticipants(locus.participants, []);
|
298
298
|
// For 1:1 space meeting the conversation Url does not exist in locus.conversation
|
299
299
|
this.updateConversationUrl(locus.conversationUrl, locus.info);
|
300
300
|
this.updateControls(locus.controls, locus.self);
|
@@ -335,6 +335,8 @@ export default class LocusInfo extends EventsScope {
|
|
335
335
|
const locus = this.getTheLocusToUpdate(data.locus);
|
336
336
|
LoggerProxy.logger.info(`Locus-info:index#parse --> received locus data: ${eventType}`);
|
337
337
|
|
338
|
+
locus.jsSdkMeta = {removedParticipantIds: []};
|
339
|
+
|
338
340
|
switch (eventType) {
|
339
341
|
case LOCUSEVENT.PARTICIPANT_JOIN:
|
340
342
|
case LOCUSEVENT.PARTICIPANT_LEFT:
|
@@ -400,7 +402,11 @@ export default class LocusInfo extends EventsScope {
|
|
400
402
|
this.participants = locus.participants;
|
401
403
|
const isReplaceMembers = ControlsUtils.isNeedReplaceMembers(this.controls, locus.controls);
|
402
404
|
this.updateLocusInfo(locus);
|
403
|
-
this.updateParticipants(
|
405
|
+
this.updateParticipants(
|
406
|
+
locus.participants,
|
407
|
+
locus.jsSdkMeta?.removedParticipantIds,
|
408
|
+
isReplaceMembers
|
409
|
+
);
|
404
410
|
this.isMeetingActive();
|
405
411
|
this.handleOneOnOneEvent(eventType);
|
406
412
|
this.updateEmbeddedApps(locus.embeddedApps);
|
@@ -462,7 +468,11 @@ export default class LocusInfo extends EventsScope {
|
|
462
468
|
const isReplaceMembers = ControlsUtils.isNeedReplaceMembers(this.controls, locus.controls);
|
463
469
|
this.mergeParticipants(this.participants, locus.participants);
|
464
470
|
this.updateLocusInfo(locus);
|
465
|
-
this.updateParticipants(
|
471
|
+
this.updateParticipants(
|
472
|
+
locus.participants,
|
473
|
+
locus.jsSdkMeta?.removedParticipantIds,
|
474
|
+
isReplaceMembers
|
475
|
+
);
|
466
476
|
this.isMeetingActive();
|
467
477
|
}
|
468
478
|
|
@@ -753,11 +763,12 @@ export default class LocusInfo extends EventsScope {
|
|
753
763
|
/**
|
754
764
|
* update meeting's members
|
755
765
|
* @param {Object} participants new participants object
|
766
|
+
* @param {Array} removedParticipantIds list of removed participants
|
756
767
|
* @param {Boolean} isReplace is replace the whole members
|
757
768
|
* @returns {Array} updatedParticipants
|
758
769
|
* @memberof LocusInfo
|
759
770
|
*/
|
760
|
-
updateParticipants(participants: object, isReplace?: boolean) {
|
771
|
+
updateParticipants(participants: object, removedParticipantIds?: string[], isReplace?: boolean) {
|
761
772
|
this.emitScoped(
|
762
773
|
{
|
763
774
|
file: 'locus-info',
|
@@ -766,6 +777,7 @@ export default class LocusInfo extends EventsScope {
|
|
766
777
|
EVENTS.LOCUS_INFO_UPDATE_PARTICIPANTS,
|
767
778
|
{
|
768
779
|
participants,
|
780
|
+
removedParticipantIds,
|
769
781
|
recordingId: this.parsedLocus.controls && this.parsedLocus.controls.record?.modifiedBy,
|
770
782
|
selfIdentity: this.parsedLocus.self && this.parsedLocus.self.selfIdentity,
|
771
783
|
selfId: this.parsedLocus.self && this.parsedLocus.self.selfId,
|
package/src/member/types.ts
CHANGED
@@ -39,6 +39,17 @@ export default class MembersCollection {
|
|
39
39
|
return this.members;
|
40
40
|
}
|
41
41
|
|
42
|
+
/**
|
43
|
+
* Removes a member from the collection
|
44
|
+
* @param {String} id
|
45
|
+
* @returns {void}
|
46
|
+
*/
|
47
|
+
remove(id: string) {
|
48
|
+
if (this.members[id]) {
|
49
|
+
delete this.members[id];
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
42
53
|
/**
|
43
54
|
* @returns {void}
|
44
55
|
* reset members
|
package/src/members/index.ts
CHANGED
@@ -74,7 +74,11 @@ import {Invitee} from '../meeting/type';
|
|
74
74
|
* @memberof Members
|
75
75
|
*/
|
76
76
|
|
77
|
-
type UpdatedMembers = {
|
77
|
+
type UpdatedMembers = {
|
78
|
+
added: Array<Member>;
|
79
|
+
updated: Array<Member>;
|
80
|
+
removedIds?: Array<string>; // removed member ids
|
81
|
+
};
|
78
82
|
/**
|
79
83
|
* @class Members
|
80
84
|
*/
|
@@ -384,12 +388,18 @@ export default class Members extends StatelessWebexPlugin {
|
|
384
388
|
* when new participant updates come in, both delta and full participants, update them in members collection
|
385
389
|
* delta object in the event will have {updated, added} and full will be the full membersCollection
|
386
390
|
* @param {Object} payload
|
387
|
-
* @param {Object} payload.participants
|
391
|
+
* @param {Object} payload.participants new/updated participants
|
392
|
+
* @param {Boolean} payload.isReplace whether to replace the whole members collection
|
393
|
+
* @param {Object} payload.removedParticipantIds ids of the removed participants
|
388
394
|
* @returns {undefined}
|
389
395
|
* @private
|
390
396
|
* @memberof Members
|
391
397
|
*/
|
392
|
-
locusParticipantsUpdate(payload: {
|
398
|
+
locusParticipantsUpdate(payload: {
|
399
|
+
participants: object;
|
400
|
+
isReplace?: boolean;
|
401
|
+
removedParticipantIds?: Array<string>;
|
402
|
+
}) {
|
393
403
|
if (payload) {
|
394
404
|
if (payload.isReplace) {
|
395
405
|
this.clearMembers();
|
@@ -547,10 +557,22 @@ export default class Members extends StatelessWebexPlugin {
|
|
547
557
|
private handleMembersUpdate(membersUpdate: UpdatedMembers) {
|
548
558
|
this.constructMembers(membersUpdate.updated, true);
|
549
559
|
this.constructMembers(membersUpdate.added, false);
|
560
|
+
this.removeMembers(membersUpdate.removedIds);
|
550
561
|
|
551
562
|
return this.membersCollection.getAll();
|
552
563
|
}
|
553
564
|
|
565
|
+
/**
|
566
|
+
* removes members from the collection
|
567
|
+
* @param {Array<string>} removedMembers removed members ids
|
568
|
+
* @returns {void}
|
569
|
+
*/
|
570
|
+
private removeMembers(removedMembers: Array<string>) {
|
571
|
+
removedMembers.forEach((memberId) => {
|
572
|
+
this.membersCollection.remove(memberId);
|
573
|
+
});
|
574
|
+
}
|
575
|
+
|
554
576
|
/**
|
555
577
|
* set members to the member collection from each updated/added lists as passed in
|
556
578
|
* @param {Array} list
|
@@ -600,6 +622,10 @@ export default class Members extends StatelessWebexPlugin {
|
|
600
622
|
}
|
601
623
|
const memberUpdate = this.update(payload.participants);
|
602
624
|
|
625
|
+
// this code depends on memberIds being the same as participantIds
|
626
|
+
// if MemberUtil.extractId() ever changes, this will need to be updated
|
627
|
+
memberUpdate.removedIds = payload.removedParticipantIds || [];
|
628
|
+
|
603
629
|
return memberUpdate;
|
604
630
|
}
|
605
631
|
|
@@ -772,7 +772,7 @@ describe('plugin-meetings', () => {
|
|
772
772
|
},
|
773
773
|
};
|
774
774
|
locusInfo.emitScoped = sinon.stub();
|
775
|
-
locusInfo.updateParticipants({});
|
775
|
+
locusInfo.updateParticipants({}, []);
|
776
776
|
|
777
777
|
// if this assertion fails, double-check the attributes used in
|
778
778
|
// the updateParticipants function in locus-info/index.js
|
@@ -790,6 +790,7 @@ describe('plugin-meetings', () => {
|
|
790
790
|
selfId: '2',
|
791
791
|
hostId: '3',
|
792
792
|
isReplace: undefined,
|
793
|
+
removedParticipantIds: [],
|
793
794
|
}
|
794
795
|
);
|
795
796
|
// note: in a real use case, recordingId, selfId, and hostId would all be the same
|
@@ -814,7 +815,7 @@ describe('plugin-meetings', () => {
|
|
814
815
|
};
|
815
816
|
|
816
817
|
locusInfo.emitScoped = sinon.stub();
|
817
|
-
locusInfo.updateParticipants({}, true);
|
818
|
+
locusInfo.updateParticipants({}, [], true);
|
818
819
|
|
819
820
|
assert.calledWith(
|
820
821
|
locusInfo.emitScoped,
|
@@ -830,6 +831,7 @@ describe('plugin-meetings', () => {
|
|
830
831
|
selfId: '2',
|
831
832
|
hostId: '3',
|
832
833
|
isReplace: true,
|
834
|
+
removedParticipantIds: [],
|
833
835
|
}
|
834
836
|
);
|
835
837
|
});
|
@@ -847,7 +849,7 @@ describe('plugin-meetings', () => {
|
|
847
849
|
];
|
848
850
|
|
849
851
|
locusInfo.emitScoped = sinon.stub();
|
850
|
-
locusInfo.updateParticipants(failureParticipant);
|
852
|
+
locusInfo.updateParticipants(failureParticipant, []);
|
851
853
|
assert.calledWith(
|
852
854
|
locusInfo.emitScoped,
|
853
855
|
{
|
@@ -2050,7 +2052,7 @@ describe('plugin-meetings', () => {
|
|
2050
2052
|
|
2051
2053
|
fakeLocus = {
|
2052
2054
|
meeting: true,
|
2053
|
-
participants:
|
2055
|
+
participants: [],
|
2054
2056
|
url: 'newLocusUrl',
|
2055
2057
|
syncUrl: 'newSyncUrl',
|
2056
2058
|
};
|
@@ -2525,7 +2527,7 @@ describe('plugin-meetings', () => {
|
|
2525
2527
|
});
|
2526
2528
|
|
2527
2529
|
it('onDeltaLocus handle delta data', () => {
|
2528
|
-
fakeLocus.participants =
|
2530
|
+
fakeLocus.participants = [];
|
2529
2531
|
const fakeBreakout = {
|
2530
2532
|
sessionId: 'sessionId',
|
2531
2533
|
groupId: 'groupId',
|
@@ -2542,11 +2544,11 @@ describe('plugin-meetings', () => {
|
|
2542
2544
|
};
|
2543
2545
|
locusInfo.updateParticipants = sinon.stub();
|
2544
2546
|
locusInfo.onDeltaLocus(fakeLocus);
|
2545
|
-
assert.calledWith(locusInfo.updateParticipants,
|
2547
|
+
assert.calledWith(locusInfo.updateParticipants, [], undefined, false);
|
2546
2548
|
|
2547
2549
|
fakeLocus.controls.breakout.sessionId = 'sessionId2';
|
2548
2550
|
locusInfo.onDeltaLocus(fakeLocus);
|
2549
|
-
assert.calledWith(locusInfo.updateParticipants,
|
2551
|
+
assert.calledWith(locusInfo.updateParticipants, [], undefined, true);
|
2550
2552
|
});
|
2551
2553
|
|
2552
2554
|
it('onDeltaLocus merges delta participants with existing participants', () => {
|
@@ -2563,7 +2565,7 @@ describe('plugin-meetings', () => {
|
|
2563
2565
|
existingParticipants,
|
2564
2566
|
FAKE_DELTA_PARTICIPANTS
|
2565
2567
|
);
|
2566
|
-
assert.calledWith(locusInfo.updateParticipants, FAKE_DELTA_PARTICIPANTS, false);
|
2568
|
+
assert.calledWith(locusInfo.updateParticipants, FAKE_DELTA_PARTICIPANTS, undefined, false);
|
2567
2569
|
});
|
2568
2570
|
|
2569
2571
|
[true, false].forEach((isDelta) =>
|
@@ -3074,6 +3076,7 @@ describe('plugin-meetings', () => {
|
|
3074
3076
|
id: 'test person id',
|
3075
3077
|
},
|
3076
3078
|
},
|
3079
|
+
participants: [],
|
3077
3080
|
});
|
3078
3081
|
|
3079
3082
|
updateLocusInfoStub.resetHistory();
|
@@ -0,0 +1,120 @@
|
|
1
|
+
const { default: MembersCollection } = require("../../../../src/members/collection");
|
2
|
+
const { default: Member } = require("../../../../src/member");
|
3
|
+
const { assert } = require('@webex/test-helper-chai');
|
4
|
+
|
5
|
+
|
6
|
+
describe('plugin-meetings', () => {
|
7
|
+
describe('MembersCollection', () => {
|
8
|
+
let collection;
|
9
|
+
let member1;
|
10
|
+
let member2;
|
11
|
+
const participant1 = { controls: {}, status: {} };
|
12
|
+
const participant2 = { controls: {}, status: {} };
|
13
|
+
|
14
|
+
beforeEach(() => {
|
15
|
+
collection = new MembersCollection();
|
16
|
+
member1 = new Member(participant1);
|
17
|
+
member2 = new Member(participant2);
|
18
|
+
});
|
19
|
+
|
20
|
+
it('starts with no members', () => {
|
21
|
+
assert.equal(Object.keys(collection.getAll()).length, 0);
|
22
|
+
});
|
23
|
+
|
24
|
+
it('can add members', () => {
|
25
|
+
collection.set('member1', member1);
|
26
|
+
assert.equal(Object.keys(collection.getAll()).length, 1);
|
27
|
+
assert.equal(collection.get('member1'), member1);
|
28
|
+
|
29
|
+
collection.set('member2', member2);
|
30
|
+
assert.equal(Object.keys(collection.getAll()).length, 2);
|
31
|
+
assert.equal(collection.get('member2'), member2);
|
32
|
+
});
|
33
|
+
|
34
|
+
it('can remove members', () => {
|
35
|
+
// Add some members first
|
36
|
+
collection.set('member1', member1);
|
37
|
+
collection.set('member2', member2);
|
38
|
+
assert.equal(Object.keys(collection.getAll()).length, 2);
|
39
|
+
|
40
|
+
// Remove one member
|
41
|
+
collection.remove('member1');
|
42
|
+
assert.equal(Object.keys(collection.getAll()).length, 1);
|
43
|
+
assert.equal(collection.get('member1'), undefined);
|
44
|
+
assert.equal(collection.get('member2'), member2);
|
45
|
+
|
46
|
+
// Remove another member
|
47
|
+
collection.remove('member2');
|
48
|
+
assert.equal(Object.keys(collection.getAll()).length, 0);
|
49
|
+
assert.equal(collection.get('member2'), undefined);
|
50
|
+
|
51
|
+
// Removing non-existent member should not cause errors
|
52
|
+
collection.remove('nonExistent');
|
53
|
+
assert.equal(Object.keys(collection.getAll()).length, 0);
|
54
|
+
});
|
55
|
+
|
56
|
+
describe('reset', () => {
|
57
|
+
it('removes all members', () => {
|
58
|
+
// Add some members first
|
59
|
+
collection.set('member1', member1);
|
60
|
+
collection.set('member2', member2);
|
61
|
+
assert.equal(Object.keys(collection.getAll()).length, 2);
|
62
|
+
|
63
|
+
// Reset should clear all members
|
64
|
+
collection.reset();
|
65
|
+
assert.equal(Object.keys(collection.getAll()).length, 0);
|
66
|
+
assert.equal(collection.get('member1'), undefined);
|
67
|
+
assert.equal(collection.get('member2'), undefined);
|
68
|
+
});
|
69
|
+
});
|
70
|
+
|
71
|
+
describe('setAll', () => {
|
72
|
+
it('replaces all members with new collection', () => {
|
73
|
+
// Add initial member
|
74
|
+
collection.set('member1', member1);
|
75
|
+
assert.equal(Object.keys(collection.getAll()).length, 1);
|
76
|
+
|
77
|
+
// Set all with new collection
|
78
|
+
const newMembers = {
|
79
|
+
'member2': member2,
|
80
|
+
'member3': new Member(participant1)
|
81
|
+
};
|
82
|
+
collection.setAll(newMembers);
|
83
|
+
|
84
|
+
assert.equal(Object.keys(collection.getAll()).length, 2);
|
85
|
+
assert.equal(collection.get('member1'), undefined);
|
86
|
+
assert.equal(collection.get('member2'), member2);
|
87
|
+
assert.exists(collection.get('member3'));
|
88
|
+
});
|
89
|
+
});
|
90
|
+
|
91
|
+
describe('get', () => {
|
92
|
+
it('returns undefined for non-existent members', () => {
|
93
|
+
assert.equal(collection.get('nonExistent'), undefined);
|
94
|
+
});
|
95
|
+
|
96
|
+
it('returns correct member for existing id', () => {
|
97
|
+
collection.set('member1', member1);
|
98
|
+
assert.equal(collection.get('member1'), member1);
|
99
|
+
});
|
100
|
+
});
|
101
|
+
|
102
|
+
describe('getAll', () => {
|
103
|
+
it('returns empty object when no members', () => {
|
104
|
+
const allMembers = collection.getAll();
|
105
|
+
assert.isObject(allMembers);
|
106
|
+
assert.equal(Object.keys(allMembers).length, 0);
|
107
|
+
});
|
108
|
+
|
109
|
+
it('returns all members', () => {
|
110
|
+
collection.set('member1', member1);
|
111
|
+
collection.set('member2', member2);
|
112
|
+
|
113
|
+
const allMembers = collection.getAll();
|
114
|
+
assert.equal(Object.keys(allMembers).length, 2);
|
115
|
+
assert.equal(allMembers.member1, member1);
|
116
|
+
assert.equal(allMembers.member2, member2);
|
117
|
+
});
|
118
|
+
});
|
119
|
+
});
|
120
|
+
});
|
@@ -320,9 +320,18 @@ describe('plugin-meetings', () => {
|
|
320
320
|
EVENT_TRIGGERS.MEMBERS_CLEAR,
|
321
321
|
{}
|
322
322
|
);
|
323
|
+
sinon.restore();
|
323
324
|
});
|
324
325
|
});
|
325
326
|
describe('#locusParticipantsUpdate', () => {
|
327
|
+
beforeEach(() => {
|
328
|
+
sinon.stub(Trigger, 'trigger');
|
329
|
+
});
|
330
|
+
|
331
|
+
afterEach(() => {
|
332
|
+
sinon.restore();
|
333
|
+
});
|
334
|
+
|
326
335
|
it('should send member update event with session info', () => {
|
327
336
|
const members = createMembers({url: url1});
|
328
337
|
const fakePayload = {
|
@@ -343,13 +352,45 @@ describe('plugin-meetings', () => {
|
|
343
352
|
},
|
344
353
|
EVENT_TRIGGERS.MEMBERS_UPDATE,
|
345
354
|
{
|
346
|
-
delta: {added: [], updated: []},
|
355
|
+
delta: {added: [], updated: [], removedIds: []},
|
347
356
|
full: {},
|
348
357
|
isReplace: true,
|
349
358
|
}
|
350
359
|
);
|
351
360
|
});
|
352
361
|
|
362
|
+
it('should handle participants being removed', () => {
|
363
|
+
const members = createMembers({url: url1});
|
364
|
+
|
365
|
+
// setup the collection with a fake member
|
366
|
+
members.membersCollection.setAll(fakeMembersCollection);
|
367
|
+
assert.equal(Object.keys(members.membersCollection.getAll()).length, 1);
|
368
|
+
|
369
|
+
// remove the member
|
370
|
+
members.locusParticipantsUpdate({
|
371
|
+
participants: [],
|
372
|
+
removedParticipantIds: ['test1'],
|
373
|
+
});
|
374
|
+
|
375
|
+
assert.equal(Object.keys(members.membersCollection.getAll()).length, 0);
|
376
|
+
|
377
|
+
// check that the event was emitted
|
378
|
+
assert.calledWith(
|
379
|
+
Trigger.trigger,
|
380
|
+
members,
|
381
|
+
{
|
382
|
+
file: 'members',
|
383
|
+
function: 'locusParticipantsUpdate',
|
384
|
+
},
|
385
|
+
EVENT_TRIGGERS.MEMBERS_UPDATE,
|
386
|
+
{
|
387
|
+
delta: {added: [], updated: [], removedIds: ['test1']},
|
388
|
+
full: {},
|
389
|
+
isReplace: false,
|
390
|
+
}
|
391
|
+
);
|
392
|
+
});
|
393
|
+
|
353
394
|
describe('handles members with paired devices correctly', () => {
|
354
395
|
const runCheck = (propsForUpdate, expectedPropsOnPairedMember) => {
|
355
396
|
const members = createMembers({url: url1});
|