Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions packages/@webex/plugin-meetings/src/locus-info/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ export default class LocusInfo extends EventsScope {
this.updateControls(locus.controls, locus.self);
this.updateLocusUrl(locus.url, ControlsUtils.isMainSessionDTO(locus));
this.updateFullState(locus.fullState);
this.updateMeetingInfo(locus.info);
this.updateMeetingInfo(locus.info, locus.self);
this.updateEmbeddedApps(locus.embeddedApps);
// self and participants generate sipUrl for 1:1 meeting
this.updateSelf(locus.self);
Expand Down Expand Up @@ -2491,9 +2491,19 @@ export default class LocusInfo extends EventsScope {
*/
updateMeetingInfo(info: object, self?: object) {
const roles = self ? SelfUtils.getRoles(self) : this.parsedLocus.self?.roles || [];
if ((info && !isEqual(this.info, info)) || (!isEqual(this.roles, roles) && info)) {
const isJoined = SelfUtils.isJoined(self || this.parsedLocus.self);
const parsedInfo = InfoUtils.getInfos(this.parsedLocus.info, info, roles, isJoined);
const isJoined = SelfUtils.isJoined(self || this.parsedLocus.self);

// The parsed userDisplayHints depend on info, roles and isJoined, so we must recompute
// whenever any of them changes. A common case is self transitioning to JOINED via a delta
// that doesn't carry an info section - in that case we fall back to the previously stored
// info so the hints get reparsed with the new joined state (e.g. VIEW_THE_PARTICIPANT_LIST).
const infoToParse = info || this.info;
const infoChanged = info && !isEqual(this.info, info);
const rolesChanged = !isEqual(this.roles, roles);
const isJoinedChanged = SelfUtils.isJoined(this.parsedLocus.self) !== isJoined;

if (infoToParse && (infoChanged || rolesChanged || isJoinedChanged)) {
const parsedInfo = InfoUtils.getInfos(this.parsedLocus.info, infoToParse, roles, isJoined);

if (parsedInfo.updates.isLocked) {
this.emitScoped(
Expand All @@ -2516,7 +2526,7 @@ export default class LocusInfo extends EventsScope {
);
}

this.info = info;
this.info = infoToParse;
this.parsedLocus.info = parsedInfo.current;
// Parses the info and adds necessary values
this.updateMeeting(parsedInfo.current);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2846,6 +2846,10 @@ describe('plugin-meetings', () => {

let expectedMeeting;

// simulate that updateSelf has been called previously (as happens in production)
// so that parsedLocus.self reflects the joined state
locusInfo.parsedLocus.self = {state: 'JOINED'};

/*
When the event is triggered, it is required that the meeting has already
been updated. This is why the meeting is being checked within the stubbed event emitter
Expand Down Expand Up @@ -2997,6 +3001,46 @@ describe('plugin-meetings', () => {
// since self is not passed to updateMeetingInfo, MEETING_INFO_UPDATED should be triggered with isIntializing: true
checkMeetingInfoUpdatedCalledForRoles(true, {isInitializing: true});
});

// joined-section hints (like ROSTER_IN_MEETING) are filtered out while not joined, so they
// are a good proxy for verifying that userDisplayHints get recomputed on a join transition
[
{
name: 'the JOINED delta carries the info section',
getSecondInfo: (info) => info,
},
{
name: 'the JOINED delta omits the info section (falls back to stored info)',
getSecondInfo: () => undefined,
},
].forEach(({name, getSecondInfo}) => {
it(`recomputes userDisplayHints when self transitions to JOINED with unchanged roles and ${name}`, () => {
const info = cloneDeep(meetingInfo); // joined: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED']

const notJoinedSelf = cloneDeep(self);
notJoinedSelf.state = 'IDLE';
notJoinedSelf.controls.role.roles = [];

const joinedSelf = cloneDeep(self);
joinedSelf.state = 'JOINED';
joinedSelf.controls.role.roles = [];

sinon.stub(locusInfo, 'emitScoped');

// first update while not joined: joined-section hints are filtered out
locusInfo.updateMeetingInfo(info, notJoinedSelf);
assert.notInclude(locusInfo.parsedLocus.info.userDisplayHints, 'ROSTER_IN_MEETING');
assert.notInclude(locusInfo.parsedLocus.info.userDisplayHints, 'LOCK_STATUS_UNLOCKED');

// self transitions to JOINED - info and roles are unchanged
locusInfo.updateMeetingInfo(getSecondInfo(info), joinedSelf);

// the hints must be recomputed with the new joined state
assert.include(locusInfo.parsedLocus.info.userDisplayHints, 'ROSTER_IN_MEETING');
assert.include(locusInfo.parsedLocus.info.userDisplayHints, 'LOCK_STATUS_UNLOCKED');
checkMeetingInfoUpdatedCalled(true, {isInitializing: false});
});
});
});

describe('#updateMediaShares', () => {
Expand Down