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): add brb logic for plugin meetings when current user steps away or returns #4031

Open
wants to merge 6 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
50 changes: 43 additions & 7 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 @@ -3052,6 +3052,11 @@ function moveFromDevice() {
});
}

function isUserSelf(member) {
const meeting = getCurrentMeeting();
return meeting.selfId === member.id
}

function claimPersonalMeetingRoom() {
console.log('DevicesControls#claimPersonalMeetingRoom()');

Expand Down Expand Up @@ -3094,7 +3099,7 @@ participantTable.addEventListener('click', (event) => {
}
const muteButton = document.getElementById('mute-participant-btn')
if (selectedParticipant.isAudioMuted) {
muteButton.innerText = meeting.selfId === selectedParticipant.id ? 'Unmute' : 'Request to unmute';
muteButton.innerText = isUserSelf(selectedParticipant) ? 'Unmute' : 'Request to unmute';
Copy link
Contributor

Choose a reason for hiding this comment

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

Were we observing any issues with this logic, leading to the change?

} else {
muteButton.innerText = 'Mute';
}
Expand Down Expand Up @@ -3330,6 +3335,25 @@ function toggleBreakout() {
}
}

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

if (meeting) {
const brbButton = document.getElementById('brb-btn');
const isBrbEnabled = brbButton.innerText === 'Step away';

try {
const result = await meeting.beRightBack(isBrbEnabled);
console.log(`meeting.beRightBack(${isBrbEnabled}): success. Result: ${result}`);
} catch (error) {
console.error(`meeting.beRightBack({${isBrbEnabled}): error: `, error);
} finally {
localMedia?.microphoneStream?.setUserMuted(isBrbEnabled);
localMedia?.cameraStream?.setUserMuted(isBrbEnabled);
}
}
}

const createAdmitDiv = () => {

const containerDiv = document.createElement('div');
Expand Down Expand Up @@ -3712,18 +3736,21 @@ function createMembersTable(members) {
const th3 = document.createElement('th');
const th4 = document.createElement('th');
const th5 = document.createElement('th');
const th6 = document.createElement('th');

th1.innerText = 'NAME';
th2.innerText = 'VIDEO';
th3.innerText = 'AUDIO';
th4.innerText = 'STATUS';
th5.innerText = 'SUPPORTS BREAKOUTS';
th6.innerText = 'AWAY';

tr.appendChild(th1);
tr.appendChild(th2);
tr.appendChild(th3);
tr.appendChild(th4);
tr.appendChild(th5);
tr.appendChild(th6);

return tr;
}
Expand All @@ -3735,6 +3762,7 @@ function createMembersTable(members) {
const td3 = document.createElement('td');
const td4 = document.createElement('td');
const td5 = document.createElement('td');
const td6 = document.createElement('td');
const label1 = createLabel(member.id);
const label2 = createLabel(member.id, member.isVideoMuted ? 'NO' : 'YES');
const label3 = createLabel(member.id, member.isAudioMuted ? 'NO' : 'YES');
Expand Down Expand Up @@ -3763,11 +3791,19 @@ function createMembersTable(members) {

td5.appendChild(label5);


if (isUserSelf(member) && member.isInMeeting) {
td6.appendChild(createButton(member.isBrb ? 'Back to meeting' : 'Step away', toggleBrb, {id: 'brb-btn'}));
} else {
td6.appendChild(createLabel(member.id, member.isBrb ? 'YES' : 'NO'));
}

tr.appendChild(td1);
tr.appendChild(td2);
tr.appendChild(td3);
tr.appendChild(td4);
tr.appendChild(td5);
tr.appendChild(td6);

return tr;
}
Expand All @@ -3778,7 +3814,7 @@ function createMembersTable(members) {

thead.appendChild(createHeadRow());

Object.entries(members).forEach(([key, value]) => {
Object.entries(members).forEach(([_, value]) => {
if (value.status !== 'NOT_IN_MEETING') {
const row = createRow(value);

Expand Down
1 change: 0 additions & 1 deletion docs/samples/browser-plugin-meetings/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,6 @@ <h2 class="collapsible">
<span id="ts-mute-audio-message" class="webex-warning"></span>
</div>
</div>

</div>
</fieldset>
</form>
Expand Down
6 changes: 3 additions & 3 deletions docs/samples/browser-plugin-meetings/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ button.btn-code {
}

.box {
max-width: 70rem;
max-width: 70rem;
margin-inline: auto;
margin-bottom: 1rem;
}
Expand Down Expand Up @@ -93,7 +93,7 @@ button.btn-code {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
/* border: 10px solid black */
}
}

.video-section {
display: flex;
Expand Down Expand Up @@ -445,4 +445,4 @@ legend {
background-color:lightgrey;
border-radius:5px;
padding: 0 2px;
}
}
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 @@ -700,6 +701,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,
}
);
}

if (parsedSelves.updates.interpretationChanged) {
this.emitScoped(
{
Expand Down
6 changes: 6 additions & 0 deletions packages/@webex/plugin-meetings/src/locus-info/selfUtils.ts
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 see a changeset for selfUtils test file. Could we add them as well?

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
58 changes: 58 additions & 0 deletions packages/@webex/plugin-meetings/src/meeting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3303,6 +3303,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 @@ -3505,6 +3519,50 @@ 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 async beRightBack(enabled: boolean): Promise<void> {
if (!this.isMultistream) {
const errorMessage = 'Meeting:index#beRightBack --> Not a multistream meeting';
const error = new Error(errorMessage);

LoggerProxy.logger.error(error);

return Promise.reject(error);
}

if (!this.mediaProperties.webrtcMediaConnection) {
const errorMessage = 'Meeting:index#beRightBack --> WebRTC media connection is not defined';
const error = new Error(errorMessage);

LoggerProxy.logger.error(error);

return Promise.reject(error);
}

// this logic should be applied only to multistream meetings
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) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not able to find a test for this catch block. Could you point me to the test where this is being tested?

LoggerProxy.logger.error('Meeting:index#beRightBack --> Error ', error);

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

/**
* 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) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would toggleBrb or setBrb be better alternatives for the function name?

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;
};
Loading
Loading