@webex/plugin-meetings 3.12.0-next.8 → 3.12.0-task-refactor.1
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/annotation/index.js +5 -14
- package/dist/annotation/index.js.map +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/config.js +2 -8
- package/dist/config.js.map +1 -1
- package/dist/constants.js +6 -29
- package/dist/constants.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +29 -1563
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/types.js +3 -13
- package/dist/hashTree/types.js.map +1 -1
- package/dist/index.js +2 -11
- package/dist/index.js.map +1 -1
- package/dist/interceptors/index.js +0 -7
- package/dist/interceptors/index.js.map +1 -1
- package/dist/interceptors/locusRouteToken.js +5 -27
- package/dist/interceptors/locusRouteToken.js.map +1 -1
- package/dist/interpretation/index.js +2 -2
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +3 -7
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +247 -642
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +0 -1
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/locus-info/types.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +1 -57
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/properties.js +2 -4
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +1 -7
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +1036 -1481
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +0 -50
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +3 -133
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +59 -142
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +7 -11
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +0 -10
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +0 -10
- package/dist/member/util.js.map +1 -1
- package/dist/metrics/constants.js +1 -7
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +60 -9
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +0 -11
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +2 -116
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/clusterReachability.js +18 -171
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +11 -21
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/reachabilityPeerConnection.js +1 -1
- package/dist/reachability/reachabilityPeerConnection.js.map +1 -1
- package/dist/reactions/reactions.type.js.map +1 -1
- package/dist/reconnection-manager/index.js +1 -0
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/types/common/browser-detection.d.ts +0 -1
- package/dist/types/common/events/events-scope.d.ts +0 -1
- package/dist/types/common/events/events.d.ts +0 -1
- package/dist/types/config.d.ts +0 -5
- package/dist/types/constants.d.ts +1 -24
- package/dist/types/hashTree/hashTreeParser.d.ts +11 -260
- package/dist/types/hashTree/types.d.ts +0 -20
- package/dist/types/index.d.ts +0 -1
- package/dist/types/interceptors/index.d.ts +1 -2
- package/dist/types/interceptors/locusRouteToken.d.ts +0 -2
- package/dist/types/locus-info/index.d.ts +47 -68
- package/dist/types/locus-info/types.d.ts +12 -28
- package/dist/types/media/MediaConnectionAwaiter.d.ts +1 -10
- package/dist/types/media/properties.d.ts +1 -2
- package/dist/types/meeting/in-meeting-actions.d.ts +0 -6
- package/dist/types/meeting/index.d.ts +7 -86
- package/dist/types/meeting/request.d.ts +1 -16
- package/dist/types/meeting/request.type.d.ts +0 -5
- package/dist/types/meeting/util.d.ts +0 -31
- package/dist/types/meeting-info/util.d.ts +0 -1
- package/dist/types/meeting-info/utilv2.d.ts +0 -1
- package/dist/types/meetings/index.d.ts +2 -4
- package/dist/types/member/index.d.ts +0 -1
- package/dist/types/member/types.d.ts +4 -4
- package/dist/types/member/util.d.ts +0 -5
- package/dist/types/metrics/constants.d.ts +0 -6
- package/dist/types/multistream/mediaRequestManager.d.ts +23 -0
- package/dist/types/multistream/sendSlotManager.d.ts +1 -23
- package/dist/types/reachability/clusterReachability.d.ts +3 -30
- package/dist/types/reactions/reactions.type.d.ts +0 -1
- package/dist/types/recording-controller/util.d.ts +5 -5
- package/dist/types/roap/index.d.ts +1 -1
- package/dist/webinar/index.js +163 -438
- package/dist/webinar/index.js.map +1 -1
- package/package.json +24 -26
- package/src/annotation/index.ts +7 -27
- package/src/config.ts +0 -5
- package/src/constants.ts +1 -30
- package/src/hashTree/hashTreeParser.ts +25 -1523
- package/src/hashTree/types.ts +1 -24
- package/src/index.ts +1 -8
- package/src/interceptors/index.ts +1 -2
- package/src/interceptors/locusRouteToken.ts +5 -22
- package/src/interpretation/index.ts +2 -2
- package/src/locus-info/controlsUtils.ts +0 -17
- package/src/locus-info/index.ts +213 -707
- package/src/locus-info/selfUtils.ts +0 -1
- package/src/locus-info/types.ts +12 -27
- package/src/media/MediaConnectionAwaiter.ts +1 -41
- package/src/media/properties.ts +1 -3
- package/src/meeting/in-meeting-actions.ts +0 -12
- package/src/meeting/index.ts +84 -461
- package/src/meeting/request.ts +0 -42
- package/src/meeting/request.type.ts +0 -6
- package/src/meeting/util.ts +2 -160
- package/src/meetings/index.ts +60 -180
- package/src/meetings/util.ts +9 -10
- package/src/member/index.ts +0 -10
- package/src/member/util.ts +0 -12
- package/src/metrics/constants.ts +0 -7
- package/src/multistream/mediaRequestManager.ts +54 -4
- package/src/multistream/remoteMediaManager.ts +0 -13
- package/src/multistream/sendSlotManager.ts +3 -97
- package/src/reachability/clusterReachability.ts +27 -153
- package/src/reachability/index.ts +1 -15
- package/src/reachability/reachabilityPeerConnection.ts +1 -3
- package/src/reactions/reactions.type.ts +0 -1
- package/src/reconnection-manager/index.ts +1 -0
- package/src/webinar/index.ts +6 -265
- package/test/unit/spec/annotation/index.ts +7 -69
- package/test/unit/spec/interceptors/locusRouteToken.ts +0 -44
- package/test/unit/spec/locus-info/controlsUtils.js +1 -56
- package/test/unit/spec/locus-info/index.js +90 -1457
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +1 -41
- package/test/unit/spec/media/properties.ts +3 -12
- package/test/unit/spec/meeting/in-meeting-actions.ts +2 -8
- package/test/unit/spec/meeting/index.js +128 -981
- package/test/unit/spec/meeting/request.js +0 -70
- package/test/unit/spec/meeting/utils.js +26 -438
- package/test/unit/spec/meetings/index.js +33 -845
- package/test/unit/spec/meetings/utils.js +1 -51
- package/test/unit/spec/member/index.js +4 -28
- package/test/unit/spec/member/util.js +27 -65
- package/test/unit/spec/multistream/mediaRequestManager.ts +85 -2
- package/test/unit/spec/multistream/remoteMediaManager.ts +0 -30
- package/test/unit/spec/multistream/sendSlotManager.ts +36 -135
- package/test/unit/spec/reachability/clusterReachability.ts +1 -125
- package/test/unit/spec/reachability/index.ts +3 -26
- package/test/unit/spec/reconnection-manager/index.js +8 -4
- package/test/unit/spec/webinar/index.ts +37 -534
- package/dist/aiEnableRequest/index.js +0 -184
- package/dist/aiEnableRequest/index.js.map +0 -1
- package/dist/aiEnableRequest/utils.js +0 -36
- package/dist/aiEnableRequest/utils.js.map +0 -1
- package/dist/hashTree/constants.js +0 -22
- package/dist/hashTree/constants.js.map +0 -1
- package/dist/hashTree/hashTree.js +0 -533
- package/dist/hashTree/hashTree.js.map +0 -1
- package/dist/hashTree/utils.js +0 -69
- package/dist/hashTree/utils.js.map +0 -1
- package/dist/interceptors/constant.js +0 -12
- package/dist/interceptors/constant.js.map +0 -1
- package/dist/interceptors/dataChannelAuthToken.js +0 -290
- package/dist/interceptors/dataChannelAuthToken.js.map +0 -1
- package/dist/interceptors/utils.js +0 -27
- package/dist/interceptors/utils.js.map +0 -1
- package/dist/types/aiEnableRequest/index.d.ts +0 -5
- package/dist/types/aiEnableRequest/utils.d.ts +0 -2
- package/dist/types/hashTree/constants.d.ts +0 -9
- package/dist/types/hashTree/hashTree.d.ts +0 -136
- package/dist/types/hashTree/utils.d.ts +0 -22
- package/dist/types/interceptors/constant.d.ts +0 -5
- package/dist/types/interceptors/dataChannelAuthToken.d.ts +0 -43
- package/dist/types/interceptors/utils.d.ts +0 -1
- package/dist/types/webinar/utils.d.ts +0 -6
- package/dist/webinar/utils.js +0 -25
- package/dist/webinar/utils.js.map +0 -1
- package/src/aiEnableRequest/README.md +0 -84
- package/src/aiEnableRequest/index.ts +0 -170
- package/src/aiEnableRequest/utils.ts +0 -25
- package/src/hashTree/constants.ts +0 -10
- package/src/hashTree/hashTree.ts +0 -480
- package/src/hashTree/utils.ts +0 -62
- package/src/interceptors/constant.ts +0 -6
- package/src/interceptors/dataChannelAuthToken.ts +0 -170
- package/src/interceptors/utils.ts +0 -16
- package/src/webinar/utils.ts +0 -16
- package/test/unit/spec/aiEnableRequest/index.ts +0 -981
- package/test/unit/spec/aiEnableRequest/utils.ts +0 -130
- package/test/unit/spec/hashTree/hashTree.ts +0 -721
- package/test/unit/spec/hashTree/hashTreeParser.ts +0 -3670
- package/test/unit/spec/hashTree/utils.ts +0 -140
- package/test/unit/spec/interceptors/dataChannelAuthToken.ts +0 -210
- package/test/unit/spec/interceptors/utils.ts +0 -75
- package/test/unit/spec/webinar/utils.ts +0 -39
package/src/locus-info/index.ts
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
CALL_REMOVED_REASON,
|
|
19
19
|
RECORDING_STATE,
|
|
20
20
|
Enum,
|
|
21
|
-
SELF_ROLES,
|
|
22
21
|
} from '../constants';
|
|
23
22
|
|
|
24
23
|
import InfoUtils from './infoUtils';
|
|
@@ -34,15 +33,12 @@ import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
|
34
33
|
import HashTreeParser, {
|
|
35
34
|
DataSet,
|
|
36
35
|
HashTreeMessage,
|
|
36
|
+
HashTreeObject,
|
|
37
|
+
isSelf,
|
|
37
38
|
LocusInfoUpdateType,
|
|
38
|
-
Metadata,
|
|
39
39
|
} from '../hashTree/hashTreeParser';
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import {Links, LocusDTO, ReplacesInfo} from './types';
|
|
43
|
-
import MeetingsUtil from '../meetings/util';
|
|
44
|
-
import {MEETING_KEY} from '../meetings/meetings.types';
|
|
45
|
-
import MeetingCollection from '../meetings/collection';
|
|
40
|
+
import {ObjectType} from '../hashTree/types';
|
|
41
|
+
import {LocusDTO} from './types';
|
|
46
42
|
|
|
47
43
|
export type LocusLLMEvent = {
|
|
48
44
|
data: {
|
|
@@ -51,12 +47,9 @@ export type LocusLLMEvent = {
|
|
|
51
47
|
};
|
|
52
48
|
};
|
|
53
49
|
|
|
54
|
-
// list of top level keys in Locus DTO relevant for Hash Tree DTOs processing
|
|
55
|
-
// it does not contain fields specific to classic Locus DTOs like sequence or baseSequence
|
|
56
50
|
const LocusDtoTopLevelKeys = [
|
|
57
51
|
'controls',
|
|
58
52
|
'fullState',
|
|
59
|
-
'embeddedApps',
|
|
60
53
|
'host',
|
|
61
54
|
'info',
|
|
62
55
|
'links',
|
|
@@ -71,13 +64,10 @@ const LocusDtoTopLevelKeys = [
|
|
|
71
64
|
'htMeta', // only exists when hash trees are used
|
|
72
65
|
];
|
|
73
66
|
|
|
74
|
-
export type LocusApiResponseBody =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
metadata?: Metadata;
|
|
79
|
-
}
|
|
80
|
-
| LocusDTO; // when we invoke APIs on the whole Locus like "mute all" backend returns the whole Locus in the response like this
|
|
67
|
+
export type LocusApiResponseBody = {
|
|
68
|
+
dataSets?: DataSet[];
|
|
69
|
+
locus: LocusDTO; // this LocusDTO here might not be the full one (for example it won't have all the participants, but it should have self)
|
|
70
|
+
};
|
|
81
71
|
|
|
82
72
|
const LocusObjectStateAfterUpdates = {
|
|
83
73
|
unchanged: 'unchanged',
|
|
@@ -87,167 +77,6 @@ const LocusObjectStateAfterUpdates = {
|
|
|
87
77
|
|
|
88
78
|
type LocusObjectStateAfterUpdates = Enum<typeof LocusObjectStateAfterUpdates>;
|
|
89
79
|
|
|
90
|
-
export type HashTreeParserEntry = {
|
|
91
|
-
parser: HashTreeParser;
|
|
92
|
-
replacedAt?: string;
|
|
93
|
-
initializedFromHashTree: boolean;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Gets the replacement information
|
|
98
|
-
*
|
|
99
|
-
* @param {any} self - "self" object from Locus DTO
|
|
100
|
-
* @param {string} deviceUrl - The URL of the user's device
|
|
101
|
-
* @returns {any} The replace information if available, otherwise undefined
|
|
102
|
-
*/
|
|
103
|
-
function getReplaceInfoFromSelf(self: any, deviceUrl: string): ReplacesInfo | undefined {
|
|
104
|
-
if (self) {
|
|
105
|
-
const device = MeetingsUtil.getThisDevice({self}, deviceUrl);
|
|
106
|
-
|
|
107
|
-
if (device?.replaces?.length > 0) {
|
|
108
|
-
return device.replaces[0];
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return undefined;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Finds a meeting by its locus URL in meeting collection. It checks all HashTreeParsers of all meetings in the collection.
|
|
117
|
-
*
|
|
118
|
-
* @param {MeetingCollection} meetingCollection - The collection of meetings to search
|
|
119
|
-
* @param {string} locusUrl - The locus URL to search for
|
|
120
|
-
* @returns {any} The meeting if found, otherwise undefined
|
|
121
|
-
*/
|
|
122
|
-
function findLocusUrlInAnyHashTreeParser(
|
|
123
|
-
meetingCollection: MeetingCollection,
|
|
124
|
-
locusUrl: string
|
|
125
|
-
): any {
|
|
126
|
-
for (const meeting of Object.values(meetingCollection.getAll()) as any[]) {
|
|
127
|
-
if (meeting?.locusInfo?.hashTreeParsers?.has(locusUrl)) {
|
|
128
|
-
return meeting;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return undefined;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Finds a meeting for a given hash tree message.
|
|
137
|
-
*
|
|
138
|
-
* @param {HashTreeMessage} message - The hash tree message to find the meeting for
|
|
139
|
-
* @param {MeetingCollection} meetingCollection - The collection of meetings to search
|
|
140
|
-
* @param {string} deviceUrl - The URL of the user's device
|
|
141
|
-
* @returns {any} The meeting if found, otherwise undefined
|
|
142
|
-
*/
|
|
143
|
-
export function findMeetingForHashTreeMessage(
|
|
144
|
-
message: HashTreeMessage,
|
|
145
|
-
meetingCollection: MeetingCollection,
|
|
146
|
-
deviceUrl: string
|
|
147
|
-
): any {
|
|
148
|
-
let foundMeeting = findLocusUrlInAnyHashTreeParser(meetingCollection, message.locusUrl);
|
|
149
|
-
|
|
150
|
-
if (foundMeeting) {
|
|
151
|
-
return foundMeeting;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// if we haven't found anything, it may mean that message has a new locusUrl
|
|
155
|
-
// check if it indicates that it replaces some existing current locusUrl (this is indicated in "self")
|
|
156
|
-
const self = message.locusStateElements?.find((el) => isSelf(el))?.data;
|
|
157
|
-
const replaces = getReplaceInfoFromSelf(self, deviceUrl);
|
|
158
|
-
|
|
159
|
-
if (replaces?.locusUrl) {
|
|
160
|
-
foundMeeting = findLocusUrlInAnyHashTreeParser(meetingCollection, replaces.locusUrl);
|
|
161
|
-
|
|
162
|
-
return foundMeeting;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return undefined;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Creates a locus object from the objects received in a hash tree message. It usually will be
|
|
170
|
-
* incomplete, because hash tree messages only contain the parts of locus that have changed,
|
|
171
|
-
* and some updates come separately over Mercury or LLM in separate messages.
|
|
172
|
-
*
|
|
173
|
-
* @param {HashTreeMessage} message hash tree message to created the locus from
|
|
174
|
-
* @returns {Object} the created locus object and metadata if present
|
|
175
|
-
*/
|
|
176
|
-
export function createLocusFromHashTreeMessage(message: HashTreeMessage): {
|
|
177
|
-
locus: LocusDTO;
|
|
178
|
-
metadata?: Metadata;
|
|
179
|
-
} {
|
|
180
|
-
const locus: LocusDTO = {
|
|
181
|
-
participants: [],
|
|
182
|
-
url: message.locusUrl,
|
|
183
|
-
};
|
|
184
|
-
let metadata: Metadata | undefined;
|
|
185
|
-
|
|
186
|
-
if (!message.locusStateElements) {
|
|
187
|
-
return {locus, metadata};
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
for (const element of message.locusStateElements) {
|
|
191
|
-
if (!element.data) {
|
|
192
|
-
// eslint-disable-next-line no-continue
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const type = element.htMeta.elementId.type.toLowerCase();
|
|
197
|
-
|
|
198
|
-
switch (type) {
|
|
199
|
-
case ObjectType.locus: {
|
|
200
|
-
// spread locus object data onto the top level, but remove keys managed by other ObjectTypes
|
|
201
|
-
const locusObjectData = {...element.data};
|
|
202
|
-
|
|
203
|
-
Object.values(ObjectTypeToLocusKeyMap).forEach((locusDtoKey) => {
|
|
204
|
-
delete locusObjectData[locusDtoKey];
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
Object.assign(locus, locusObjectData);
|
|
208
|
-
break;
|
|
209
|
-
}
|
|
210
|
-
case ObjectType.participant:
|
|
211
|
-
locus.participants.push(element.data);
|
|
212
|
-
break;
|
|
213
|
-
case ObjectType.mediaShare:
|
|
214
|
-
if (!locus.mediaShares) {
|
|
215
|
-
locus.mediaShares = [];
|
|
216
|
-
}
|
|
217
|
-
locus.mediaShares.push(element.data);
|
|
218
|
-
break;
|
|
219
|
-
case ObjectType.embeddedApp:
|
|
220
|
-
if (!locus.embeddedApps) {
|
|
221
|
-
locus.embeddedApps = [];
|
|
222
|
-
}
|
|
223
|
-
locus.embeddedApps.push(element.data);
|
|
224
|
-
break;
|
|
225
|
-
case ObjectType.control:
|
|
226
|
-
if (!locus.controls) {
|
|
227
|
-
locus.controls = {};
|
|
228
|
-
}
|
|
229
|
-
Object.assign(locus.controls, element.data);
|
|
230
|
-
break;
|
|
231
|
-
case ObjectType.links:
|
|
232
|
-
case ObjectType.info:
|
|
233
|
-
case ObjectType.fullState:
|
|
234
|
-
case ObjectType.self: {
|
|
235
|
-
const locusDtoKey = ObjectTypeToLocusKeyMap[type];
|
|
236
|
-
locus[locusDtoKey] = element.data;
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
case ObjectType.metadata:
|
|
240
|
-
// metadata is not part of Locus DTO
|
|
241
|
-
metadata = {...element.data, htMeta: element.htMeta} as Metadata;
|
|
242
|
-
break;
|
|
243
|
-
default:
|
|
244
|
-
break;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return {locus, metadata};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
80
|
/**
|
|
252
81
|
* @description LocusInfo extends ChildEmitter to convert locusInfo info a private emitter to parent object
|
|
253
82
|
* @export
|
|
@@ -265,7 +94,10 @@ export default class LocusInfo extends EventsScope {
|
|
|
265
94
|
aclUrl: any;
|
|
266
95
|
baseSequence: any;
|
|
267
96
|
created: any;
|
|
97
|
+
identities: any;
|
|
98
|
+
membership: any;
|
|
268
99
|
participants: any;
|
|
100
|
+
participantsUrl: any;
|
|
269
101
|
replaces: any;
|
|
270
102
|
scheduledMeeting: any;
|
|
271
103
|
sequence: any;
|
|
@@ -277,11 +109,13 @@ export default class LocusInfo extends EventsScope {
|
|
|
277
109
|
info: any;
|
|
278
110
|
roles: any;
|
|
279
111
|
mediaShares: any;
|
|
112
|
+
replace: any;
|
|
280
113
|
url: any;
|
|
281
|
-
|
|
114
|
+
services: any;
|
|
115
|
+
resources: any;
|
|
282
116
|
mainSessionLocusCache: any;
|
|
283
117
|
self: any;
|
|
284
|
-
|
|
118
|
+
hashTreeParser?: HashTreeParser;
|
|
285
119
|
hashTreeObjectId2ParticipantId: Map<number, string>; // mapping of hash tree object ids to participant ids
|
|
286
120
|
classicVsHashTreeMismatchMetricCounter = 0;
|
|
287
121
|
|
|
@@ -303,7 +137,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
303
137
|
this.meetingId = meetingId;
|
|
304
138
|
this.updateMeeting = updateMeeting;
|
|
305
139
|
this.locusParser = new LocusDeltaParser();
|
|
306
|
-
this.hashTreeParsers = new Map();
|
|
307
140
|
this.hashTreeObjectId2ParticipantId = new Map();
|
|
308
141
|
}
|
|
309
142
|
|
|
@@ -409,7 +242,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
409
242
|
'Locus-info:index#doLocusSync --> got full DTO when we asked for delta'
|
|
410
243
|
);
|
|
411
244
|
}
|
|
412
|
-
meeting.locusInfo.onFullLocus(
|
|
245
|
+
meeting.locusInfo.onFullLocus(res.body);
|
|
413
246
|
})
|
|
414
247
|
.catch((e) => {
|
|
415
248
|
LoggerProxy.logger.info(
|
|
@@ -493,10 +326,13 @@ export default class LocusInfo extends EventsScope {
|
|
|
493
326
|
init(locus: any = {}) {
|
|
494
327
|
this.created = locus.created || null;
|
|
495
328
|
this.scheduledMeeting = locus.meeting || null;
|
|
329
|
+
this.participantsUrl = locus.participantsUrl || null;
|
|
496
330
|
this.replaces = locus.replaces || null;
|
|
497
331
|
this.aclUrl = locus.aclUrl || null;
|
|
498
332
|
this.baseSequence = locus.baseSequence || null;
|
|
499
333
|
this.sequence = locus.sequence || null;
|
|
334
|
+
this.membership = locus.membership || null;
|
|
335
|
+
this.identities = locus.identities || null;
|
|
500
336
|
this.participants = locus.participants || null;
|
|
501
337
|
|
|
502
338
|
/**
|
|
@@ -522,60 +358,29 @@ export default class LocusInfo extends EventsScope {
|
|
|
522
358
|
this.updateSelf(locus.self);
|
|
523
359
|
this.updateHostInfo(locus.host);
|
|
524
360
|
this.updateMediaShares(locus.mediaShares);
|
|
525
|
-
this.
|
|
361
|
+
this.updateServices(locus.links?.services);
|
|
362
|
+
this.updateResources(locus.links?.resources);
|
|
526
363
|
}
|
|
527
364
|
|
|
528
365
|
/**
|
|
529
|
-
* Creates
|
|
530
|
-
* @param {Object}
|
|
531
|
-
* @
|
|
532
|
-
* @param {Object} params.initialLocus - initial locus data
|
|
533
|
-
* @param {Object} params.metadata - hash tree metadata
|
|
534
|
-
* @param {string} params.replacedAt - timestamp from Locus indicating when the replacement happened
|
|
535
|
-
* @returns {HashTreeParser} the newly created parser
|
|
366
|
+
* Creates the HashTreeParser instance.
|
|
367
|
+
* @param {Object} initial locus data
|
|
368
|
+
* @returns {void}
|
|
536
369
|
*/
|
|
537
370
|
private createHashTreeParser({
|
|
538
|
-
locusUrl,
|
|
539
371
|
initialLocus,
|
|
540
|
-
metadata,
|
|
541
|
-
replacedAt,
|
|
542
372
|
}: {
|
|
543
|
-
locusUrl: string;
|
|
544
373
|
initialLocus: {
|
|
545
374
|
dataSets: Array<DataSet>;
|
|
546
375
|
locus: any;
|
|
547
376
|
};
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
}): HashTreeParser {
|
|
551
|
-
const parser = new HashTreeParser({
|
|
377
|
+
}) {
|
|
378
|
+
return new HashTreeParser({
|
|
552
379
|
initialLocus,
|
|
553
|
-
metadata,
|
|
554
380
|
webexRequest: this.webex.request.bind(this.webex),
|
|
555
|
-
locusInfoUpdateCallback: this.updateFromHashTree.bind(this
|
|
556
|
-
debugId: `HT-${
|
|
557
|
-
excludedDataSets: this.webex.config.meetings.locus?.excludedDataSets,
|
|
381
|
+
locusInfoUpdateCallback: this.updateFromHashTree.bind(this),
|
|
382
|
+
debugId: `HT-${this.meetingId.substring(0, 4)}`,
|
|
558
383
|
});
|
|
559
|
-
|
|
560
|
-
// When a new HashTreeParser is created, previous one should be stopped.
|
|
561
|
-
// Locus will only be sending us updates for the current one.
|
|
562
|
-
for (const [existingLocusUrl, existingEntry] of this.hashTreeParsers) {
|
|
563
|
-
if (existingEntry.parser.state !== 'stopped') {
|
|
564
|
-
existingEntry.parser.stop();
|
|
565
|
-
if (replacedAt) {
|
|
566
|
-
existingEntry.replacedAt = replacedAt;
|
|
567
|
-
} else {
|
|
568
|
-
LoggerProxy.logger.warn(
|
|
569
|
-
`Locus-info:index#createHashTreeParser --> no replacedAt timestamp provided for new HashTreeParser with locusUrl ${locusUrl}, replacing ${existingLocusUrl}`
|
|
570
|
-
);
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
this.hashTreeParsers.set(locusUrl, {parser, initializedFromHashTree: false});
|
|
576
|
-
this.hashTreeObjectId2ParticipantId.clear();
|
|
577
|
-
|
|
578
|
-
return parser;
|
|
579
384
|
}
|
|
580
385
|
|
|
581
386
|
/**
|
|
@@ -589,7 +394,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
589
394
|
trigger: 'join-response';
|
|
590
395
|
locus: LocusDTO;
|
|
591
396
|
dataSets?: DataSet[];
|
|
592
|
-
metadata?: Metadata;
|
|
593
397
|
}
|
|
594
398
|
| {
|
|
595
399
|
trigger: 'locus-message';
|
|
@@ -604,50 +408,41 @@ export default class LocusInfo extends EventsScope {
|
|
|
604
408
|
switch (data.trigger) {
|
|
605
409
|
case 'locus-message':
|
|
606
410
|
if (data.hashTreeMessage) {
|
|
607
|
-
// we need the
|
|
411
|
+
// we need the SELF object to be in the received message, because it contains visibleDataSets
|
|
608
412
|
// and these are needed to initialize all the hash trees
|
|
609
|
-
const
|
|
610
|
-
isMetadata(el)
|
|
611
|
-
);
|
|
413
|
+
const selfObject = data.hashTreeMessage.locusStateElements?.find((el) => isSelf(el));
|
|
612
414
|
|
|
613
|
-
if (!
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
LoggerProxy.logger.info(
|
|
617
|
-
`Locus-info:index#initialSetup --> cannot initialize HashTreeParser, Metadata object with visibleDataSets is missing in the message`
|
|
415
|
+
if (!selfObject?.data?.visibleDataSets) {
|
|
416
|
+
LoggerProxy.logger.warn(
|
|
417
|
+
`Locus-info:index#initialSetup --> cannot initialize HashTreeParser, SELF object with visibleDataSets is missing in the message`
|
|
618
418
|
);
|
|
619
419
|
|
|
620
|
-
|
|
621
|
-
throw new Error('Metadata object with visibleDataSets is missing in the message');
|
|
420
|
+
throw new Error('SELF object with visibleDataSets is missing in the message');
|
|
622
421
|
}
|
|
623
422
|
|
|
624
423
|
LoggerProxy.logger.info(
|
|
625
424
|
'Locus-info:index#initialSetup --> creating HashTreeParser from message'
|
|
626
425
|
);
|
|
627
426
|
// first create the HashTreeParser, but don't initialize it with any data yet
|
|
628
|
-
|
|
629
|
-
|
|
427
|
+
// pass just a fake locus that contains only the visibleDataSets
|
|
428
|
+
this.hashTreeParser = this.createHashTreeParser({
|
|
630
429
|
initialLocus: {
|
|
631
|
-
locus:
|
|
632
|
-
dataSets: data.hashTreeMessage.dataSets,
|
|
633
|
-
},
|
|
634
|
-
metadata: {
|
|
635
|
-
htMeta: metadataObject.htMeta,
|
|
636
|
-
visibleDataSets: metadataObject.data.visibleDataSets,
|
|
430
|
+
locus: {self: {visibleDataSets: selfObject.data.visibleDataSets}},
|
|
431
|
+
dataSets: [], // empty, because they will be populated in initializeFromMessage() call // dataSets: data.hashTreeMessage.dataSets,
|
|
637
432
|
},
|
|
638
433
|
});
|
|
639
434
|
|
|
640
435
|
// now handle the message - that should populate all the visible datasets
|
|
641
|
-
await hashTreeParser.initializeFromMessage(data.hashTreeMessage);
|
|
436
|
+
await this.hashTreeParser.initializeFromMessage(data.hashTreeMessage);
|
|
642
437
|
} else {
|
|
643
438
|
// "classic" Locus case, no hash trees involved
|
|
644
439
|
this.updateLocusCache(data.locus);
|
|
645
|
-
this.onFullLocus(
|
|
440
|
+
this.onFullLocus(data.locus, undefined);
|
|
646
441
|
}
|
|
647
442
|
break;
|
|
648
443
|
case 'join-response':
|
|
649
444
|
this.updateLocusCache(data.locus);
|
|
650
|
-
this.onFullLocus(
|
|
445
|
+
this.onFullLocus(data.locus, undefined, data.dataSets);
|
|
651
446
|
break;
|
|
652
447
|
case 'get-loci-response':
|
|
653
448
|
if (data.locus?.links?.resources?.visibleDataSets?.url) {
|
|
@@ -655,21 +450,20 @@ export default class LocusInfo extends EventsScope {
|
|
|
655
450
|
'Locus-info:index#initialSetup --> creating HashTreeParser from get-loci-response'
|
|
656
451
|
);
|
|
657
452
|
// first create the HashTreeParser, but don't initialize it with any data yet
|
|
658
|
-
|
|
659
|
-
|
|
453
|
+
// pass just a fake locus that contains only the visibleDataSets
|
|
454
|
+
this.hashTreeParser = this.createHashTreeParser({
|
|
660
455
|
initialLocus: {
|
|
661
|
-
locus:
|
|
456
|
+
locus: {self: {visibleDataSets: data.locus?.self?.visibleDataSets}},
|
|
662
457
|
dataSets: [], // empty, because we don't have them yet
|
|
663
458
|
},
|
|
664
|
-
metadata: null, // get-loci-response doesn't contain Metadata object
|
|
665
459
|
});
|
|
666
460
|
|
|
667
461
|
// now initialize all the data
|
|
668
|
-
await hashTreeParser.initializeFromGetLociResponse(data.locus);
|
|
462
|
+
await this.hashTreeParser.initializeFromGetLociResponse(data.locus);
|
|
669
463
|
} else {
|
|
670
464
|
// "classic" Locus case, no hash trees involved
|
|
671
465
|
this.updateLocusCache(data.locus);
|
|
672
|
-
this.onFullLocus(
|
|
466
|
+
this.onFullLocus(data.locus, undefined);
|
|
673
467
|
}
|
|
674
468
|
}
|
|
675
469
|
// Change it to true after it receives it first locus object
|
|
@@ -683,42 +477,27 @@ export default class LocusInfo extends EventsScope {
|
|
|
683
477
|
* @returns {void}
|
|
684
478
|
*/
|
|
685
479
|
handleLocusAPIResponse(meeting, responseBody: LocusApiResponseBody): void {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
meeting,
|
|
694
|
-
`expected hash tree dataSets in API response but they are missing`
|
|
695
|
-
);
|
|
696
|
-
// continuing as we can still manage without responseBody.dataSets, but this is very suspicious
|
|
697
|
-
}
|
|
698
|
-
LoggerProxy.logger.info(
|
|
699
|
-
'Locus-info:index#handleLocusAPIResponse --> passing Locus API response to HashTreeParser: ',
|
|
700
|
-
responseBody
|
|
701
|
-
);
|
|
702
|
-
// update the data in our hash trees
|
|
703
|
-
hashTreeParserEntry.parser.handleLocusUpdate(responseBody);
|
|
704
|
-
} else {
|
|
705
|
-
// LocusDTO without wrapper - pass it through as if it had no dataSets
|
|
706
|
-
hashTreeParserEntry.parser.handleLocusUpdate({locus: responseBody});
|
|
707
|
-
}
|
|
480
|
+
if (this.hashTreeParser) {
|
|
481
|
+
// API responses with hash tree are a bit problematic and not fully confirmed how they will look like
|
|
482
|
+
// we don't really need them, because all updates are guaranteed to come via Mercury or LLM messages anyway
|
|
483
|
+
// so it's OK to skip them for now
|
|
484
|
+
LoggerProxy.logger.info(
|
|
485
|
+
'Locus-info:index#handleLocusAPIResponse: skipping handling of API http response with hashTreeParser'
|
|
486
|
+
);
|
|
708
487
|
} else {
|
|
709
|
-
if (
|
|
488
|
+
if (responseBody.dataSets) {
|
|
710
489
|
this.sendClassicVsHashTreeMismatchMetric(
|
|
711
490
|
meeting,
|
|
712
491
|
`unexpected hash tree dataSets in API response`
|
|
713
492
|
);
|
|
714
493
|
}
|
|
715
494
|
// classic Locus delta
|
|
716
|
-
|
|
717
|
-
this.handleLocusDelta(locus, meeting);
|
|
495
|
+
this.handleLocusDelta(responseBody.locus, meeting);
|
|
718
496
|
}
|
|
719
497
|
}
|
|
720
498
|
|
|
721
499
|
/**
|
|
500
|
+
*
|
|
722
501
|
* @param {HashTreeObject} object data set object
|
|
723
502
|
* @param {any} locus
|
|
724
503
|
* @returns {void}
|
|
@@ -726,39 +505,29 @@ export default class LocusInfo extends EventsScope {
|
|
|
726
505
|
updateLocusFromHashTreeObject(object: HashTreeObject, locus: LocusDTO): LocusDTO {
|
|
727
506
|
const type = object.htMeta.elementId.type.toLowerCase();
|
|
728
507
|
|
|
729
|
-
const addParticipantObject = (obj: HashTreeObject) => {
|
|
730
|
-
if (!locus.participants) {
|
|
731
|
-
locus.participants = [];
|
|
732
|
-
}
|
|
733
|
-
locus.participants.push(obj.data);
|
|
734
|
-
this.hashTreeObjectId2ParticipantId.set(obj.htMeta.elementId.id, obj.data.id);
|
|
735
|
-
};
|
|
736
|
-
|
|
737
508
|
switch (type) {
|
|
738
509
|
case ObjectType.locus: {
|
|
739
510
|
if (!object.data) {
|
|
740
511
|
// not doing anything here, as we need Locus to always be there (at least some fields)
|
|
741
512
|
// and that's already taken care of in updateFromHashTree()
|
|
742
513
|
LoggerProxy.logger.info(
|
|
743
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object removed
|
|
514
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object removed`
|
|
744
515
|
);
|
|
745
516
|
|
|
746
517
|
return locus;
|
|
747
518
|
}
|
|
748
519
|
// replace the main locus
|
|
749
520
|
|
|
750
|
-
// The Locus object we receive from backend has empty participants
|
|
751
|
-
//
|
|
752
|
-
//
|
|
521
|
+
// The Locus object we receive from backend has empty participants, so removing them to avoid it overriding the ones in our current locus object
|
|
522
|
+
// Also, other fields like mediaShares are managed by other ObjectType updates, so removing them too
|
|
523
|
+
// BTW, it also doesn't have "self". That's OK as it won't override existing locus.self and also existing SDK code can handle that missing self in Locus updates
|
|
753
524
|
const locusObjectFromData = object.data;
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
delete locusObjectFromData[locusDtoKey];
|
|
757
|
-
});
|
|
525
|
+
delete locusObjectFromData.participants;
|
|
526
|
+
delete locusObjectFromData.mediaShares;
|
|
758
527
|
|
|
759
528
|
locus = {...locus, ...locusObjectFromData};
|
|
760
529
|
LoggerProxy.logger.info(
|
|
761
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object updated
|
|
530
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object updated`
|
|
762
531
|
);
|
|
763
532
|
break;
|
|
764
533
|
}
|
|
@@ -771,7 +540,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
771
540
|
object.data.name === 'content'
|
|
772
541
|
? `floor=${object.data.floor?.disposition}, ${object.data.floor?.beneficiary?.id}`
|
|
773
542
|
: ''
|
|
774
|
-
}
|
|
543
|
+
}`
|
|
775
544
|
);
|
|
776
545
|
const existingMediaShare = locus.mediaShares?.find(
|
|
777
546
|
(ms) => ms.htMeta.elementId.id === object.htMeta.elementId.id
|
|
@@ -785,46 +554,27 @@ export default class LocusInfo extends EventsScope {
|
|
|
785
554
|
}
|
|
786
555
|
} else {
|
|
787
556
|
LoggerProxy.logger.info(
|
|
788
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> mediaShare id=${object.htMeta.elementId.id} removed
|
|
557
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> mediaShare id=${object.htMeta.elementId.id} removed`
|
|
789
558
|
);
|
|
790
559
|
locus.mediaShares = locus.mediaShares?.filter(
|
|
791
560
|
(ms) => ms.htMeta.elementId.id !== object.htMeta.elementId.id
|
|
792
561
|
);
|
|
793
562
|
}
|
|
794
563
|
break;
|
|
795
|
-
case ObjectType.embeddedApp:
|
|
796
|
-
if (object.data) {
|
|
797
|
-
LoggerProxy.logger.info(
|
|
798
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> embeddedApp id=${object.htMeta.elementId.id} url='${object.data.url}' updated version=${object.htMeta.elementId.version}:`,
|
|
799
|
-
object.data
|
|
800
|
-
);
|
|
801
|
-
const existingEmbeddedApp = locus.embeddedApps?.find(
|
|
802
|
-
(ms) => ms.htMeta.elementId.id === object.htMeta.elementId.id
|
|
803
|
-
);
|
|
804
|
-
|
|
805
|
-
if (existingEmbeddedApp) {
|
|
806
|
-
Object.assign(existingEmbeddedApp, object.data);
|
|
807
|
-
} else {
|
|
808
|
-
locus.embeddedApps = locus.embeddedApps || [];
|
|
809
|
-
locus.embeddedApps.push(object.data);
|
|
810
|
-
}
|
|
811
|
-
} else {
|
|
812
|
-
LoggerProxy.logger.info(
|
|
813
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> embeddedApp id=${object.htMeta.elementId.id} removed, version=${object.htMeta.elementId.version}`
|
|
814
|
-
);
|
|
815
|
-
locus.embeddedApps = locus.embeddedApps?.filter(
|
|
816
|
-
(ms) => ms.htMeta.elementId.id !== object.htMeta.elementId.id
|
|
817
|
-
);
|
|
818
|
-
}
|
|
819
|
-
break;
|
|
820
564
|
case ObjectType.participant:
|
|
821
565
|
LoggerProxy.logger.info(
|
|
822
566
|
`Locus-info:index#updateLocusFromHashTreeObject --> participant id=${
|
|
823
567
|
object.htMeta.elementId.id
|
|
824
|
-
} ${object.data ? 'updated' : 'removed'}
|
|
568
|
+
} ${object.data ? 'updated' : 'removed'}`
|
|
825
569
|
);
|
|
826
570
|
if (object.data) {
|
|
827
|
-
|
|
571
|
+
if (!locus.participants) {
|
|
572
|
+
locus.participants = [];
|
|
573
|
+
}
|
|
574
|
+
const participantObject = object.data;
|
|
575
|
+
participantObject.htMeta = object.htMeta;
|
|
576
|
+
locus.participants.push(participantObject);
|
|
577
|
+
this.hashTreeObjectId2ParticipantId.set(object.htMeta.elementId.id, participantObject.id);
|
|
828
578
|
} else {
|
|
829
579
|
const participantId = this.hashTreeObjectId2ParticipantId.get(object.htMeta.elementId.id);
|
|
830
580
|
|
|
@@ -834,83 +584,20 @@ export default class LocusInfo extends EventsScope {
|
|
|
834
584
|
locus.jsSdkMeta.removedParticipantIds.push(participantId);
|
|
835
585
|
this.hashTreeObjectId2ParticipantId.delete(object.htMeta.elementId.id);
|
|
836
586
|
}
|
|
837
|
-
// Create self from the participant if it matches self identity and is being moved.
|
|
838
|
-
// We need this, because participant update comes in LLM message often before the self update from Mercury.
|
|
839
|
-
// Other parts of the code detect move only by looking at self, while some other parts of the SDK/webapp code
|
|
840
|
-
// look at participant for roles etc, so if participant is updated but not self, then it looks like we our lost roles temporarily
|
|
841
|
-
// (until self is updated)
|
|
842
|
-
// This will be fixed properly in SPARK-790239
|
|
843
|
-
if (
|
|
844
|
-
object.data &&
|
|
845
|
-
object.data.identity === locus.self?.identity &&
|
|
846
|
-
object.data.state === 'LEFT' &&
|
|
847
|
-
object.data.reason === 'MOVED'
|
|
848
|
-
) {
|
|
849
|
-
LoggerProxy.logger.info(
|
|
850
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> FOUND a match for MOVED self in participant object ${object.htMeta.elementId.id}`
|
|
851
|
-
);
|
|
852
|
-
Object.assign(locus[ObjectTypeToLocusKeyMap[ObjectType.self]], object.data);
|
|
853
|
-
}
|
|
854
587
|
break;
|
|
855
|
-
case ObjectType.control:
|
|
856
|
-
if (object.data) {
|
|
857
|
-
Object.keys(object.data).forEach((controlKey) => {
|
|
858
|
-
LoggerProxy.logger.info(
|
|
859
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> control ${controlKey} updated:`,
|
|
860
|
-
object.data[controlKey]
|
|
861
|
-
);
|
|
862
|
-
if (!locus.controls) {
|
|
863
|
-
locus.controls = {};
|
|
864
|
-
}
|
|
865
|
-
locus.controls[controlKey] = object.data[controlKey];
|
|
866
|
-
});
|
|
867
|
-
} else {
|
|
868
|
-
LoggerProxy.logger.warn(
|
|
869
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> control object update without data - this is not expected!`
|
|
870
|
-
);
|
|
871
|
-
}
|
|
872
|
-
break;
|
|
873
|
-
case ObjectType.links:
|
|
874
|
-
case ObjectType.info:
|
|
875
|
-
case ObjectType.fullState:
|
|
876
588
|
case ObjectType.self:
|
|
877
589
|
if (!object.data) {
|
|
878
590
|
// self without data is handled inside HashTreeParser and results in LocusInfoUpdateType.MEETING_ENDED, so we should never get here
|
|
879
|
-
// all other types info, fullstate, etc - Locus should never send them without data
|
|
880
591
|
LoggerProxy.logger.warn(
|
|
881
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> received
|
|
592
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> received SELF object without data, this is not expected!`
|
|
882
593
|
);
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> ${type} object updated to version ${object.htMeta.elementId.version}`
|
|
886
|
-
);
|
|
887
|
-
const locusDtoKey = ObjectTypeToLocusKeyMap[type];
|
|
888
|
-
locus[locusDtoKey] = object.data;
|
|
889
|
-
|
|
890
|
-
/* Hash tree based webinar attendees don't receive a Participant object for themselves from Locus,
|
|
891
|
-
but a lot of existing code in SDK and web app expects a member object for self to exist,
|
|
892
|
-
so whenever SELF changes for a webinar attendee, we copy it into a participant object.
|
|
893
|
-
We can do it, because SELF has always all the same properties as a participant object.
|
|
894
|
-
*/
|
|
895
|
-
if (
|
|
896
|
-
type === ObjectType.self &&
|
|
897
|
-
locus.info?.isWebinar &&
|
|
898
|
-
object.data.controls?.role?.roles?.find(
|
|
899
|
-
(r) => r.type === SELF_ROLES.ATTENDEE && r.hasRole
|
|
900
|
-
)
|
|
901
|
-
) {
|
|
902
|
-
LoggerProxy.logger.info(
|
|
903
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> webinar attendee: creating participant object from self`
|
|
904
|
-
);
|
|
905
|
-
addParticipantObject(object);
|
|
906
|
-
}
|
|
594
|
+
|
|
595
|
+
return locus;
|
|
907
596
|
}
|
|
908
|
-
break;
|
|
909
|
-
case ObjectType.metadata:
|
|
910
597
|
LoggerProxy.logger.info(
|
|
911
|
-
`Locus-info:index#updateLocusFromHashTreeObject -->
|
|
598
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> SELF object updated`
|
|
912
599
|
);
|
|
913
|
-
|
|
600
|
+
locus.self = object.data;
|
|
914
601
|
break;
|
|
915
602
|
default:
|
|
916
603
|
LoggerProxy.logger.warn(
|
|
@@ -944,92 +631,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
944
631
|
}
|
|
945
632
|
}
|
|
946
633
|
|
|
947
|
-
/**
|
|
948
|
-
* Checks if the hash tree message should trigger a switch to a different HashTreeParser
|
|
949
|
-
*
|
|
950
|
-
* @param {HashTreeMessage} message incoming hash tree message
|
|
951
|
-
* @returns {boolean} true if the message was handled as a parser switch, false otherwise
|
|
952
|
-
*/
|
|
953
|
-
private handleHashTreeParserSwitch(message: HashTreeMessage): boolean {
|
|
954
|
-
const entry = this.hashTreeParsers.get(message.locusUrl);
|
|
955
|
-
|
|
956
|
-
const self = message.locusStateElements?.find((el) => isSelf(el))?.data;
|
|
957
|
-
const replaces = getReplaceInfoFromSelf(
|
|
958
|
-
self,
|
|
959
|
-
// @ts-ignore
|
|
960
|
-
this.webex.internal.device.url
|
|
961
|
-
);
|
|
962
|
-
|
|
963
|
-
if (!entry) {
|
|
964
|
-
// Metadata object that contains information about visible datasets is needed to initialize the HashTreeParser,
|
|
965
|
-
// but it's buried inside the message, we need to find it and pass it to HashTreeParser constructor
|
|
966
|
-
const metadata = message.locusStateElements?.find((el) => isMetadata(el));
|
|
967
|
-
|
|
968
|
-
if (metadata?.data?.visibleDataSets?.length > 0) {
|
|
969
|
-
LoggerProxy.logger.info(
|
|
970
|
-
`Locus-info:index#handleHashTreeParserSwitch --> no hash tree parser found for locusUrl ${message.locusUrl}, creating a new one`
|
|
971
|
-
);
|
|
972
|
-
|
|
973
|
-
const parser = this.createHashTreeParser({
|
|
974
|
-
locusUrl: message.locusUrl,
|
|
975
|
-
initialLocus: {
|
|
976
|
-
locus: null,
|
|
977
|
-
dataSets: message.dataSets,
|
|
978
|
-
},
|
|
979
|
-
metadata: {
|
|
980
|
-
htMeta: metadata.htMeta,
|
|
981
|
-
visibleDataSets: metadata.data.visibleDataSets,
|
|
982
|
-
},
|
|
983
|
-
replacedAt: replaces?.replacedAt,
|
|
984
|
-
});
|
|
985
|
-
|
|
986
|
-
// handle the message with the new parser
|
|
987
|
-
parser.handleMessage(message);
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
return true;
|
|
991
|
-
}
|
|
992
|
-
if (entry.parser.state === 'stopped') {
|
|
993
|
-
// the message matches a stopped parser, we need to check if maybe this is a new "replacement" and we need to re-activate the parser
|
|
994
|
-
// this happens when you move from breakout A -> breakout B -> back to breakout A
|
|
995
|
-
if (replaces) {
|
|
996
|
-
if (replaces.replacedAt > (entry.replacedAt || '')) {
|
|
997
|
-
LoggerProxy.logger.info(
|
|
998
|
-
`Locus-info:index#handleHashTreeParserSwitch --> resuming a HashTreeParser for locusUrl=${message.locusUrl}, which replaces ${replaces.locusUrl}`
|
|
999
|
-
);
|
|
1000
|
-
const replacedEntry = this.hashTreeParsers.get(replaces.locusUrl);
|
|
1001
|
-
|
|
1002
|
-
if (replacedEntry) {
|
|
1003
|
-
replacedEntry.replacedAt = replaces.replacedAt;
|
|
1004
|
-
entry.initializedFromHashTree = false;
|
|
1005
|
-
this.hashTreeObjectId2ParticipantId.clear();
|
|
1006
|
-
|
|
1007
|
-
replacedEntry.parser.stop();
|
|
1008
|
-
entry.parser.resume(message);
|
|
1009
|
-
} else {
|
|
1010
|
-
LoggerProxy.logger.warn(
|
|
1011
|
-
`Locus-info:index#handleHashTreeParserSwitch --> the parser that is supposed to be replaced with the currently resumed parser is not found, locusUrl=${replaces.locusUrl}`
|
|
1012
|
-
);
|
|
1013
|
-
}
|
|
1014
|
-
} else {
|
|
1015
|
-
LoggerProxy.logger.info(
|
|
1016
|
-
`Locus-info:index#handleHashTreeParserSwitch --> received message for stopped HashTreeParser with locusUrl ${message.locusUrl}, but replaces info provided is not newer, so not re-activating the parser`
|
|
1017
|
-
);
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
return true;
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
LoggerProxy.logger.info(
|
|
1024
|
-
`Locus-info:index#handleHashTreeParserSwitch --> received message for stopped HashTreeParser with locusUrl ${message.locusUrl}, but no replaces info provided, so not re-activating the parser`
|
|
1025
|
-
);
|
|
1026
|
-
|
|
1027
|
-
return true;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
return false;
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
634
|
/**
|
|
1034
635
|
* Handles a hash tree message received from Locus.
|
|
1035
636
|
*
|
|
@@ -1048,28 +649,18 @@ export default class LocusInfo extends EventsScope {
|
|
|
1048
649
|
return;
|
|
1049
650
|
}
|
|
1050
651
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
if (parserSwitched) {
|
|
1054
|
-
return;
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
const entry = this.hashTreeParsers.get(message.locusUrl);
|
|
1058
|
-
|
|
1059
|
-
entry.parser.handleMessage(message);
|
|
652
|
+
this.hashTreeParser.handleMessage(message);
|
|
1060
653
|
}
|
|
1061
654
|
|
|
1062
655
|
/**
|
|
1063
656
|
* Callback registered with HashTreeParser to receive locus info updates.
|
|
1064
657
|
* Updates our locus info based on the data parsed by the hash tree parser.
|
|
1065
658
|
*
|
|
1066
|
-
* @param {string} locusUrl - the locus URL for which the update is received
|
|
1067
659
|
* @param {LocusInfoUpdateType} updateType - The type of update received.
|
|
1068
660
|
* @param {Object} [data] - Additional data for the update, if applicable.
|
|
1069
661
|
* @returns {void}
|
|
1070
662
|
*/
|
|
1071
663
|
private updateFromHashTree(
|
|
1072
|
-
locusUrl: string,
|
|
1073
664
|
updateType: LocusInfoUpdateType,
|
|
1074
665
|
data?: {updatedObjects: HashTreeObject[]}
|
|
1075
666
|
) {
|
|
@@ -1078,10 +669,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
1078
669
|
// initialize our new locus
|
|
1079
670
|
let locus: LocusDTO = {
|
|
1080
671
|
participants: [],
|
|
1081
|
-
jsSdkMeta: {
|
|
1082
|
-
removedParticipantIds: [],
|
|
1083
|
-
forceReplaceMembers: false,
|
|
1084
|
-
},
|
|
672
|
+
jsSdkMeta: {removedParticipantIds: []},
|
|
1085
673
|
};
|
|
1086
674
|
|
|
1087
675
|
// first go over all the updates and check what happens with the main locus object
|
|
@@ -1114,40 +702,21 @@ export default class LocusInfo extends EventsScope {
|
|
|
1114
702
|
}
|
|
1115
703
|
});
|
|
1116
704
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
if
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
LoggerProxy.logger.info(
|
|
1124
|
-
`Locus-info:index#updateFromHashTree --> first INITIAL update for locusUrl ${locusUrl}, starting from empty state`
|
|
1125
|
-
);
|
|
1126
|
-
hashTreeParserEntry.initializedFromHashTree = true;
|
|
1127
|
-
locus.jsSdkMeta.forceReplaceMembers = true;
|
|
1128
|
-
} else if (
|
|
1129
|
-
// if Locus object is unchanged or removed, we need to keep using the existing locus
|
|
1130
|
-
// because the rest of the locusInfo code expects locus to always be present (with at least some of the fields)
|
|
1131
|
-
// if it gets updated, we only need to have the fields that are not part of "locus" object (like "info" or "mediaShares")
|
|
1132
|
-
// so that when Locus object gets updated, if the new one is missing some field, that field will
|
|
1133
|
-
// be removed from our locusInfo
|
|
705
|
+
// if Locus object is unchanged or removed, we need to keep using the existing locus
|
|
706
|
+
// because the rest of the locusInfo code expects locus to always be present (with at least some of the fields)
|
|
707
|
+
// if it gets updated, we don't need to do anything and we start with an empty one
|
|
708
|
+
// so that when it gets updated, if the new one is missing some field, that field will
|
|
709
|
+
// be removed from our locusInfo
|
|
710
|
+
if (
|
|
1134
711
|
locusObjectStateAfterUpdates === LocusObjectStateAfterUpdates.unchanged ||
|
|
1135
712
|
locusObjectStateAfterUpdates === LocusObjectStateAfterUpdates.removed
|
|
1136
713
|
) {
|
|
1137
|
-
// copy over
|
|
714
|
+
// copy over existing locus
|
|
1138
715
|
LocusDtoTopLevelKeys.forEach((key) => {
|
|
1139
716
|
if (key !== 'participants') {
|
|
1140
717
|
locus[key] = cloneDeep(this[key]);
|
|
1141
718
|
}
|
|
1142
719
|
});
|
|
1143
|
-
} else {
|
|
1144
|
-
// initialize only the fields that are not part of main "Locus" object
|
|
1145
|
-
// (except participants, which need to stay empty - that means "no participant changes")
|
|
1146
|
-
Object.values(ObjectTypeToLocusKeyMap).forEach((locusDtoKey) => {
|
|
1147
|
-
if (locusDtoKey !== 'participants') {
|
|
1148
|
-
locus[locusDtoKey] = cloneDeep(this[locusDtoKey]);
|
|
1149
|
-
}
|
|
1150
|
-
});
|
|
1151
720
|
}
|
|
1152
721
|
|
|
1153
722
|
LoggerProxy.logger.info(
|
|
@@ -1155,7 +724,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
1155
724
|
data.updatedObjects.map((o) => ({
|
|
1156
725
|
type: o.htMeta.elementId.type,
|
|
1157
726
|
id: o.htMeta.elementId.id,
|
|
1158
|
-
hasData:
|
|
727
|
+
hasData: o.data !== undefined,
|
|
1159
728
|
}))
|
|
1160
729
|
)}`
|
|
1161
730
|
);
|
|
@@ -1171,14 +740,11 @@ export default class LocusInfo extends EventsScope {
|
|
|
1171
740
|
}
|
|
1172
741
|
|
|
1173
742
|
case LocusInfoUpdateType.MEETING_ENDED: {
|
|
743
|
+
LoggerProxy.logger.info(
|
|
744
|
+
`Locus-info:index#updateFromHashTree --> received signal that meeting ended, destroying meeting ${this.meetingId}`
|
|
745
|
+
);
|
|
1174
746
|
const meeting = this.webex.meetings.meetingCollection.get(this.meetingId);
|
|
1175
|
-
|
|
1176
|
-
if (meeting) {
|
|
1177
|
-
LoggerProxy.logger.info(
|
|
1178
|
-
`Locus-info:index#updateFromHashTree --> received signal that meeting ended, destroying meeting ${this.meetingId}`
|
|
1179
|
-
);
|
|
1180
|
-
this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.SELF_REMOVED);
|
|
1181
|
-
}
|
|
747
|
+
this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.SELF_REMOVED);
|
|
1182
748
|
}
|
|
1183
749
|
}
|
|
1184
750
|
}
|
|
@@ -1190,25 +756,15 @@ export default class LocusInfo extends EventsScope {
|
|
|
1190
756
|
* @memberof LocusInfo
|
|
1191
757
|
*/
|
|
1192
758
|
parse(meeting: any, data: any) {
|
|
1193
|
-
if (this.
|
|
759
|
+
if (this.hashTreeParser) {
|
|
1194
760
|
this.handleHashTreeMessage(
|
|
1195
761
|
meeting,
|
|
1196
762
|
data.eventType,
|
|
1197
763
|
data.stateElementsMessage as HashTreeMessage
|
|
1198
764
|
);
|
|
1199
765
|
} else {
|
|
766
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
1200
767
|
const {eventType} = data;
|
|
1201
|
-
|
|
1202
|
-
if (eventType === LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
|
|
1203
|
-
// this can happen when we get an event before join http response
|
|
1204
|
-
// it's OK to just ignore it
|
|
1205
|
-
LoggerProxy.logger.info(
|
|
1206
|
-
`Locus-info:index#parse --> received locus hash tree event before hashTreeParser is created`
|
|
1207
|
-
);
|
|
1208
|
-
|
|
1209
|
-
return;
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
768
|
const locus = this.getTheLocusToUpdate(data.locus);
|
|
1213
769
|
LoggerProxy.logger.info(`Locus-info:index#parse --> received locus data: ${eventType}`);
|
|
1214
770
|
|
|
@@ -1229,11 +785,17 @@ export default class LocusInfo extends EventsScope {
|
|
|
1229
785
|
case LOCUSEVENT.PARTICIPANT_DECLINED:
|
|
1230
786
|
case LOCUSEVENT.FLOOR_GRANTED:
|
|
1231
787
|
case LOCUSEVENT.FLOOR_RELEASED:
|
|
1232
|
-
this.onFullLocus(
|
|
788
|
+
this.onFullLocus(locus, eventType);
|
|
1233
789
|
break;
|
|
1234
790
|
case LOCUSEVENT.DIFFERENCE:
|
|
1235
791
|
this.handleLocusDelta(locus, meeting);
|
|
1236
792
|
break;
|
|
793
|
+
case LOCUSEVENT.HASH_TREE_DATA_UPDATED:
|
|
794
|
+
this.sendClassicVsHashTreeMismatchMetric(
|
|
795
|
+
meeting,
|
|
796
|
+
`got ${eventType}, expected classic events`
|
|
797
|
+
);
|
|
798
|
+
break;
|
|
1237
799
|
|
|
1238
800
|
default:
|
|
1239
801
|
// Why will there be a event with no eventType ????
|
|
@@ -1257,63 +819,46 @@ export default class LocusInfo extends EventsScope {
|
|
|
1257
819
|
/**
|
|
1258
820
|
* Function for handling full locus when it's using hash trees (so not the "classic" one).
|
|
1259
821
|
*
|
|
1260
|
-
* @param {string} debugText string explaining the trigger for this call, added to logs for debugging purposes
|
|
1261
822
|
* @param {object} locus locus object
|
|
1262
|
-
* @param {object} metadata locus hash trees metadata
|
|
1263
823
|
* @param {string} eventType locus event
|
|
1264
824
|
* @param {DataSet[]} dataSets
|
|
1265
825
|
* @returns {void}
|
|
1266
826
|
*/
|
|
1267
|
-
private onFullLocusWithHashTrees(
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
metadata: Metadata,
|
|
1271
|
-
eventType?: string,
|
|
1272
|
-
dataSets?: Array<DataSet>
|
|
1273
|
-
) {
|
|
1274
|
-
if (!this.hashTreeParsers.has(locus.url)) {
|
|
827
|
+
private onFullLocusWithHashTrees(locus: any, eventType?: string, dataSets?: Array<DataSet>) {
|
|
828
|
+
if (!this.hashTreeParser) {
|
|
829
|
+
LoggerProxy.logger.info(`Locus-info:index#onFullLocus --> creating hash tree parser`);
|
|
1275
830
|
LoggerProxy.logger.info(
|
|
1276
|
-
|
|
1277
|
-
);
|
|
1278
|
-
LoggerProxy.logger.info(
|
|
1279
|
-
`Locus-info:index#onFullLocus (${debugText}) --> dataSets:`,
|
|
831
|
+
'Locus-info:index#onFullLocus --> dataSets:',
|
|
1280
832
|
dataSets,
|
|
1281
833
|
' and locus:',
|
|
1282
|
-
locus
|
|
1283
|
-
' and metadata:',
|
|
1284
|
-
metadata
|
|
834
|
+
locus
|
|
1285
835
|
);
|
|
1286
|
-
this.createHashTreeParser({
|
|
1287
|
-
locusUrl: locus.url,
|
|
836
|
+
this.hashTreeParser = this.createHashTreeParser({
|
|
1288
837
|
initialLocus: {locus, dataSets},
|
|
1289
|
-
metadata,
|
|
1290
838
|
});
|
|
1291
|
-
// we have a full locus to start with, so we consider Locus info to be "initialized"
|
|
1292
|
-
this.hashTreeParsers.get(locus.url).initializedFromHashTree = true;
|
|
1293
839
|
this.onFullLocusCommon(locus, eventType);
|
|
1294
840
|
} else {
|
|
1295
841
|
// in this case the Locus we're getting is not necessarily the full one
|
|
1296
842
|
// so treat it like if we just got it in any api response
|
|
1297
843
|
|
|
1298
844
|
LoggerProxy.logger.info(
|
|
1299
|
-
|
|
845
|
+
'Locus-info:index#onFullLocus --> hash tree parser already exists, handling it like a normal API response'
|
|
1300
846
|
);
|
|
1301
|
-
this.handleLocusAPIResponse(undefined, {dataSets, locus
|
|
847
|
+
this.handleLocusAPIResponse(undefined, {dataSets, locus});
|
|
1302
848
|
}
|
|
1303
849
|
}
|
|
1304
850
|
|
|
1305
851
|
/**
|
|
1306
852
|
* Function for handling full locus when it's the "classic" one (not hash trees)
|
|
1307
853
|
*
|
|
1308
|
-
* @param {string} debugText string explaining the trigger for this call, added to logs for debugging purposes
|
|
1309
854
|
* @param {object} locus locus object
|
|
1310
855
|
* @param {string} eventType locus event
|
|
1311
856
|
* @returns {void}
|
|
1312
857
|
*/
|
|
1313
|
-
private onFullLocusClassic(
|
|
858
|
+
private onFullLocusClassic(locus: any, eventType?: string) {
|
|
1314
859
|
if (!this.locusParser.isNewFullLocus(locus)) {
|
|
1315
860
|
LoggerProxy.logger.info(
|
|
1316
|
-
`Locus-info:index#onFullLocus
|
|
861
|
+
`Locus-info:index#onFullLocus --> ignoring old full locus DTO, eventType=${eventType}`
|
|
1317
862
|
);
|
|
1318
863
|
|
|
1319
864
|
return;
|
|
@@ -1323,37 +868,24 @@ export default class LocusInfo extends EventsScope {
|
|
|
1323
868
|
|
|
1324
869
|
/**
|
|
1325
870
|
* updates the locus with full locus object
|
|
1326
|
-
* @param {string} debugText string explaining the trigger for this call, added to logs for debugging purposes
|
|
1327
871
|
* @param {object} locus locus object
|
|
1328
872
|
* @param {string} eventType locus event
|
|
1329
873
|
* @param {DataSet[]} dataSets
|
|
1330
|
-
* @param {object} metadata locus hash trees metadata
|
|
1331
874
|
* @returns {object} null
|
|
1332
875
|
* @memberof LocusInfo
|
|
1333
876
|
*/
|
|
1334
|
-
onFullLocus(
|
|
1335
|
-
debugText: string,
|
|
1336
|
-
locus: any,
|
|
1337
|
-
eventType?: string,
|
|
1338
|
-
dataSets?: Array<DataSet>,
|
|
1339
|
-
metadata?: Metadata
|
|
1340
|
-
) {
|
|
877
|
+
onFullLocus(locus: any, eventType?: string, dataSets?: Array<DataSet>) {
|
|
1341
878
|
if (!locus) {
|
|
1342
879
|
LoggerProxy.logger.error(
|
|
1343
|
-
|
|
880
|
+
'Locus-info:index#onFullLocus --> object passed as argument was invalid, continuing.'
|
|
1344
881
|
);
|
|
1345
882
|
}
|
|
1346
883
|
|
|
1347
884
|
if (dataSets) {
|
|
1348
|
-
if (!metadata) {
|
|
1349
|
-
throw new Error(
|
|
1350
|
-
`Locus-info:index#onFullLocus (${debugText}) --> hash tree metadata is missing with full Locus`
|
|
1351
|
-
);
|
|
1352
|
-
}
|
|
1353
885
|
// this is the new hashmap Locus DTO format (only applicable to webinars for now)
|
|
1354
|
-
this.onFullLocusWithHashTrees(
|
|
886
|
+
this.onFullLocusWithHashTrees(locus, eventType, dataSets);
|
|
1355
887
|
} else {
|
|
1356
|
-
this.onFullLocusClassic(
|
|
888
|
+
this.onFullLocusClassic(locus, eventType);
|
|
1357
889
|
}
|
|
1358
890
|
}
|
|
1359
891
|
|
|
@@ -1368,7 +900,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
1368
900
|
this.participants = locus.participants;
|
|
1369
901
|
this.participants?.forEach((participant) => {
|
|
1370
902
|
// participant.htMeta is set only for hash tree based locus
|
|
1371
|
-
if (
|
|
903
|
+
if (participant.htMeta?.elementId.id) {
|
|
1372
904
|
this.hashTreeObjectId2ParticipantId.set(participant.htMeta.elementId.id, participant.id);
|
|
1373
905
|
}
|
|
1374
906
|
});
|
|
@@ -1395,8 +927,8 @@ export default class LocusInfo extends EventsScope {
|
|
|
1395
927
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
1396
928
|
handleOneOnOneEvent(eventType: string) {
|
|
1397
929
|
if (
|
|
1398
|
-
this.parsedLocus.fullState
|
|
1399
|
-
this.parsedLocus.fullState
|
|
930
|
+
this.parsedLocus.fullState.type === _CALL_ ||
|
|
931
|
+
this.parsedLocus.fullState.type === _SIP_BRIDGE_
|
|
1400
932
|
) {
|
|
1401
933
|
// for 1:1 bob calls alice and alice declines, notify the meeting state
|
|
1402
934
|
if (eventType === LOCUSEVENT.PARTICIPANT_DECLINED) {
|
|
@@ -1431,61 +963,20 @@ export default class LocusInfo extends EventsScope {
|
|
|
1431
963
|
}
|
|
1432
964
|
}
|
|
1433
965
|
|
|
1434
|
-
/**
|
|
1435
|
-
* Makes sure that passed in locus object has a participant object for self.
|
|
1436
|
-
*
|
|
1437
|
-
* @param {LocusDTO} locus The locus object to check and modify if needed
|
|
1438
|
-
* @returns {void}
|
|
1439
|
-
*/
|
|
1440
|
-
ensureSelfParticipantExists(locus: any) {
|
|
1441
|
-
const {self} = locus;
|
|
1442
|
-
|
|
1443
|
-
// sanity check, this should never fail
|
|
1444
|
-
if (!self?.identity || !Array.isArray(locus.participants)) {
|
|
1445
|
-
LoggerProxy.logger.warn(
|
|
1446
|
-
`Locus-info:index#ensureSelfParticipantExists --> locus object is missing required fields, cannot ensure self participant exists. self?.identity="${self?.identity}"`
|
|
1447
|
-
);
|
|
1448
|
-
|
|
1449
|
-
return;
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
const selfExists = locus.participants.some(
|
|
1453
|
-
(participant) => participant.identity === self.identity
|
|
1454
|
-
);
|
|
1455
|
-
|
|
1456
|
-
if (!selfExists) {
|
|
1457
|
-
locus.participants.push({...self});
|
|
1458
|
-
}
|
|
1459
|
-
}
|
|
1460
|
-
|
|
1461
966
|
/**
|
|
1462
967
|
* @param {Object} locus
|
|
1463
968
|
* @returns {undefined}
|
|
1464
969
|
* @memberof LocusInfo
|
|
1465
970
|
*/
|
|
1466
971
|
onDeltaLocus(locus: any) {
|
|
1467
|
-
const isReplaceMembers =
|
|
1468
|
-
locus.jsSdkMeta?.forceReplaceMembers !== undefined
|
|
1469
|
-
? locus.jsSdkMeta.forceReplaceMembers
|
|
1470
|
-
: ControlsUtils.isNeedReplaceMembers(this.controls, locus.controls);
|
|
1471
|
-
|
|
1472
|
-
if (isReplaceMembers) {
|
|
1473
|
-
// when we're moving between breakouts, Locus sometimes doesn't send us
|
|
1474
|
-
// any participants at all for a few seconds
|
|
1475
|
-
// Web app relies on having at least the self participant always there
|
|
1476
|
-
// so we copy self into participants if it's not there.
|
|
1477
|
-
this.ensureSelfParticipantExists(locus);
|
|
1478
|
-
}
|
|
972
|
+
const isReplaceMembers = ControlsUtils.isNeedReplaceMembers(this.controls, locus.controls);
|
|
1479
973
|
this.mergeParticipants(this.participants, locus.participants);
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
isReplaceMembers
|
|
1487
|
-
);
|
|
1488
|
-
}
|
|
974
|
+
this.updateLocusInfo(locus);
|
|
975
|
+
this.updateParticipants(
|
|
976
|
+
locus.participants,
|
|
977
|
+
locus.jsSdkMeta?.removedParticipantIds,
|
|
978
|
+
isReplaceMembers
|
|
979
|
+
);
|
|
1489
980
|
this.isMeetingActive();
|
|
1490
981
|
}
|
|
1491
982
|
|
|
@@ -1499,7 +990,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
1499
990
|
// When moved to a breakout session locus sends a message for the previous locus
|
|
1500
991
|
// indicating that we have been moved. It isn't helpful to continue parsing this
|
|
1501
992
|
// as it gets interpreted as if we have left the call
|
|
1502
|
-
return
|
|
993
|
+
return;
|
|
1503
994
|
}
|
|
1504
995
|
|
|
1505
996
|
this.updateControls(locus.controls, locus.self);
|
|
@@ -1510,17 +1001,19 @@ export default class LocusInfo extends EventsScope {
|
|
|
1510
1001
|
this.updateLocusUrl(locus.url, ControlsUtils.isMainSessionDTO(locus));
|
|
1511
1002
|
this.updateMeetingInfo(locus.info, locus.self);
|
|
1512
1003
|
this.updateMediaShares(locus.mediaShares);
|
|
1513
|
-
this.
|
|
1004
|
+
this.updateParticipantsUrl(locus.participantsUrl);
|
|
1005
|
+
this.updateReplace(locus.replace);
|
|
1514
1006
|
this.updateSelf(locus.self);
|
|
1515
1007
|
this.updateAclUrl(locus.aclUrl);
|
|
1516
1008
|
this.updateBasequence(locus.baseSequence);
|
|
1517
1009
|
this.updateSequence(locus.sequence);
|
|
1010
|
+
this.updateMemberShip(locus.membership);
|
|
1011
|
+
this.updateIdentifiers(locus.identities);
|
|
1518
1012
|
this.updateEmbeddedApps(locus.embeddedApps);
|
|
1519
|
-
this.
|
|
1013
|
+
this.updateServices(locus.links?.services);
|
|
1014
|
+
this.updateResources(locus.links?.resources);
|
|
1520
1015
|
this.compareAndUpdate();
|
|
1521
1016
|
// update which required to compare different objects from locus
|
|
1522
|
-
|
|
1523
|
-
return true;
|
|
1524
1017
|
}
|
|
1525
1018
|
|
|
1526
1019
|
/**
|
|
@@ -1552,9 +1045,9 @@ export default class LocusInfo extends EventsScope {
|
|
|
1552
1045
|
*/
|
|
1553
1046
|
isMeetingActive() {
|
|
1554
1047
|
if (
|
|
1555
|
-
this.parsedLocus.fullState
|
|
1556
|
-
this.parsedLocus.fullState
|
|
1557
|
-
this.parsedLocus.fullState
|
|
1048
|
+
this.parsedLocus.fullState.type === _CALL_ ||
|
|
1049
|
+
this.parsedLocus.fullState.type === _SIP_BRIDGE_ ||
|
|
1050
|
+
this.parsedLocus.fullState.type === _SPACE_SHARE_
|
|
1558
1051
|
) {
|
|
1559
1052
|
// @ts-ignore
|
|
1560
1053
|
const partner = this.getLocusPartner(this.participants, this.self);
|
|
@@ -1647,7 +1140,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
1647
1140
|
}
|
|
1648
1141
|
);
|
|
1649
1142
|
}
|
|
1650
|
-
} else if (this.parsedLocus.fullState
|
|
1143
|
+
} else if (this.parsedLocus.fullState.type === _MEETING_) {
|
|
1651
1144
|
if (
|
|
1652
1145
|
this.fullState &&
|
|
1653
1146
|
(this.fullState.state === LOCUS.STATE.INACTIVE ||
|
|
@@ -1676,6 +1169,27 @@ export default class LocusInfo extends EventsScope {
|
|
|
1676
1169
|
shouldLeave: false,
|
|
1677
1170
|
}
|
|
1678
1171
|
);
|
|
1172
|
+
} else if (this.fullState && this.fullState.removed) {
|
|
1173
|
+
// user has been dropped from a meeting
|
|
1174
|
+
|
|
1175
|
+
// @ts-ignore
|
|
1176
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
1177
|
+
name: 'client.call.remote-ended',
|
|
1178
|
+
options: {
|
|
1179
|
+
meetingId: this.meetingId,
|
|
1180
|
+
},
|
|
1181
|
+
});
|
|
1182
|
+
this.emitScoped(
|
|
1183
|
+
{
|
|
1184
|
+
file: 'locus-info',
|
|
1185
|
+
function: 'isMeetingActive',
|
|
1186
|
+
},
|
|
1187
|
+
EVENTS.DESTROY_MEETING,
|
|
1188
|
+
{
|
|
1189
|
+
reason: MEETING_REMOVED_REASON.FULLSTATE_REMOVED,
|
|
1190
|
+
shouldLeave: false,
|
|
1191
|
+
}
|
|
1192
|
+
);
|
|
1679
1193
|
}
|
|
1680
1194
|
// If you are guest and you are removed from the meeting
|
|
1681
1195
|
// You wont get any further events
|
|
@@ -1723,7 +1237,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
1723
1237
|
compareSelfAndHost() {
|
|
1724
1238
|
// In some cases the host info is not present but the moderator values changes from null to false so it triggers an update
|
|
1725
1239
|
if (
|
|
1726
|
-
this.parsedLocus.self &&
|
|
1727
1240
|
this.parsedLocus.self.selfIdentity === this.parsedLocus.host?.hostId &&
|
|
1728
1241
|
this.parsedLocus.self.moderator
|
|
1729
1242
|
) {
|
|
@@ -1810,8 +1323,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
1810
1323
|
hasRecordingPausedChanged,
|
|
1811
1324
|
hasMeetingContainerChanged,
|
|
1812
1325
|
hasTranscribeChanged,
|
|
1813
|
-
hasHesiodLLMIdChanged,
|
|
1814
|
-
hasAiSummaryNotificationChanged,
|
|
1815
1326
|
hasTranscribeSpokenLanguageChanged,
|
|
1816
1327
|
hasManualCaptionChanged,
|
|
1817
1328
|
hasEntryExitToneChanged,
|
|
@@ -1953,34 +1464,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
1953
1464
|
);
|
|
1954
1465
|
}
|
|
1955
1466
|
|
|
1956
|
-
if (hasHesiodLLMIdChanged) {
|
|
1957
|
-
const {hesiodLlmId} = current.transcribe;
|
|
1958
|
-
|
|
1959
|
-
this.emitScoped(
|
|
1960
|
-
{
|
|
1961
|
-
file: 'locus-info',
|
|
1962
|
-
function: 'updateControls',
|
|
1963
|
-
},
|
|
1964
|
-
LOCUSINFO.EVENTS.CONTROLS_MEETING_HESIOD_LLM_ID_UPDATED,
|
|
1965
|
-
{
|
|
1966
|
-
hesiodLlmId,
|
|
1967
|
-
}
|
|
1968
|
-
);
|
|
1969
|
-
}
|
|
1970
|
-
|
|
1971
|
-
if (hasAiSummaryNotificationChanged) {
|
|
1972
|
-
this.emitScoped(
|
|
1973
|
-
{
|
|
1974
|
-
file: 'locus-info',
|
|
1975
|
-
function: 'updateControls',
|
|
1976
|
-
},
|
|
1977
|
-
LOCUSINFO.EVENTS.CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED,
|
|
1978
|
-
{
|
|
1979
|
-
aiSummaryNotification: current.transcribe.aiSummaryNotification,
|
|
1980
|
-
}
|
|
1981
|
-
);
|
|
1982
|
-
}
|
|
1983
|
-
|
|
1984
1467
|
if (hasTranscribeSpokenLanguageChanged) {
|
|
1985
1468
|
const {spokenLanguage} = current.transcribe;
|
|
1986
1469
|
|
|
@@ -2013,7 +1496,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
2013
1496
|
|
|
2014
1497
|
if (hasBreakoutChanged) {
|
|
2015
1498
|
const {breakout} = current;
|
|
2016
|
-
|
|
2017
1499
|
breakout.breakoutMoveId = SelfUtils.getReplacedBreakoutMoveId(
|
|
2018
1500
|
self,
|
|
2019
1501
|
this.webex.internal.device.url
|
|
@@ -2181,19 +1663,17 @@ export default class LocusInfo extends EventsScope {
|
|
|
2181
1663
|
}
|
|
2182
1664
|
|
|
2183
1665
|
/**
|
|
2184
|
-
*
|
|
2185
|
-
* @param {Object} links
|
|
1666
|
+
* @param {Object} services
|
|
2186
1667
|
* @returns {undefined}
|
|
2187
1668
|
* @memberof LocusInfo
|
|
2188
1669
|
*/
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
if (services && !isEqual(this.links?.services, services)) {
|
|
1670
|
+
updateServices(services: Record<'breakout' | 'record', {url: string}>) {
|
|
1671
|
+
if (services && !isEqual(this.services, services)) {
|
|
1672
|
+
this.services = services;
|
|
2193
1673
|
this.emitScoped(
|
|
2194
1674
|
{
|
|
2195
1675
|
file: 'locus-info',
|
|
2196
|
-
function: '
|
|
1676
|
+
function: 'updateServices',
|
|
2197
1677
|
},
|
|
2198
1678
|
LOCUSINFO.EVENTS.LINKS_SERVICES,
|
|
2199
1679
|
{
|
|
@@ -2201,12 +1681,20 @@ export default class LocusInfo extends EventsScope {
|
|
|
2201
1681
|
}
|
|
2202
1682
|
);
|
|
2203
1683
|
}
|
|
1684
|
+
}
|
|
2204
1685
|
|
|
2205
|
-
|
|
1686
|
+
/**
|
|
1687
|
+
* @param {Object} resources
|
|
1688
|
+
* @returns {undefined}
|
|
1689
|
+
* @memberof LocusInfo
|
|
1690
|
+
*/
|
|
1691
|
+
updateResources(resources: Record<'webcastInstance', {url: string}>) {
|
|
1692
|
+
if (resources && !isEqual(this.resources, resources)) {
|
|
1693
|
+
this.resources = resources;
|
|
2206
1694
|
this.emitScoped(
|
|
2207
1695
|
{
|
|
2208
1696
|
file: 'locus-info',
|
|
2209
|
-
function: '
|
|
1697
|
+
function: 'updateResources',
|
|
2210
1698
|
},
|
|
2211
1699
|
LOCUSINFO.EVENTS.LINKS_RESOURCES,
|
|
2212
1700
|
{
|
|
@@ -2214,8 +1702,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
2214
1702
|
}
|
|
2215
1703
|
);
|
|
2216
1704
|
}
|
|
2217
|
-
|
|
2218
|
-
this.links = links;
|
|
2219
1705
|
}
|
|
2220
1706
|
|
|
2221
1707
|
/**
|
|
@@ -2402,13 +1888,24 @@ export default class LocusInfo extends EventsScope {
|
|
|
2402
1888
|
}
|
|
2403
1889
|
|
|
2404
1890
|
/**
|
|
2405
|
-
* @param {
|
|
1891
|
+
* @param {String} participantsUrl
|
|
1892
|
+
* @returns {undefined}
|
|
1893
|
+
* @memberof LocusInfo
|
|
1894
|
+
*/
|
|
1895
|
+
updateParticipantsUrl(participantsUrl: string) {
|
|
1896
|
+
if (participantsUrl && !isEqual(this.participantsUrl, participantsUrl)) {
|
|
1897
|
+
this.participantsUrl = participantsUrl;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
/**
|
|
1902
|
+
* @param {Object} replace
|
|
2406
1903
|
* @returns {undefined}
|
|
2407
1904
|
* @memberof LocusInfo
|
|
2408
1905
|
*/
|
|
2409
|
-
|
|
2410
|
-
if (
|
|
2411
|
-
this.
|
|
1906
|
+
updateReplace(replace: object) {
|
|
1907
|
+
if (replace && !isEqual(this.replace, replace)) {
|
|
1908
|
+
this.replace = replace;
|
|
2412
1909
|
}
|
|
2413
1910
|
}
|
|
2414
1911
|
|
|
@@ -2439,14 +1936,14 @@ export default class LocusInfo extends EventsScope {
|
|
|
2439
1936
|
}
|
|
2440
1937
|
|
|
2441
1938
|
// TODO: check if we need to save the sipUri here as well
|
|
2442
|
-
// this.emit(LOCUSINFO.EVENTS.MEETING_UPDATE, SelfUtils.getSipUrl(this.getLocusPartner(participants, self), this.parsedLocus.fullState
|
|
1939
|
+
// this.emit(LOCUSINFO.EVENTS.MEETING_UPDATE, SelfUtils.getSipUrl(this.getLocusPartner(participants, self), this.parsedLocus.fullState.type, this.parsedLocus.info.sipUri));
|
|
2443
1940
|
const result = SelfUtils.getSipUrl(
|
|
2444
1941
|
this.getLocusPartner(this.participants, self),
|
|
2445
|
-
this.parsedLocus.fullState
|
|
2446
|
-
this.parsedLocus.info
|
|
1942
|
+
this.parsedLocus.fullState.type,
|
|
1943
|
+
this.parsedLocus.info.sipUri
|
|
2447
1944
|
);
|
|
2448
1945
|
|
|
2449
|
-
if (result
|
|
1946
|
+
if (result.sipUri) {
|
|
2450
1947
|
this.updateMeeting(result);
|
|
2451
1948
|
}
|
|
2452
1949
|
|
|
@@ -2491,19 +1988,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
2491
1988
|
);
|
|
2492
1989
|
}
|
|
2493
1990
|
|
|
2494
|
-
if (parsedSelves.updates.selfIdChanged) {
|
|
2495
|
-
this.emitScoped(
|
|
2496
|
-
{
|
|
2497
|
-
file: 'locus-info',
|
|
2498
|
-
function: 'updateSelf',
|
|
2499
|
-
},
|
|
2500
|
-
LOCUSINFO.EVENTS.SELF_ID_CHANGED,
|
|
2501
|
-
{
|
|
2502
|
-
selfId: parsedSelves.current.selfId,
|
|
2503
|
-
}
|
|
2504
|
-
);
|
|
2505
|
-
}
|
|
2506
|
-
|
|
2507
1991
|
if (parsedSelves.updates.interpretationChanged) {
|
|
2508
1992
|
this.emitScoped(
|
|
2509
1993
|
{
|
|
@@ -2758,6 +2242,28 @@ export default class LocusInfo extends EventsScope {
|
|
|
2758
2242
|
}
|
|
2759
2243
|
}
|
|
2760
2244
|
|
|
2245
|
+
/**
|
|
2246
|
+
* @param {Object} membership
|
|
2247
|
+
* @returns {undefined}
|
|
2248
|
+
* @memberof LocusInfo
|
|
2249
|
+
*/
|
|
2250
|
+
updateMemberShip(membership: object) {
|
|
2251
|
+
if (membership && !isEqual(this.membership, membership)) {
|
|
2252
|
+
this.membership = membership;
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
/**
|
|
2257
|
+
* @param {Array} identities
|
|
2258
|
+
* @returns {undefined}
|
|
2259
|
+
* @memberof LocusInfo
|
|
2260
|
+
*/
|
|
2261
|
+
updateIdentifiers(identities: Array<any>) {
|
|
2262
|
+
if (identities && !isEqual(this.identities, identities)) {
|
|
2263
|
+
this.identities = identities;
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2761
2267
|
/**
|
|
2762
2268
|
* check the locus is main session's one or not, if is main session's, update main session cache
|
|
2763
2269
|
* @param {Object} locus
|