@webex/plugin-meetings 3.9.0-next.2 → 3.9.0-next.20
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 +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +38 -10
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/parser.js +4 -1
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/media/properties.js +53 -5
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +2 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +189 -122
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +2 -5
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +25 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +30 -11
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +29 -21
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +31 -25
- package/dist/meetings/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 +42 -20
- package/dist/members/index.js.map +1 -1
- package/dist/members/util.js +7 -2
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/index.js +3 -3
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/constants.d.ts +2 -0
- package/dist/types/locus-info/index.d.ts +54 -1
- package/dist/types/media/properties.d.ts +21 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
- package/dist/types/meeting/index.d.ts +11 -1
- package/dist/types/meeting/request.d.ts +9 -0
- package/dist/types/meeting/util.d.ts +10 -3
- package/dist/types/meeting-info/meeting-info-v2.d.ts +6 -3
- package/dist/types/meetings/index.d.ts +3 -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 +12 -2
- package/dist/types/members/util.d.ts +6 -3
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +16 -16
- package/src/constants.ts +2 -0
- package/src/locus-info/index.ts +84 -9
- package/src/locus-info/parser.ts +5 -1
- package/src/media/properties.ts +43 -0
- package/src/meeting/in-meeting-actions.ts +4 -0
- package/src/meeting/index.ts +91 -4
- package/src/meeting/muteState.ts +2 -6
- package/src/meeting/request.ts +23 -0
- package/src/meeting/util.ts +41 -20
- package/src/meeting-info/meeting-info-v2.ts +24 -5
- package/src/meetings/index.ts +9 -3
- package/src/member/types.ts +1 -0
- package/src/members/collection.ts +11 -0
- package/src/members/index.ts +38 -5
- package/src/members/util.ts +18 -2
- package/src/metrics/constants.ts +1 -0
- package/src/reachability/index.ts +3 -3
- package/test/unit/spec/locus-info/index.js +30 -15
- package/test/unit/spec/locus-info/parser.js +3 -2
- package/test/unit/spec/media/properties.ts +137 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
- package/test/unit/spec/meeting/index.js +255 -27
- package/test/unit/spec/meeting/muteState.js +32 -6
- package/test/unit/spec/meeting/request.js +21 -0
- package/test/unit/spec/meeting/utils.js +45 -16
- package/test/unit/spec/meeting-info/meetinginfov2.js +8 -3
- package/test/unit/spec/meetings/index.js +10 -1
- package/test/unit/spec/members/collection.js +120 -0
- package/test/unit/spec/members/index.js +72 -3
- package/test/unit/spec/members/request.js +55 -0
- package/test/unit/spec/members/utils.js +116 -14
- package/test/unit/spec/reachability/index.ts +158 -3
package/src/locus-info/parser.ts
CHANGED
@@ -728,13 +728,17 @@ export default class Parser {
|
|
728
728
|
break;
|
729
729
|
|
730
730
|
case USE_INCOMING:
|
731
|
-
case LOCUS_URL_CHANGED:
|
732
731
|
// update working copy for future comparisons.
|
733
732
|
// Note: The working copy of parser gets updated in .onFullLocus()
|
734
733
|
// and here when USE_INCOMING or LOCUS_URL_CHANGED locus.
|
735
734
|
this.workingCopy = newLoci;
|
736
735
|
break;
|
737
736
|
|
737
|
+
case LOCUS_URL_CHANGED:
|
738
|
+
// clear the working copy completely, do a full locus sync
|
739
|
+
this.workingCopy = null;
|
740
|
+
break;
|
741
|
+
|
738
742
|
case WAIT:
|
739
743
|
// we've taken newLoci from the front of the queue, so put it back there as we have to wait
|
740
744
|
// for the one that should be in front of it, before we can process it
|
package/src/media/properties.ts
CHANGED
@@ -9,9 +9,12 @@ import {
|
|
9
9
|
|
10
10
|
import {parse} from '@webex/ts-sdp';
|
11
11
|
import {ClientEvent} from '@webex/internal-plugin-metrics';
|
12
|
+
import {throttle} from 'lodash';
|
13
|
+
import Metrics from '../metrics';
|
12
14
|
import {MEETINGS, QUALITY_LEVELS} from '../constants';
|
13
15
|
import LoggerProxy from '../common/logs/logger-proxy';
|
14
16
|
import MediaConnectionAwaiter from './MediaConnectionAwaiter';
|
17
|
+
import BEHAVIORAL_METRICS from '../metrics/constants';
|
15
18
|
|
16
19
|
export type MediaDirection = {
|
17
20
|
sendAudio: boolean;
|
@@ -41,6 +44,8 @@ export default class MediaProperties {
|
|
41
44
|
videoDeviceId: any;
|
42
45
|
videoStream?: LocalCameraStream;
|
43
46
|
namespace = MEETINGS;
|
47
|
+
mediaIssueCounters: {[key: string]: number} = {};
|
48
|
+
throttledSendMediaIssueMetric: ReturnType<typeof throttle>;
|
44
49
|
|
45
50
|
/**
|
46
51
|
* @param {Object} [options] -- to auto construct
|
@@ -66,6 +71,15 @@ export default class MediaProperties {
|
|
66
71
|
this.remoteQualityLevel = QUALITY_LEVELS.HIGH;
|
67
72
|
this.mediaSettings = {};
|
68
73
|
this.videoDeviceId = null;
|
74
|
+
|
75
|
+
this.throttledSendMediaIssueMetric = throttle((eventPayload) => {
|
76
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED, {
|
77
|
+
...eventPayload,
|
78
|
+
});
|
79
|
+
Object.keys(this.mediaIssueCounters).forEach((key) => {
|
80
|
+
this.mediaIssueCounters[key] = 0;
|
81
|
+
});
|
82
|
+
}, 1000 * 60 * 5); // at most once every 5 minutes
|
69
83
|
}
|
70
84
|
|
71
85
|
/**
|
@@ -139,8 +153,14 @@ export default class MediaProperties {
|
|
139
153
|
this.videoDeviceId = deviceId;
|
140
154
|
}
|
141
155
|
|
156
|
+
/**
|
157
|
+
* Clears the webrtcMediaConnection. This method should be called after
|
158
|
+
* peer connection is closed and no longer needed.
|
159
|
+
* @returns {void}
|
160
|
+
*/
|
142
161
|
unsetPeerConnection() {
|
143
162
|
this.webrtcMediaConnection = null;
|
163
|
+
this.throttledSendMediaIssueMetric.flush();
|
144
164
|
}
|
145
165
|
|
146
166
|
/**
|
@@ -424,4 +444,27 @@ export default class MediaProperties {
|
|
424
444
|
};
|
425
445
|
}
|
426
446
|
}
|
447
|
+
|
448
|
+
/**
|
449
|
+
* Sends a metric about a media issue. Metrics are throttled so that we don't
|
450
|
+
* send too many of them, but include a count so that we know how many issues
|
451
|
+
* were detected.
|
452
|
+
*
|
453
|
+
* @param {string} issueType
|
454
|
+
* @param {string} issueSubType
|
455
|
+
* @param {string} correlationId
|
456
|
+
* @returns {void}
|
457
|
+
*/
|
458
|
+
public sendMediaIssueMetric(issueType: string, issueSubType: string, correlationId) {
|
459
|
+
const key = `${issueType}_${issueSubType}`;
|
460
|
+
|
461
|
+
const count = (this.mediaIssueCounters[key] || 0) + 1;
|
462
|
+
|
463
|
+
this.mediaIssueCounters[key] = count;
|
464
|
+
|
465
|
+
this.throttledSendMediaIssueMetric({
|
466
|
+
correlationId,
|
467
|
+
...this.mediaIssueCounters,
|
468
|
+
});
|
469
|
+
}
|
427
470
|
}
|
@@ -44,6 +44,7 @@ interface IInMeetingActions {
|
|
44
44
|
|
45
45
|
isManualCaptionActive?: boolean;
|
46
46
|
isSaveTranscriptsEnabled?: boolean;
|
47
|
+
isSpokenLanguageAutoDetectionEnabled?: boolean;
|
47
48
|
isWebexAssistantActive?: boolean;
|
48
49
|
canViewCaptionPanel?: boolean;
|
49
50
|
isRealTimeTranslationEnabled?: boolean;
|
@@ -187,6 +188,8 @@ export default class InMeetingActions implements IInMeetingActions {
|
|
187
188
|
|
188
189
|
isSaveTranscriptsEnabled = null;
|
189
190
|
|
191
|
+
isSpokenLanguageAutoDetectionEnabled = null;
|
192
|
+
|
190
193
|
isWebexAssistantActive = null;
|
191
194
|
|
192
195
|
canViewCaptionPanel = null;
|
@@ -363,6 +366,7 @@ export default class InMeetingActions implements IInMeetingActions {
|
|
363
366
|
canStopManualCaption: this.canStopManualCaption,
|
364
367
|
isManualCaptionActive: this.isManualCaptionActive,
|
365
368
|
isSaveTranscriptsEnabled: this.isSaveTranscriptsEnabled,
|
369
|
+
isSpokenLanguageAutoDetectionEnabled: this.isSpokenLanguageAutoDetectionEnabled,
|
366
370
|
isWebexAssistantActive: this.isWebexAssistantActive,
|
367
371
|
canViewCaptionPanel: this.canViewCaptionPanel,
|
368
372
|
isRealTimeTranslationEnabled: this.isRealTimeTranslationEnabled,
|
package/src/meeting/index.ts
CHANGED
@@ -28,6 +28,8 @@ import {
|
|
28
28
|
StatsAnalyzerEventNames,
|
29
29
|
NetworkQualityEventNames,
|
30
30
|
NetworkQualityMonitor,
|
31
|
+
StatsMonitor,
|
32
|
+
StatsMonitorEventNames,
|
31
33
|
} from '@webex/internal-media-core';
|
32
34
|
|
33
35
|
import {
|
@@ -269,6 +271,7 @@ export enum ScreenShareFloorStatus {
|
|
269
271
|
type FetchMeetingInfoParams = {
|
270
272
|
password?: string;
|
271
273
|
registrationId?: string;
|
274
|
+
classificationId?: string;
|
272
275
|
captchaCode?: string;
|
273
276
|
extraParams?: Record<string, any>;
|
274
277
|
sendCAevents?: boolean;
|
@@ -633,6 +636,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
633
636
|
shareStatus: string;
|
634
637
|
screenShareFloorState: ScreenShareFloorStatus;
|
635
638
|
statsAnalyzer: StatsAnalyzer;
|
639
|
+
statsMonitor: StatsMonitor;
|
636
640
|
transcription: Transcription;
|
637
641
|
updateMediaConnections: (mediaConnections: any[]) => void;
|
638
642
|
userDisplayHints: any;
|
@@ -1286,6 +1290,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
1286
1290
|
* @memberof Meeting
|
1287
1291
|
*/
|
1288
1292
|
this.networkQualityMonitor = null;
|
1293
|
+
/**
|
1294
|
+
* @instance
|
1295
|
+
* @type {StatsMonitor}
|
1296
|
+
* @private
|
1297
|
+
* @memberof Meeting
|
1298
|
+
*/
|
1299
|
+
this.statsMonitor = null;
|
1289
1300
|
/**
|
1290
1301
|
* Indicates network status of the webrtc media connection
|
1291
1302
|
* @instance
|
@@ -1902,6 +1913,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
1902
1913
|
extraParams = {},
|
1903
1914
|
sendCAevents = false,
|
1904
1915
|
registrationId = null,
|
1916
|
+
classificationId = null,
|
1905
1917
|
}): Promise<void> {
|
1906
1918
|
try {
|
1907
1919
|
const captchaInfo = captchaCode
|
@@ -1918,7 +1930,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
1918
1930
|
this.locusId,
|
1919
1931
|
extraParams,
|
1920
1932
|
{meetingId: this.id, sendCAevents},
|
1921
|
-
registrationId
|
1933
|
+
registrationId,
|
1934
|
+
null,
|
1935
|
+
classificationId
|
1922
1936
|
);
|
1923
1937
|
|
1924
1938
|
this.parseMeetingInfo(info?.body, this.destination, info?.errors);
|
@@ -3184,6 +3198,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
3184
3198
|
this.shareCAEventSentStatus.receiveStart = false;
|
3185
3199
|
this.shareCAEventSentStatus.receiveStop = false;
|
3186
3200
|
|
3201
|
+
let finalBeneficiaryId = contentShare.beneficiaryId;
|
3202
|
+
// In case of attendee in webinar, the whiteboard is shared by other participants
|
3203
|
+
if (this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee) {
|
3204
|
+
if (!finalBeneficiaryId && whiteboardShare.beneficiaryId) {
|
3205
|
+
finalBeneficiaryId = whiteboardShare.beneficiaryId;
|
3206
|
+
}
|
3207
|
+
}
|
3208
|
+
|
3187
3209
|
Trigger.trigger(
|
3188
3210
|
this,
|
3189
3211
|
{
|
@@ -3192,7 +3214,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
3192
3214
|
},
|
3193
3215
|
EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
3194
3216
|
{
|
3195
|
-
memberId:
|
3217
|
+
memberId: finalBeneficiaryId,
|
3196
3218
|
url: contentShare.url,
|
3197
3219
|
shareInstanceId: this.remoteShareInstanceId,
|
3198
3220
|
annotationInfo: contentShare.annotation,
|
@@ -4214,6 +4236,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
4214
4236
|
isLocalRecordingPaused: MeetingUtil.isLocalRecordingPaused(this.userDisplayHints),
|
4215
4237
|
isManualCaptionActive: MeetingUtil.isManualCaptionActive(this.userDisplayHints),
|
4216
4238
|
isSaveTranscriptsEnabled: MeetingUtil.isSaveTranscriptsEnabled(this.userDisplayHints),
|
4239
|
+
isSpokenLanguageAutoDetectionEnabled: MeetingUtil.isSpokenLanguageAutoDetectionEnabled(
|
4240
|
+
this.userDisplayHints
|
4241
|
+
),
|
4217
4242
|
isWebexAssistantActive: MeetingUtil.isWebexAssistantActive(this.userDisplayHints),
|
4218
4243
|
canViewCaptionPanel: MeetingUtil.canViewCaptionPanel(this.userDisplayHints),
|
4219
4244
|
isRealTimeTranslationEnabled: MeetingUtil.isRealTimeTranslationEnabled(
|
@@ -6796,6 +6821,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
6796
6821
|
// @ts-ignore
|
6797
6822
|
this.webex.internal.newMetrics.submitClientEvent({
|
6798
6823
|
name: 'client.ice.start',
|
6824
|
+
payload: {
|
6825
|
+
// @ts-ignore
|
6826
|
+
labels: MeetingUtil.getCaEventLabelsForIpVersion(this.webex),
|
6827
|
+
},
|
6799
6828
|
options: {
|
6800
6829
|
meetingId: this.id,
|
6801
6830
|
},
|
@@ -6965,10 +6994,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
6965
6994
|
}
|
6966
6995
|
}
|
6967
6996
|
|
6968
|
-
// Count members that are in the meeting.
|
6997
|
+
// Count members that are in the meeting or in the lobby.
|
6969
6998
|
const {members} = this.getMembers().membersCollection;
|
6970
6999
|
event.data.intervalMetadata.meetingUserCount = Object.values(members).filter(
|
6971
|
-
(member: Member) => member.isInMeeting
|
7000
|
+
(member: Member) => member.isInMeeting || member.isInLobby
|
6972
7001
|
).length;
|
6973
7002
|
|
6974
7003
|
// @ts-ignore
|
@@ -7327,10 +7356,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
7327
7356
|
if (this.config.stats.enableStatsAnalyzer) {
|
7328
7357
|
// @ts-ignore - config coming from registerPlugin
|
7329
7358
|
this.networkQualityMonitor = new NetworkQualityMonitor(this.config.stats);
|
7359
|
+
this.statsMonitor = new StatsMonitor();
|
7330
7360
|
this.statsAnalyzer = new StatsAnalyzer({
|
7331
7361
|
// @ts-ignore - config coming from registerPlugin
|
7332
7362
|
config: this.config.stats,
|
7333
7363
|
networkQualityMonitor: this.networkQualityMonitor,
|
7364
|
+
statsMonitor: this.statsMonitor,
|
7334
7365
|
isMultistream: this.isMultistream,
|
7335
7366
|
});
|
7336
7367
|
this.shareCAEventSentStatus = {
|
@@ -7344,6 +7375,33 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
7344
7375
|
NetworkQualityEventNames.NETWORK_QUALITY,
|
7345
7376
|
this.sendNetworkQualityEvent.bind(this)
|
7346
7377
|
);
|
7378
|
+
|
7379
|
+
this.statsMonitor.on(StatsMonitorEventNames.INBOUND_AUDIO_ISSUE, (data) => {
|
7380
|
+
// Before forwarding any inbound audio issues to the app, make sure that we have at least one other
|
7381
|
+
// participant in the meeting with unmuted audio.
|
7382
|
+
// We don't check this.mediaProperties.mediaDirection here, because that's already handled in statsAnalyzer,
|
7383
|
+
// so we won't get this event if we are not setup to receive any audio
|
7384
|
+
const atLeastOneUnmutedOtherMember = Object.values(
|
7385
|
+
this.members.membersCollection.getAll()
|
7386
|
+
).find((member) => {
|
7387
|
+
return !member.isSelf && !member.isPairedWithSelf && !member.isAudioMuted;
|
7388
|
+
});
|
7389
|
+
|
7390
|
+
if (atLeastOneUnmutedOtherMember) {
|
7391
|
+
this.mediaProperties.sendMediaIssueMetric(
|
7392
|
+
'inbound_audio',
|
7393
|
+
data.issueSubType,
|
7394
|
+
this.correlationId
|
7395
|
+
);
|
7396
|
+
|
7397
|
+
Trigger.trigger(
|
7398
|
+
this,
|
7399
|
+
{file: 'meeting/index', function: 'createStatsAnalyzer'},
|
7400
|
+
EVENT_TRIGGERS.MEDIA_INBOUND_AUDIO_ISSUE_DETECTED,
|
7401
|
+
data
|
7402
|
+
);
|
7403
|
+
}
|
7404
|
+
});
|
7347
7405
|
}
|
7348
7406
|
}
|
7349
7407
|
|
@@ -7642,6 +7700,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
7642
7700
|
}
|
7643
7701
|
|
7644
7702
|
this.statsAnalyzer = null;
|
7703
|
+
this.networkQualityMonitor?.removeAllListeners();
|
7704
|
+
this.networkQualityMonitor = null;
|
7705
|
+
this.statsMonitor?.removeAllListeners();
|
7706
|
+
this.statsMonitor = null;
|
7645
7707
|
|
7646
7708
|
// when media fails, we want to upload a webrtc dump to see whats going on
|
7647
7709
|
// this function is async, but returns once the stats have been gathered
|
@@ -7665,6 +7727,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
7665
7727
|
await this.statsAnalyzer.stopAnalyzer();
|
7666
7728
|
}
|
7667
7729
|
this.statsAnalyzer = null;
|
7730
|
+
this.networkQualityMonitor?.removeAllListeners();
|
7731
|
+
this.networkQualityMonitor = null;
|
7732
|
+
this.statsMonitor?.removeAllListeners();
|
7733
|
+
this.statsMonitor = null;
|
7668
7734
|
|
7669
7735
|
this.isMultistream = false;
|
7670
7736
|
|
@@ -7836,6 +7902,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
7836
7902
|
|
7837
7903
|
this.allowMediaInLobby = options?.allowMediaInLobby;
|
7838
7904
|
|
7905
|
+
// @ts-ignore
|
7906
|
+
const ipver = MeetingUtil.getIpVersion(this.webex); // used just for metrics
|
7907
|
+
|
7839
7908
|
// If the user is unjoined or guest waiting in lobby dont allow the user to addMedia
|
7840
7909
|
// @ts-ignore - isUserUnadmitted coming from SelfUtil
|
7841
7910
|
if (this.isUserUnadmitted && !this.wirelessShare && !this.allowMediaInLobby) {
|
@@ -7934,6 +8003,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
7934
8003
|
locus_id: this.locusUrl.split('/').pop(),
|
7935
8004
|
connectionType,
|
7936
8005
|
ipVersion,
|
8006
|
+
ipver,
|
7937
8007
|
selectedCandidatePairChanges,
|
7938
8008
|
numTransports,
|
7939
8009
|
isMultistream: this.isMultistream,
|
@@ -8002,6 +8072,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
8002
8072
|
...reachabilityMetrics,
|
8003
8073
|
...iceCandidateErrors,
|
8004
8074
|
iceCandidatesCount: this.iceCandidatesCount,
|
8075
|
+
ipver,
|
8005
8076
|
});
|
8006
8077
|
|
8007
8078
|
await this.cleanUpOnAddMediaFailure();
|
@@ -9907,4 +9978,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
9907
9978
|
|
9908
9979
|
return this.meetingRequest.synchronizeStage(this.locusUrl, videoLayout);
|
9909
9980
|
}
|
9981
|
+
|
9982
|
+
/**
|
9983
|
+
* Notifies the host with the given meeting UUID and display names.
|
9984
|
+
*
|
9985
|
+
* @param {string} meetingUuid - The UUID of the meeting.
|
9986
|
+
* @param {string[]} displayName - An array of display names to notify the host with.
|
9987
|
+
* @returns {Promise<any>} The result of the notifyHost request.
|
9988
|
+
*/
|
9989
|
+
notifyHost(meetingUuid: string, displayName: string[]) {
|
9990
|
+
return this.meetingRequest.notifyHost(
|
9991
|
+
this.meetingInfo.siteFullUrl,
|
9992
|
+
this.locusId,
|
9993
|
+
meetingUuid,
|
9994
|
+
displayName
|
9995
|
+
);
|
9996
|
+
}
|
9910
9997
|
}
|
package/src/meeting/muteState.ts
CHANGED
@@ -291,18 +291,14 @@ export class MuteState {
|
|
291
291
|
);
|
292
292
|
|
293
293
|
return MeetingUtil.remoteUpdateAudioVideo(meeting, audioMuted, videoMuted)
|
294
|
-
.then((
|
294
|
+
.then((response) => {
|
295
295
|
LoggerProxy.logger.info(
|
296
296
|
`Meeting:muteState#sendLocalMuteRequestToServer --> ${this.type}: local mute (audio=${audioMuted}, video=${videoMuted}) applied to server`
|
297
297
|
);
|
298
298
|
|
299
299
|
this.state.server.localMute = this.type === AUDIO ? audioMuted : videoMuted;
|
300
300
|
|
301
|
-
|
302
|
-
meeting.locusInfo.handleLocusDelta(locus, meeting);
|
303
|
-
}
|
304
|
-
|
305
|
-
return locus;
|
301
|
+
return MeetingUtil.updateLocusFromApiResponse(meeting, response);
|
306
302
|
})
|
307
303
|
.catch((remoteUpdateError) => {
|
308
304
|
LoggerProxy.logger.warn(
|
package/src/meeting/request.ts
CHANGED
@@ -985,4 +985,27 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
985
985
|
body: {videoLayout},
|
986
986
|
});
|
987
987
|
}
|
988
|
+
|
989
|
+
/**
|
990
|
+
* Sends a request to notify the host of a meeting.
|
991
|
+
* @param {string} siteFullUrl - The site URL.
|
992
|
+
* @param {string} locusId - The locus ID.
|
993
|
+
* @param {string} meetingUuid - The meeting UUID.
|
994
|
+
* @param {Array<string>} displayName - The display names to notify the host about.
|
995
|
+
* @returns {Promise}
|
996
|
+
*/
|
997
|
+
notifyHost(siteFullUrl: string, locusId: string, meetingUuid: string, displayName: string[]) {
|
998
|
+
// @ts-ignore
|
999
|
+
return this.request({
|
1000
|
+
method: HTTP_VERBS.POST,
|
1001
|
+
uri: `https://${siteFullUrl}/wbxappapi/v1/meetings/${meetingUuid}/notifyhost`,
|
1002
|
+
body: {
|
1003
|
+
displayName,
|
1004
|
+
size: displayName?.length,
|
1005
|
+
},
|
1006
|
+
headers: {
|
1007
|
+
locusId,
|
1008
|
+
},
|
1009
|
+
});
|
1010
|
+
}
|
988
1011
|
}
|
package/src/meeting/util.ts
CHANGED
@@ -59,18 +59,16 @@ const MeetingUtil = {
|
|
59
59
|
);
|
60
60
|
}
|
61
61
|
|
62
|
-
return meeting.locusMediaRequest
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
})
|
73
|
-
.then((response) => response?.body?.locus);
|
62
|
+
return meeting.locusMediaRequest.send({
|
63
|
+
type: 'LocalMute',
|
64
|
+
selfUrl: meeting.selfUrl,
|
65
|
+
mediaId: meeting.mediaId,
|
66
|
+
sequence: meeting.locusInfo.sequence,
|
67
|
+
muteOptions: {
|
68
|
+
audioMuted,
|
69
|
+
videoMuted,
|
70
|
+
},
|
71
|
+
});
|
74
72
|
},
|
75
73
|
|
76
74
|
hasOwner: (info) => info && info.owner,
|
@@ -115,6 +113,28 @@ const MeetingUtil = {
|
|
115
113
|
return IP_VERSION.unknown;
|
116
114
|
},
|
117
115
|
|
116
|
+
/**
|
117
|
+
* Returns CA event labels related to Orpheus ipver parameter that can be sent to CA with any CA event
|
118
|
+
* @param {any} webex instance
|
119
|
+
* @returns {Array<string>|undefined} array of CA event labels or undefined if no labels should be sent
|
120
|
+
*/
|
121
|
+
getCaEventLabelsForIpVersion(webex: any): Array<string> | undefined {
|
122
|
+
const ipver = MeetingUtil.getIpVersion(webex);
|
123
|
+
|
124
|
+
switch (ipver) {
|
125
|
+
case IP_VERSION.unknown:
|
126
|
+
return undefined;
|
127
|
+
case IP_VERSION.only_ipv4:
|
128
|
+
return ['hasIpv4_true'];
|
129
|
+
case IP_VERSION.only_ipv6:
|
130
|
+
return ['hasIpv6_true'];
|
131
|
+
case IP_VERSION.ipv4_and_ipv6:
|
132
|
+
return ['hasIpv4_true', 'hasIpv6_true'];
|
133
|
+
default:
|
134
|
+
return undefined;
|
135
|
+
}
|
136
|
+
},
|
137
|
+
|
118
138
|
joinMeeting: async (meeting, options) => {
|
119
139
|
if (!meeting) {
|
120
140
|
return Promise.reject(new ParameterError('You need a meeting object.'));
|
@@ -618,6 +638,9 @@ const MeetingUtil = {
|
|
618
638
|
isManualCaptionActive: (displayHints) =>
|
619
639
|
displayHints.includes(DISPLAY_HINTS.MANUAL_CAPTION_STATUS_ACTIVE),
|
620
640
|
|
641
|
+
isSpokenLanguageAutoDetectionEnabled: (displayHints) =>
|
642
|
+
displayHints.includes(DISPLAY_HINTS.SPOKEN_LANGUAGE_AUTO_DETECTION_ENABLED),
|
643
|
+
|
621
644
|
isWebexAssistantActive: (displayHints) =>
|
622
645
|
displayHints.includes(DISPLAY_HINTS.WEBEX_ASSISTANT_STATUS_ACTIVE),
|
623
646
|
|
@@ -673,22 +696,20 @@ const MeetingUtil = {
|
|
673
696
|
},
|
674
697
|
|
675
698
|
/**
|
676
|
-
* Updates the locus info for the meeting with the
|
677
|
-
* returned from requests
|
699
|
+
* Updates the locus info for the meeting with the locus
|
700
|
+
* information returned from API requests made to Locus
|
678
701
|
* Returns the original response object
|
679
702
|
* @param {Object} meeting The meeting object
|
680
703
|
* @param {Object} response The response of the http request
|
681
704
|
* @returns {Object}
|
682
705
|
*/
|
683
|
-
|
706
|
+
updateLocusFromApiResponse: (meeting, response) => {
|
684
707
|
if (!meeting) {
|
685
708
|
return response;
|
686
709
|
}
|
687
710
|
|
688
|
-
|
689
|
-
|
690
|
-
if (locus) {
|
691
|
-
meeting.locusInfo.handleLocusDelta(locus, meeting);
|
711
|
+
if (response?.body?.locus) {
|
712
|
+
meeting.locusInfo.handleLocusAPIResponse(meeting, response.body);
|
692
713
|
}
|
693
714
|
|
694
715
|
return response;
|
@@ -735,7 +756,7 @@ const MeetingUtil = {
|
|
735
756
|
|
736
757
|
return meeting
|
737
758
|
.request(options)
|
738
|
-
.then((response) => MeetingUtil.
|
759
|
+
.then((response) => MeetingUtil.updateLocusFromApiResponse(meeting, response));
|
739
760
|
};
|
740
761
|
|
741
762
|
return locusDeltaRequest;
|
@@ -371,6 +371,7 @@ export default class MeetingInfoV2 {
|
|
371
371
|
* @param {String} conversationUrl conversationUrl to start adhoc meeting on
|
372
372
|
* @param {String} installedOrgID org ID of user's machine
|
373
373
|
* @param {Boolean} enableStaticMeetingLink whether or not to enable static meeting link
|
374
|
+
* @param {String} classificationId need it to start adhoc meeting if space support classification
|
374
375
|
* @returns {Promise} returns a meeting info object
|
375
376
|
* @public
|
376
377
|
* @memberof MeetingInfo
|
@@ -379,7 +380,8 @@ export default class MeetingInfoV2 {
|
|
379
380
|
conversationUrl: string,
|
380
381
|
installedOrgID?: string,
|
381
382
|
// setting this to true enables static meeting link
|
382
|
-
enableStaticMeetingLink = false
|
383
|
+
enableStaticMeetingLink = false,
|
384
|
+
classificationId = undefined
|
383
385
|
) {
|
384
386
|
const getInvitees = (particpants = []) => {
|
385
387
|
const invitees = [];
|
@@ -407,6 +409,7 @@ export default class MeetingInfoV2 {
|
|
407
409
|
invitees: getInvitees(conversation.participants?.items),
|
408
410
|
installedOrgID,
|
409
411
|
schedule: enableStaticMeetingLink,
|
412
|
+
classificationId,
|
410
413
|
};
|
411
414
|
|
412
415
|
if (installedOrgID) {
|
@@ -429,16 +432,26 @@ export default class MeetingInfoV2 {
|
|
429
432
|
* Creates adhoc space meetings for a space by fetching the conversation infomation
|
430
433
|
* @param {String} conversationUrl conversationUrl to start adhoc meeting on
|
431
434
|
* @param {String} installedOrgID org ID of user's machine
|
435
|
+
* @param {String} classificationId if space is support classification, it needs provide it during start instant meeting
|
432
436
|
* @returns {Promise} returns a meeting info object
|
433
437
|
* @public
|
434
438
|
* @memberof MeetingInfo
|
435
439
|
*/
|
436
|
-
async createAdhocSpaceMeeting(
|
440
|
+
async createAdhocSpaceMeeting(
|
441
|
+
conversationUrl: string,
|
442
|
+
installedOrgID?: string,
|
443
|
+
classificationId?: string
|
444
|
+
) {
|
437
445
|
if (!this.webex.meetings.preferredWebexSite) {
|
438
446
|
throw Error('No preferred webex site found');
|
439
447
|
}
|
440
448
|
|
441
|
-
return this.createAdhocSpaceMeetingOrEnableStaticMeetingLink(
|
449
|
+
return this.createAdhocSpaceMeetingOrEnableStaticMeetingLink(
|
450
|
+
conversationUrl,
|
451
|
+
installedOrgID,
|
452
|
+
false,
|
453
|
+
classificationId
|
454
|
+
)
|
442
455
|
.then((requestResult) => {
|
443
456
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADHOC_MEETING_SUCCESS);
|
444
457
|
|
@@ -618,6 +631,7 @@ export default class MeetingInfoV2 {
|
|
618
631
|
* @param {Object} options
|
619
632
|
* @param {String} registrationId
|
620
633
|
* @param {String} fullSiteUrl
|
634
|
+
* @param {String} classificationId
|
621
635
|
* @returns {Promise} returns a meeting info object
|
622
636
|
* @public
|
623
637
|
* @memberof MeetingInfo
|
@@ -635,7 +649,8 @@ export default class MeetingInfoV2 {
|
|
635
649
|
extraParams: object = {},
|
636
650
|
options: {meetingId?: string; sendCAevents?: boolean} = {},
|
637
651
|
registrationId: string = null,
|
638
|
-
fullSiteUrl: string = null
|
652
|
+
fullSiteUrl: string = null,
|
653
|
+
classificationId: string = null
|
639
654
|
) {
|
640
655
|
const {meetingId, sendCAevents} = options;
|
641
656
|
|
@@ -650,7 +665,11 @@ export default class MeetingInfoV2 {
|
|
650
665
|
this.webex.config.meetings.experimental.enableAdhocMeetings &&
|
651
666
|
this.webex.meetings.preferredWebexSite
|
652
667
|
) {
|
653
|
-
return this.createAdhocSpaceMeeting(
|
668
|
+
return this.createAdhocSpaceMeeting(
|
669
|
+
destinationType.destination,
|
670
|
+
installedOrgID,
|
671
|
+
classificationId
|
672
|
+
);
|
654
673
|
}
|
655
674
|
|
656
675
|
const body = await MeetingInfoUtil.getRequestBody({
|
package/src/meetings/index.ts
CHANGED
@@ -1331,6 +1331,7 @@ export default class Meetings extends WebexPlugin {
|
|
1331
1331
|
* @param {Object} [meetingInfo] - Pre-fetched complete meeting info
|
1332
1332
|
* @param {String} [meetingLookupUrl] - meeting info prefetch url
|
1333
1333
|
* @param {string} sessionCorrelationId - the optional specified sessionCorrelationId (callStateForMetrics.sessionCorrelationId) can be provided instead
|
1334
|
+
* @param {String} classificationId - If space support classification, it will provide it while start instant meeting
|
1334
1335
|
* @returns {Promise<Meeting>} A new Meeting.
|
1335
1336
|
* @public
|
1336
1337
|
* @memberof Meetings
|
@@ -1345,7 +1346,8 @@ export default class Meetings extends WebexPlugin {
|
|
1345
1346
|
callStateForMetrics: CallStateForMetrics = undefined,
|
1346
1347
|
meetingInfo = undefined,
|
1347
1348
|
meetingLookupUrl = undefined,
|
1348
|
-
sessionCorrelationId: string = undefined
|
1349
|
+
sessionCorrelationId: string = undefined,
|
1350
|
+
classificationId: string = undefined
|
1349
1351
|
) {
|
1350
1352
|
// Validate meeting information based on the provided destination and
|
1351
1353
|
// type. This must be performed prior to determining if the meeting is
|
@@ -1415,7 +1417,8 @@ export default class Meetings extends WebexPlugin {
|
|
1415
1417
|
callStateForMetrics,
|
1416
1418
|
failOnMissingMeetingInfo,
|
1417
1419
|
meetingInfo,
|
1418
|
-
meetingLookupUrl
|
1420
|
+
meetingLookupUrl,
|
1421
|
+
classificationId
|
1419
1422
|
).then((createdMeeting: any) => {
|
1420
1423
|
// If the meeting was successfully created.
|
1421
1424
|
if (createdMeeting && createdMeeting.on) {
|
@@ -1529,6 +1532,7 @@ export default class Meetings extends WebexPlugin {
|
|
1529
1532
|
* @param {Boolean} failOnMissingMeetingInfo - whether to throw an error if meeting info fails to fetch (for calls that are not 1:1 or content share)
|
1530
1533
|
* @param {Object} [meetingInfo] - Pre-fetched complete meeting info
|
1531
1534
|
* @param {String} [meetingLookupUrl] - meeting info prefetch url
|
1535
|
+
* @param {String} classificationId see create()
|
1532
1536
|
* @returns {Promise} a new meeting instance complete with meeting info and destination
|
1533
1537
|
* @private
|
1534
1538
|
* @memberof Meetings
|
@@ -1541,7 +1545,8 @@ export default class Meetings extends WebexPlugin {
|
|
1541
1545
|
callStateForMetrics: CallStateForMetrics = undefined,
|
1542
1546
|
failOnMissingMeetingInfo = false,
|
1543
1547
|
meetingInfo = undefined,
|
1544
|
-
meetingLookupUrl = undefined
|
1548
|
+
meetingLookupUrl = undefined,
|
1549
|
+
classificationId = undefined
|
1545
1550
|
) {
|
1546
1551
|
const meeting = new Meeting(
|
1547
1552
|
{
|
@@ -1589,6 +1594,7 @@ export default class Meetings extends WebexPlugin {
|
|
1589
1594
|
// @ts-ignore
|
1590
1595
|
const {enableUnifiedMeetings} = this.config.experimental;
|
1591
1596
|
const meetingInfoOptions = {
|
1597
|
+
classificationId,
|
1592
1598
|
extraParams: infoExtraParams,
|
1593
1599
|
sendCAevents: !!callStateForMetrics?.correlationId, // if client sends correlation id as argument of public create(), then it means that this meeting creation is part of a pre-join intent from user
|
1594
1600
|
};
|
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
|