@webex/plugin-meetings 3.8.1-next.31 → 3.8.1-next.33

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.
@@ -4,6 +4,7 @@ import MembersCollection from './collection';
4
4
  import { ReceiveSlotManager } from '../multistream/receiveSlotManager';
5
5
  import { MediaRequestManager } from '../multistream/mediaRequestManager';
6
6
  import { ServerRoleShape } from './types';
7
+ import { Invitee } from '../meeting/type';
7
8
  /**
8
9
  * @class Members
9
10
  */
@@ -212,31 +213,28 @@ export default class Members extends StatelessWebexPlugin {
212
213
  private update;
213
214
  /**
214
215
  * Adds a guest Member to the associated meeting
215
- * @param {String} invitee
216
+ * @param {Invitee} invitee
216
217
  * @param {Boolean} [alertIfActive]
217
218
  * @returns {Promise}
218
219
  * @memberof Members
219
220
  */
220
- addMember(invitee: any, alertIfActive?: boolean): any;
221
+ addMember(invitee: Invitee, alertIfActive?: boolean): any;
221
222
  /**
222
223
  * Cancels an outgoing PSTN call to the associated meeting
223
- * @param {String} invitee
224
+ * @param {Invitee} invitee
224
225
  * @returns {Promise}
225
226
  * @memberof Members
226
227
  */
227
- cancelPhoneInvite(invitee: any): any;
228
+ cancelPhoneInvite(invitee: Invitee): any;
228
229
  /**
229
230
  * Cancels an SIP/phone call to the associated meeting
230
- * @param {Object} invitee
231
+ * @param {Invitee} invitee
231
232
  * @param {String} invitee.memberId - The memberId of the invitee
232
233
  * @param {Boolean} [invitee.isInternalNumber] - When cancel phone invitation, if the number is internal
233
234
  * @returns {Promise}
234
235
  * @memberof Members
235
236
  */
236
- cancelInviteByMemberId(invitee: {
237
- memberId: string;
238
- isInternalNumber?: boolean;
239
- }): any;
237
+ cancelInviteByMemberId(invitee: Invitee): any;
240
238
  /**
241
239
  * Admits waiting members (invited guests to meeting)
242
240
  * @param {Array} memberIds
@@ -1,4 +1,5 @@
1
1
  import { RoleAssignmentOptions, RoleAssignmentRequest, ServerRoleShape } from './types';
2
+ import { Invitee } from '../meeting/type';
2
3
  declare const MembersUtil: {
3
4
  /**
4
5
  * @param {Object} invitee with emailAddress, email or phoneNumber
@@ -63,7 +64,7 @@ declare const MembersUtil: {
63
64
  alertIfActive: any;
64
65
  };
65
66
  };
66
- isInvalidInvitee: (invitee: any) => boolean;
67
+ isInvalidInvitee: (invitee: Invitee) => boolean;
67
68
  getRemoveMemberRequestParams: (options: any) => {
68
69
  method: string;
69
70
  uri: string;
@@ -458,7 +458,7 @@ var Webinar = _webexCore.WebexPlugin.extend({
458
458
  }, _callee7);
459
459
  }))();
460
460
  },
461
- version: "3.8.1-next.31"
461
+ version: "3.8.1-next.33"
462
462
  });
463
463
  var _default = exports.default = Webinar;
464
464
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -43,7 +43,7 @@
43
43
  "@webex/eslint-config-legacy": "0.0.0",
44
44
  "@webex/jest-config-legacy": "0.0.0",
45
45
  "@webex/legacy-tools": "0.0.0",
46
- "@webex/plugin-meetings": "3.8.1-next.31",
46
+ "@webex/plugin-meetings": "3.8.1-next.33",
47
47
  "@webex/plugin-rooms": "3.8.1-next.7",
48
48
  "@webex/test-helper-chai": "3.8.1-next.10",
49
49
  "@webex/test-helper-mocha": "3.8.1-next.10",
@@ -71,7 +71,7 @@
71
71
  "@webex/internal-plugin-metrics": "3.8.1-next.10",
72
72
  "@webex/internal-plugin-support": "3.8.1-next.10",
73
73
  "@webex/internal-plugin-user": "3.8.1-next.10",
74
- "@webex/internal-plugin-voicea": "3.8.1-next.31",
74
+ "@webex/internal-plugin-voicea": "3.8.1-next.33",
75
75
  "@webex/media-helpers": "3.8.1-next.14",
76
76
  "@webex/plugin-people": "3.8.1-next.10",
77
77
  "@webex/plugin-rooms": "3.8.1-next.7",
@@ -93,5 +93,5 @@
93
93
  "//": [
94
94
  "TODO: upgrade jwt-decode when moving to node 18"
95
95
  ],
96
- "version": "3.8.1-next.31"
96
+ "version": "3.8.1-next.33"
97
97
  }
@@ -166,6 +166,7 @@ import MultistreamNotSupportedError from '../common/errors/multistream-not-suppo
166
166
  import JoinForbiddenError from '../common/errors/join-forbidden-error';
167
167
  import {ReachabilityMetrics} from '../reachability/reachability.types';
168
168
  import {SetStageOptions, SetStageVideoLayout, UnsetStageVideoLayout} from './request.type';
169
+ import {Invitee} from './type';
169
170
 
170
171
  // default callback so we don't call an undefined function, but in practice it should never be used
171
172
  const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
@@ -3064,12 +3065,16 @@ export default class Meeting extends StatelessWebexPlugin {
3064
3065
  // There is no concept of local/remote share for whiteboard
3065
3066
  // It does not matter who requested to share the whiteboard, everyone gets the same view
3066
3067
  else if (whiteboardShare.disposition === FLOOR_ACTION.GRANTED) {
3067
- // WHITEBOARD - sharing whiteboard
3068
- // Webinar attendee should receive whiteboard as remote share
3069
- newShareStatus =
3070
- this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee
3071
- ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
3072
- : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
3068
+ if (this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee) {
3069
+ // WHITEBOARD - sharing whiteboard
3070
+ // Webinar attendee should receive whiteboard as remote share
3071
+ newShareStatus = SHARE_STATUS.REMOTE_SHARE_ACTIVE;
3072
+ } else if (this.guest) {
3073
+ // If user is a guest to a meeting, they should receive whiteboard as remote share
3074
+ newShareStatus = SHARE_STATUS.REMOTE_SHARE_ACTIVE;
3075
+ } else {
3076
+ newShareStatus = SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
3077
+ }
3073
3078
  }
3074
3079
  // or if content share is either released or null and whiteboard share is either released or null, no one is sharing
3075
3080
  else if (
@@ -3835,49 +3840,43 @@ export default class Meeting extends StatelessWebexPlugin {
3835
3840
 
3836
3841
  /**
3837
3842
  * Invite a guest to the call that isn't normally part of this call
3838
- * @param {Object} invitee
3843
+ * @param {Invitee} invitee
3839
3844
  * @param {String} invitee.emailAddress
3840
3845
  * @param {String} invitee.email
3841
3846
  * @param {String} invitee.phoneNumber
3842
3847
  * @param {Boolean} [alertIfActive]
3848
+ * @param {Boolean} [invitee.skipEmailValidation]
3849
+ * @param {Boolean} [invitee.isInternalNumber]
3843
3850
  * @returns {Promise} see #members.addMember
3844
3851
  * @public
3845
3852
  * @memberof Meeting
3846
3853
  */
3847
- public invite(
3848
- invitee: {
3849
- emailAddress: string;
3850
- email: string;
3851
- phoneNumber: string;
3852
- roles: Array<string>;
3853
- },
3854
- alertIfActive = true
3855
- ) {
3854
+ public invite(invitee: Invitee, alertIfActive = true) {
3856
3855
  return this.members.addMember(invitee, alertIfActive);
3857
3856
  }
3858
3857
 
3859
3858
  /**
3860
3859
  * Cancel an outgoing phone call invitation made during a meeting
3861
- * @param {Object} invitee
3860
+ * @param {Invitee} invitee
3862
3861
  * @param {String} invitee.phoneNumber
3863
3862
  * @returns {Promise} see #members.cancelPhoneInvite
3864
3863
  * @public
3865
3864
  * @memberof Meeting
3866
3865
  */
3867
- public cancelPhoneInvite(invitee: {phoneNumber: string}) {
3866
+ public cancelPhoneInvite(invitee: Invitee) {
3868
3867
  return this.members.cancelPhoneInvite(invitee);
3869
3868
  }
3870
3869
 
3871
3870
  /**
3872
3871
  * Cancel an SIP/phone call invitation made during a meeting
3873
- * @param {Object} invitee
3872
+ * @param {Invitee} invitee
3874
3873
  * @param {String} invitee.memberId
3875
3874
  * @param {Boolean} [invitee.isInternalNumber] - When cancel phone invitation, if the number is internal
3876
3875
  * @returns {Promise} see #members.cancelInviteByMemberId
3877
3876
  * @public
3878
3877
  * @memberof Meeting
3879
3878
  */
3880
- public cancelInviteByMemberId(invitee: {memberId: string; isInternalNumber?: boolean}) {
3879
+ public cancelInviteByMemberId(invitee: Invitee) {
3881
3880
  return this.members.cancelInviteByMemberId(invitee);
3882
3881
  }
3883
3882
 
@@ -0,0 +1,9 @@
1
+ export type Invitee = {
2
+ memberId: string;
3
+ emailAddress: string;
4
+ email: string;
5
+ phoneNumber: string;
6
+ roles: Array<string>;
7
+ skipEmailValidation?: boolean;
8
+ isInternalNumber?: boolean;
9
+ };
@@ -30,6 +30,7 @@ import MembersUtil from './util';
30
30
  import {ReceiveSlotManager} from '../multistream/receiveSlotManager';
31
31
  import {MediaRequestManager} from '../multistream/mediaRequestManager';
32
32
  import {ServerRoleShape} from './types';
33
+ import {Invitee} from '../meeting/type';
33
34
 
34
35
  /**
35
36
  * Members Update Event
@@ -800,18 +801,18 @@ export default class Members extends StatelessWebexPlugin {
800
801
 
801
802
  /**
802
803
  * Adds a guest Member to the associated meeting
803
- * @param {String} invitee
804
+ * @param {Invitee} invitee
804
805
  * @param {Boolean} [alertIfActive]
805
806
  * @returns {Promise}
806
807
  * @memberof Members
807
808
  */
808
- addMember(invitee: any, alertIfActive?: boolean) {
809
+ addMember(invitee: Invitee, alertIfActive?: boolean) {
809
810
  if (!this.locusUrl) {
810
811
  return Promise.reject(
811
812
  new ParameterError('The associated locus url for this meeting object must be defined.')
812
813
  );
813
814
  }
814
- if (MembersUtil.isInvalidInvitee(invitee)) {
815
+ if (invitee?.skipEmailValidation !== true && MembersUtil.isInvalidInvitee(invitee)) {
815
816
  return Promise.reject(
816
817
  new ParameterError(
817
818
  'The invitee must be defined with either a valid email, emailAddress or phoneNumber property.'
@@ -825,11 +826,11 @@ export default class Members extends StatelessWebexPlugin {
825
826
 
826
827
  /**
827
828
  * Cancels an outgoing PSTN call to the associated meeting
828
- * @param {String} invitee
829
+ * @param {Invitee} invitee
829
830
  * @returns {Promise}
830
831
  * @memberof Members
831
832
  */
832
- cancelPhoneInvite(invitee: any) {
833
+ cancelPhoneInvite(invitee: Invitee) {
833
834
  if (!this.locusUrl) {
834
835
  return Promise.reject(
835
836
  new ParameterError('The associated locus url for this meeting object must be defined.')
@@ -847,13 +848,13 @@ export default class Members extends StatelessWebexPlugin {
847
848
 
848
849
  /**
849
850
  * Cancels an SIP/phone call to the associated meeting
850
- * @param {Object} invitee
851
+ * @param {Invitee} invitee
851
852
  * @param {String} invitee.memberId - The memberId of the invitee
852
853
  * @param {Boolean} [invitee.isInternalNumber] - When cancel phone invitation, if the number is internal
853
854
  * @returns {Promise}
854
855
  * @memberof Members
855
856
  */
856
- cancelInviteByMemberId(invitee: {memberId: string; isInternalNumber?: boolean}) {
857
+ cancelInviteByMemberId(invitee: Invitee) {
857
858
  if (!this.locusUrl) {
858
859
  return Promise.reject(
859
860
  new ParameterError('The associated locus url for this meeting object must be defined.')
@@ -14,6 +14,7 @@ import {
14
14
  } from '../constants';
15
15
 
16
16
  import {RoleAssignmentOptions, RoleAssignmentRequest, ServerRoleShape} from './types';
17
+ import {Invitee} from '../meeting/type';
17
18
 
18
19
  const MembersUtil = {
19
20
  /**
@@ -105,7 +106,7 @@ const MembersUtil = {
105
106
  return requestParams;
106
107
  },
107
108
 
108
- isInvalidInvitee: (invitee) => {
109
+ isInvalidInvitee: (invitee: Invitee) => {
109
110
  if (!(invitee && (invitee.email || invitee.emailAddress || invitee.phoneNumber))) {
110
111
  return true;
111
112
  }
@@ -56,6 +56,7 @@ import * as MeetingRequestImport from '@webex/plugin-meetings/src/meeting/reques
56
56
  import LocusInfo from '@webex/plugin-meetings/src/locus-info';
57
57
  import MediaProperties from '@webex/plugin-meetings/src/media/properties';
58
58
  import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
59
+ import MembersUtil from '@webex/plugin-meetings/src/members/util';
59
60
  import MeetingsUtil from '@webex/plugin-meetings/src/meetings/util';
60
61
  import Media from '@webex/plugin-meetings/src/media/index';
61
62
  import ReconnectionManager from '@webex/plugin-meetings/src/reconnection-manager';
@@ -610,7 +611,6 @@ describe('plugin-meetings', () => {
610
611
  assert.isFalse(meeting.isLocusCall());
611
612
  });
612
613
  });
613
-
614
614
  describe('#invite', () => {
615
615
  it('should have #invite', () => {
616
616
  assert.exists(meeting.invite);
@@ -621,8 +621,6 @@ describe('plugin-meetings', () => {
621
621
  it('should proxy members #addMember and return a promise', async () => {
622
622
  const invite = meeting.invite(uuid1, false);
623
623
 
624
- assert.exists(invite.then);
625
- await invite;
626
624
  assert.calledOnce(meeting.members.addMember);
627
625
  assert.calledWith(meeting.members.addMember, uuid1, false);
628
626
  });
@@ -12433,7 +12431,7 @@ describe('plugin-meetings', () => {
12433
12431
  activeSharingId.whiteboard = beneficiaryId;
12434
12432
 
12435
12433
  eventTrigger.share.push(
12436
- meeting.webinar.selfIsAttendee
12434
+ meeting.webinar.selfIsAttendee || meeting.guest
12437
12435
  ? {
12438
12436
  eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
12439
12437
  functionName: 'remoteShare',
@@ -12452,7 +12450,8 @@ describe('plugin-meetings', () => {
12452
12450
  }
12453
12451
  );
12454
12452
 
12455
- shareStatus = meeting.webinar.selfIsAttendee
12453
+ shareStatus =
12454
+ meeting.webinar.selfIsAttendee || meeting.guest
12456
12455
  ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
12457
12456
  : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
12458
12457
  }
@@ -12670,6 +12669,36 @@ describe('plugin-meetings', () => {
12670
12669
  });
12671
12670
  });
12672
12671
 
12672
+ describe('Whiteboard Share - User is guest', () => {
12673
+ it('User receives a remote share instead of whiteboard share', () => {
12674
+ // Set the guest flag
12675
+ meeting.guest = true;
12676
+
12677
+ // Step 1: Start sharing whiteboard A
12678
+ const data1 = generateData(
12679
+ blankPayload, // Initial payload
12680
+ true, // isGranting: Granting share
12681
+ false, // isContent: Whiteboard (not content)
12682
+ USER_IDS.REMOTE_A, // Beneficiary ID: Remote user A
12683
+ RESOURCE_URLS.WHITEBOARD_A // Resource URL: Whiteboard A
12684
+ );
12685
+
12686
+ // Step 2: Stop sharing whiteboard A
12687
+ const data2 = generateData(
12688
+ data1.payload, // Updated payload from Step 1
12689
+ false, // isGranting: Stopping share
12690
+ false, // isContent: Whiteboard
12691
+ USER_IDS.REMOTE_A // Beneficiary ID: Remote user A
12692
+ );
12693
+
12694
+ // Validate the payload changes and status updates
12695
+ payloadTestHelper([data1]);
12696
+
12697
+ // Specific assertions for guest
12698
+ assert.equal(meeting.shareStatus, SHARE_STATUS.REMOTE_SHARE_ACTIVE);
12699
+ });
12700
+ });
12701
+
12673
12702
  describe('Whiteboard A --> Whiteboard B', () => {
12674
12703
  it('Scenario #1: you share both whiteboards', () => {
12675
12704
  const data1 = generateData(
@@ -177,6 +177,26 @@ describe('plugin-meetings', () => {
177
177
  assert.isFalse(MembersUtil.isInvalidInvitee({email: 'sip:test@cisco.com'}), 'SIP email should be valid');
178
178
  });
179
179
 
180
+ it('should skip email validation if skipEmailValidation is true', async () => {
181
+ sandbox.spy(MembersUtil, 'isInvalidInvitee');
182
+
183
+ const members = createMembers({url: true});
184
+
185
+ await members.addMember({email: '8618578675309', skipEmailValidation: true});
186
+
187
+ assert.notCalled(MembersUtil.isInvalidInvitee);
188
+ });
189
+
190
+ it('should not skip email validation if skipEmailValidation is not equal true', async () => {
191
+ sandbox.spy(MembersUtil, 'isInvalidInvitee');
192
+
193
+ const members = createMembers({url: true});
194
+
195
+ await members.addMember({email: '86185786@ds.com'});
196
+
197
+ assert.called(MembersUtil.isInvalidInvitee);
198
+ });
199
+
180
200
  it('should accept valid phone with isInternalNumber', async () => {
181
201
  sandbox.spy(MembersUtil, 'isInvalidInvitee');
182
202
 
@@ -190,6 +210,22 @@ describe('plugin-meetings', () => {
190
210
  assert.isFalse(MembersUtil.isInvalidInvitee({phoneNumber: '18578675309', isInternalNumber: true}));
191
211
  assert.isTrue(MembersUtil.isInvalidInvitee({phoneNumber: '+8618578675309', isInternalNumber: true}));
192
212
  });
213
+
214
+ it('should not crash if params is undefined', async () => {
215
+ sandbox.spy(MembersUtil, 'isInvalidInvitee');
216
+
217
+ const members = createMembers({url: true});
218
+
219
+ try {
220
+ await members.addMember(undefined);
221
+ } catch (err) {
222
+ assert.instanceOf(err, ParameterError);
223
+
224
+ assert.equal(err.message, 'The invitee must be defined with either a valid email, emailAddress or phoneNumber property.');
225
+ }
226
+
227
+ assert.called(MembersUtil.isInvalidInvitee);
228
+ });
193
229
  });
194
230
 
195
231
  describe('#admitMembers', () => {