diff --git a/lang/main.json b/lang/main.json index a6183abf3..a552db864 100644 --- a/lang/main.json +++ b/lang/main.json @@ -885,6 +885,11 @@ "knockButton": "Ask to Join", "knockTitle": "Someone wants to join the meeting", "nameField": "Enter your name", + "notificationLobbyAccessDenied": "{{targetParticipantName}} has been rejected to join by {{originParticipantName}}", + "notificationLobbyAccessGranted": "{{targetParticipantName}} has been allowed to join by {{originParticipantName}}", + "notificationLobbyDisabled": "Lobby has been disabled by {{originParticipantName}}", + "notificationLobbyEnabled": "Lobby has been enabled by {{originParticipantName}}", + "notificationTitle": "Lobby", "passwordField": "Enter meeting password", "passwordJoinButton": "Join", "reject": "Reject", diff --git a/react/features/lobby/functions.js b/react/features/lobby/functions.js index 151c93340..3db19489e 100644 --- a/react/features/lobby/functions.js +++ b/react/features/lobby/functions.js @@ -1,6 +1,23 @@ // @flow import { getCurrentConference } from '../base/conference'; +import { toState } from '../base/redux'; + +const JID_PATTERN = '[^@]+@[^/]+/(.+)'; + +/** + * Returns a knocking participant by ID or JID. + * + * @param {Function | Object} stateful - The Redux state or a function that resolves to the Redux state. + * @param {string} id - The ID or JID of the participant. + * @returns {Object} + */ +export function getKnockingParticipantById(stateful: Function | Object, id: string): Object { + const { knockingParticipants } = toState(stateful)['features/lobby']; + const idToFind = getIdFromJid(id) || id; + + return knockingParticipants.find(p => p.id === idToFind); +} /** * Approves (lets in) or rejects a knocking participant. @@ -21,3 +38,15 @@ export function setKnockingParticipantApproval(getState: Function, id: string, a } } } + +/** + * Parses an ID from a JID, if a JID is provided, undefined otherwise. + * + * @param {string} jid - The JID to get the ID from. + * @returns {?string} + */ +function getIdFromJid(jid: string): ?string { + const match = new RegExp(JID_PATTERN, 'g').exec(jid) || []; + + return match[1]; +} diff --git a/react/features/lobby/middleware.js b/react/features/lobby/middleware.js index 0542285ba..da974bcec 100644 --- a/react/features/lobby/middleware.js +++ b/react/features/lobby/middleware.js @@ -2,7 +2,7 @@ import { CONFERENCE_FAILED, CONFERENCE_JOINED } from '../base/conference'; import { JitsiConferenceErrors, JitsiConferenceEvents } from '../base/lib-jitsi-meet'; -import { getFirstLoadableAvatarUrl } from '../base/participants'; +import { getFirstLoadableAvatarUrl, getParticipantDisplayName } from '../base/participants'; import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux'; import { NOTIFICATION_TYPE, showNotification } from '../notifications'; import { isPrejoinPageEnabled } from '../prejoin/functions'; @@ -17,6 +17,7 @@ import { startKnocking, setPasswordJoinFailed } from './actions'; +import { getKnockingParticipantById } from './functions'; MiddlewareRegistry.register(store => next => action => { switch (action.type) { @@ -43,7 +44,7 @@ MiddlewareRegistry.register(store => next => action => { */ StateListenerRegistry.register( state => state['features/base/conference'].conference, - (conference, { dispatch }, previousConference) => { + (conference, { dispatch, getState }, previousConference) => { if (conference && !previousConference) { conference.on(JitsiConferenceEvents.MEMBERS_ONLY_CHANGED, enabled => { dispatch(setLobbyModeEnabled(enabled)); @@ -66,6 +67,13 @@ StateListenerRegistry.register( conference.on(JitsiConferenceEvents.LOBBY_USER_LEFT, id => { dispatch(knockingParticipantLeft(id)); }); + + conference.on(JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED, (origin, sender) => + _maybeSendLobbyNotification(origin, sender, { + dispatch, + getState + }) + ); } }); @@ -151,3 +159,43 @@ function _findLoadableAvatarForKnockingParticipant({ dispatch, getState }, { id }); } } + +/** + * Check the endpoint message that arrived through the conference and + * sends a lobby notification, if the message belongs to the feature. + * + * @param {Object} origin - The origin (initiator) of the message. + * @param {Object} message - The actual message. + * @param {Object} store - The Redux store. + * @returns {void} + */ +function _maybeSendLobbyNotification(origin, message, { dispatch, getState }) { + if (!origin?._id || message?.type !== 'lobby-notify') { + return; + } + + const notificationProps: any = { + descriptionArguments: { + originParticipantName: getParticipantDisplayName(getState, origin._id) + }, + titleKey: 'lobby.notificationTitle' + }; + + switch (message.event) { + case 'LOBBY-ENABLED': + notificationProps.descriptionKey = `lobby.notificationLobby${message.value ? 'En' : 'Dis'}abled`; + break; + case 'LOBBY-ACCESS-GRANTED': + notificationProps.descriptionKey = 'lobby.notificationLobbyAccessGranted'; + notificationProps.descriptionArguments.targetParticipantName + = getKnockingParticipantById(getState, message.value)?.name; + break; + case 'LOBBY-ACCESS-DENIED': + notificationProps.descriptionKey = 'lobby.notificationLobbyAccessDenied'; + notificationProps.descriptionArguments.targetParticipantName + = getKnockingParticipantById(getState, message.value)?.name; + break; + } + + dispatch(showNotification(notificationProps, 5000)); +}