Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugin-meetings): Introduce SDK changes to call WCME and Locus APIs when current user steps away or returns #3942

Open
wants to merge 31 commits into
base: stepAway
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fc570e9
feat: introduce step away in sdk and test-app
Oct 28, 2024
ac86df9
fix(plugin-meetings): added unit tests
fnowakow Oct 31, 2024
3cbb587
fix(plugin-meetings): added NOT unit test
fnowakow Oct 31, 2024
faf4a73
chore: rename functions, update wcme
Nov 4, 2024
c9dd4d3
chore: update brbChanged SelfUtils
Nov 4, 2024
48caff9
chore: update beRightBack, add todo
Nov 5, 2024
1300726
chore: add todo
Nov 5, 2024
317e1b7
fix(plugin-meetings): fix beRightBack sourceStateOverride
fnowakow Nov 7, 2024
7ec0182
fix(plugin-meetings): audio/video (un)mute fix
fnowakow Nov 7, 2024
6e1ba35
Merge branch 'antsukan/SPARK-559645' into fnowakow/SPARK-559645-unit_…
fnowakow Nov 7, 2024
4b2caf4
fix(plugin-meetings): updated tests
fnowakow Nov 7, 2024
74a1683
fix(plugin-meetings): added unit tests
fnowakow Oct 31, 2024
f7c5436
fix(plugin-meetings): added NOT unit test
fnowakow Oct 31, 2024
a92e0a8
fix(plugin-meetings): updated tests
fnowakow Nov 7, 2024
f6b8bb6
Merge remote-tracking branch 'fork/fnowakow/SPARK-559645-unit_tests' …
fnowakow Nov 7, 2024
1f2e3d2
fix(plugin-meetings): event name change to self
fnowakow Nov 7, 2024
403bf8e
chore(plugin-meetings): add todo
fnowakow Nov 7, 2024
ed9f255
chore(plugin-meetings): multistream support
fnowakow Nov 8, 2024
c956670
chore(plugin-meetings): removed multistream
fnowakow Nov 8, 2024
108d3ad
test(plugin-meetings): update brb tests
Nov 13, 2024
6ab2870
chore: nit updates for logs
Nov 13, 2024
550c66a
test(plugin-meetings): update brb test names
Nov 13, 2024
d20f826
chore: update beRightBack api logic
Nov 13, 2024
231698f
test(plugin-meetings): check brb for both multistream and transcoded
Nov 14, 2024
bd6f757
test(plugin-meetings): finalize brb tests
Nov 14, 2024
31dac59
test(plugin-meetings): remove unnecessary stub
Nov 14, 2024
1685a02
test(plugin-meetings): re-arrange test and add afterEach
Nov 14, 2024
4ca7e41
test(plugin-meetings): remove only
Nov 14, 2024
0056acf
fix(plugin-meetings): log error
Nov 14, 2024
7d58d02
fix(plugin-meetings): rever to return statement
Nov 14, 2024
88d1bb1
fix(plugin-meetings): throw and log error
Nov 15, 2024
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
27 changes: 22 additions & 5 deletions docs/samples/browser-plugin-meetings/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const breakoutsList = document.getElementById('breakouts-list');
const breakoutTable = document.getElementById('breakout-table');
const breakoutHostOperation = document.getElementById('breakout-host-operation');
const getStatsButton = document.getElementById('get-stats');
const tcpReachabilityConfigElm = document.getElementById('enable-tcp-reachability');
const tcpReachabilityConfigElm = document.getElementById('enable-tcp-reachability');
const tlsReachabilityConfigElm = document.getElementById('enable-tls-reachability');

const guestName = document.querySelector('#guest-name');
Expand Down Expand Up @@ -388,7 +388,7 @@ createMeetingSelectElm.addEventListener('change', (event) => {
}
else {
notes.classList.add('hidden');

}
});

Expand Down Expand Up @@ -950,7 +950,7 @@ function cleanUpMedia() {
elem.srcObject.getTracks().forEach((track) => track.stop());
// eslint-disable-next-line no-param-reassign
elem.srcObject = null;

if(elem.id === "local-video") {
clearVideoResolutionCheckInterval(localVideoResElm, localVideoResolutionInterval);
}
Expand Down Expand Up @@ -1566,7 +1566,7 @@ async function stopStartVideo() {
console.error(error);
}
}

}

async function stopStartAudio() {
Expand Down Expand Up @@ -1608,7 +1608,7 @@ async function stopStartAudio() {
console.error(error);
}
}

}

function populateSourceDevices(mediaDevice) {
Expand Down Expand Up @@ -3330,6 +3330,23 @@ function toggleBreakout() {
}
}

async function toggleBrb() {
const meeting = getCurrentMeeting();

if (meeting) {
const enabled = document.getElementById('brb').checked;
try {
const result = await meeting.beRightBack(enabled);
console.log(`meeting.beRightBack(${enabled}): success. Result: ${result}`);
} catch (error) {
console.error(`meeting.beRightBack({${enabled}): error: `, error);
} finally {
localMedia?.microphoneStream?.setUserMuted(enabled);
localMedia?.cameraStream?.setUserMuted(enabled);
}
}
}

const createAdmitDiv = () => {

const containerDiv = document.createElement('div');
Expand Down
8 changes: 8 additions & 0 deletions docs/samples/browser-plugin-meetings/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,14 @@ <h2 class="collapsible">
</div>
</div>

<div>
<fieldset>
<legend>Step Away</legend>
<input type="checkbox" id="brb" onclick="toggleBrb()">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this should be a button and not a checkbox, since it'll be a button in the web client. The button should reflect the brb status of the current user, and would be a good way for us to test whether we are able to accurately show our brb status in the UI, as well as handle any potential race condition that might occur because of it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good comment, and I believe it's worth changing. We can improve this part in this task and not block the RP with this issue. https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-574653

<label for="brb">Enable Step Away</label><br>
</fieldset>
</div>

</div>
</fieldset>
</form>
Expand Down
2 changes: 1 addition & 1 deletion packages/@webex/media-helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"deploy:npm": "yarn npm publish"
},
"dependencies": {
"@webex/internal-media-core": "2.11.3",
"@webex/internal-media-core": "2.12.0",
"@webex/ts-events": "^1.1.0",
"@webex/web-media-effects": "2.19.0"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/@webex/plugin-meetings/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
},
"dependencies": {
"@webex/common": "workspace:*",
"@webex/internal-media-core": "2.11.3",
"@webex/internal-media-core": "2.12.0",
"@webex/internal-plugin-conversation": "workspace:*",
"@webex/internal-plugin-device": "workspace:*",
"@webex/internal-plugin-llm": "workspace:*",
Expand Down
2 changes: 2 additions & 0 deletions packages/@webex/plugin-meetings/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ export const EVENT_TRIGGERS = {
MEETING_SELF_CANNOT_VIEW_PARTICIPANT_LIST: 'meeting:self:cannotViewParticipantList',
MEETING_SELF_IS_SHARING_BLOCKED: 'meeting:self:isSharingBlocked',
MEETING_SELF_ROLES_CHANGED: 'meeting:self:rolesChanged',
MEETING_SELF_BRB_UPDATE: 'meeting:self:brbUpdate',
MEETING_CONTROLS_LAYOUT_UPDATE: 'meeting:layout:update',
MEETING_ENTRY_EXIT_TONE_UPDATE: 'meeting:entryExitTone:update',
MEETING_BREAKOUTS_UPDATE: 'meeting:breakouts:update',
Expand Down Expand Up @@ -695,6 +696,7 @@ export const LOCUSINFO = {
SELF_IS_SHARING_BLOCKED_CHANGE: 'SELF_IS_SHARING_BLOCKED_CHANGE',
SELF_MEETING_BREAKOUTS_CHANGED: 'SELF_MEETING_BREAKOUTS_CHANGED',
SELF_MEETING_INTERPRETATION_CHANGED: 'SELF_MEETING_INTERPRETATION_CHANGED',
SELF_MEETING_BRB_CHANGED: 'SELF_MEETING_BRB_CHANGED',
MEDIA_INACTIVITY: 'MEDIA_INACTIVITY',
LINKS_SERVICES: 'LINKS_SERVICES',
},
Expand Down
13 changes: 13 additions & 0 deletions packages/@webex/plugin-meetings/src/locus-info/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,19 @@ export default class LocusInfo extends EventsScope {
);
}

if (parsedSelves.updates.brbChanged) {
this.emitScoped(
{
file: 'locus-info',
function: 'updateSelf',
},
LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED,
{
brb: parsedSelves.current.brb,
antsukanova marked this conversation as resolved.
Show resolved Hide resolved
}
);
}

if (parsedSelves.updates.interpretationChanged) {
this.emitScoped(
{
Expand Down
6 changes: 6 additions & 0 deletions packages/@webex/plugin-meetings/src/locus-info/selfUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ SelfUtils.parse = (self: any, deviceId: string) => {
breakoutSessions: SelfUtils.getBreakoutSessions(self),
breakout: SelfUtils.getBreakout(self),
interpretation: SelfUtils.getInterpretation(self),
brb: SelfUtils.getBrb(self),
};
}

Expand All @@ -75,6 +76,7 @@ SelfUtils.parse = (self: any, deviceId: string) => {
SelfUtils.getBreakoutSessions = (self) => self?.controls?.breakout?.sessions;
SelfUtils.getBreakout = (self) => self?.controls?.breakout;
SelfUtils.getInterpretation = (self) => self?.controls?.interpretation;
SelfUtils.getBrb = (self) => self?.controls?.brb;

SelfUtils.getLayout = (self) =>
Array.isArray(self?.controls?.layouts) ? self.controls.layouts[0].type : undefined;
Expand Down Expand Up @@ -128,6 +130,7 @@ SelfUtils.getSelves = (oldSelf, newSelf, deviceId) => {
updates.isSharingBlockedChanged = previous?.isSharingBlocked !== current.isSharingBlocked;
updates.breakoutsChanged = SelfUtils.breakoutsChanged(previous, current);
updates.interpretationChanged = SelfUtils.interpretationChanged(previous, current);
updates.brbChanged = SelfUtils.brbChanged(previous, current);

return {
previous,
Expand Down Expand Up @@ -159,6 +162,9 @@ SelfUtils.breakoutsChanged = (previous, current) =>
SelfUtils.interpretationChanged = (previous, current) =>
!isEqual(previous?.interpretation, current?.interpretation) && !!current?.interpretation;

SelfUtils.brbChanged = (previous, current) =>
!isEqual(previous?.brb, current?.brb) && current?.brb !== undefined;

SelfUtils.isMediaInactive = (previous, current) => {
if (
previous &&
Expand Down
44 changes: 44 additions & 0 deletions packages/@webex/plugin-meetings/src/meeting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3290,6 +3290,20 @@ export default class Meeting extends StatelessWebexPlugin {
}
});

this.locusInfo.on(LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED, (payload) => {
Trigger.trigger(
this,
{
file: 'meeting/index',
function: 'setUpLocusInfoSelfListener',
},
EVENT_TRIGGERS.MEETING_SELF_BRB_UPDATE,
{
payload,
}
);
});

this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ROLES_CHANGED, (payload) => {
const isModeratorOrCohost =
payload.newRoles?.includes(SELF_ROLES.MODERATOR) ||
Expand Down Expand Up @@ -3492,6 +3506,36 @@ export default class Meeting extends StatelessWebexPlugin {
return this.members.admitMembers(memberIds, locusUrls);
}

/**
* Manages be right back status updates for the current participant.
*
* @param {boolean} enabled - Indicates whether the user enabled brb or not.
* @returns {Promise<void>} resolves when the brb status is updated or does nothing if not in a multistream meeting.
* @throws {Error} - Throws an error if the request fails.
*/
public beRightBack(enabled: boolean): Promise<void> {
// this logic should be applied only to multistream meetings
if (this.isMultistream && this.mediaProperties.webrtcMediaConnection) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this if statement anymore.

return this.meetingRequest
.sendBrb({
enabled,
locusUrl: this.locusUrl,
deviceUrl: this.deviceUrl,
selfId: this.selfId,
})
.then(() => {
this.sendSlotManager.setSourceStateOverride(MediaType.VideoMain, enabled ? 'away' : null);
})
.catch((error) => {
LoggerProxy.logger.error('Meeting:index#beRightBack --> Error ', error);

return Promise.reject(error);
});
}

return undefined;
edvujic marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Remove the member from the meeting, boot them
* @param {String} memberId
Expand Down
27 changes: 26 additions & 1 deletion packages/@webex/plugin-meetings/src/meeting/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
ANNOTATION,
IP_VERSION,
} from '../constants';
import {SendReactionOptions, ToggleReactionsOptions} from './request.type';
import {SendReactionOptions, BrbOptions, ToggleReactionsOptions} from './request.type';
import MeetingUtil from './util';
import {AnnotationInfo} from '../annotation/annotation.types';

Expand Down Expand Up @@ -916,4 +916,29 @@ export default class MeetingRequest extends StatelessWebexPlugin {
uri: locusUrl,
});
}

/**
* Sends a request to set be right back status.
*
* @param {Object} options - The options for brb request.
* @param {boolean} options.enabled - Whether brb status is enabled.
* @param {string} options.locusUrl - The URL of the locus.
* @param {string} options.deviceUrl - The URL of the device.
* @param {string} options.selfId - The ID of the participant.
* @returns {Promise}
*/
sendBrb({enabled, locusUrl, deviceUrl, selfId}: BrbOptions) {
const uri = `${locusUrl}/${PARTICIPANT}/${selfId}/${CONTROLS}`;

return this.locusDeltaRequest({
method: HTTP_VERBS.PATCH,
uri,
body: {
brb: {
enabled,
deviceUrl,
},
},
});
}
}
7 changes: 7 additions & 0 deletions packages/@webex/plugin-meetings/src/meeting/request.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ export type ToggleReactionsOptions = {
locusUrl: string;
requestingParticipantId: string;
};

export type BrbOptions = {
enabled: boolean;
locusUrl: string;
deviceUrl: string;
selfId: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
LocalStream,
MultistreamRoapMediaConnection,
NamedMediaGroup,
StreamState,
} from '@webex/internal-media-core';

export default class SendSlotManager {
Expand Down Expand Up @@ -83,6 +84,36 @@ export default class SendSlotManager {
);
}

/**
* Sets the source state override for the given media type.
* @param {MediaType} mediaType - The type of media (must be MediaType.VideoMain to apply source state changes).
* @param {StreamState | null} state - The state to set or null to clear the override value.
* @returns {void}
*/
public setSourceStateOverride(mediaType: MediaType, state: StreamState | null) {
if (mediaType !== MediaType.VideoMain) {
throw new Error(
`sendSlotManager cannot set source state override which media type is ${mediaType}`
);
}

const slot = this.slots.get(mediaType);

if (!slot) {
throw new Error(`Slot for ${mediaType} does not exist`);
}

if (state) {
slot.setSourceStateOverride(state);
} else {
slot.clearSourceStateOverride();
}

this.LoggerProxy.logger.info(
`SendSlotsManager->setSourceStateOverride#set source state override for ${mediaType} to ${state}`
);
}

/**
* This method publishes the given stream to the sendSlot for the given mediaType
* @param {MediaType} mediaType MediaType of the sendSlot to which a stream needs to be published (AUDIO_MAIN/VIDEO_MAIN/AUDIO_SLIDES/VIDEO_SLIDES)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,47 @@ describe('plugin-meetings', () => {
});

describe('#updateSelf', () => {
it('should trigger SELF_MEETING_BRB_CHANGED when brb state changed', () => {
locusInfo.self = undefined;

const assertBrb = (enabled) => {
const selfWithBrbChanged = cloneDeep(self);
selfWithBrbChanged.controls.brb = enabled;

locusInfo.emitScoped = sinon.stub();
locusInfo.updateSelf(selfWithBrbChanged, []);

assert.calledWith(
locusInfo.emitScoped,
{ file: 'locus-info', function: 'updateSelf' },
LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED,
{ brb: enabled }
)
};

assertBrb(true);
assertBrb(false);
})

it('should not trigger SELF_MEETING_BRB_CHANGED when brb state did not change', () => {
const assertBrb = (enabled) => {

locusInfo.self = undefined;
locusInfo.emitScoped = sinon.stub();
locusInfo.updateSelf(undefined, []);
edvujic marked this conversation as resolved.
Show resolved Hide resolved

assert.neverCalledWith(
locusInfo.emitScoped,
{ file: 'locus-info', function: 'updateSelf' },
LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED,
{ brb: enabled }
)
};

assertBrb(true);
assertBrb(false);
})

it('should trigger CONTROLS_MEETING_LAYOUT_UPDATED when the meeting layout controls change', () => {
const layoutType = 'EXAMPLE TYPE';

Expand Down
Loading
Loading