2022-09-21 08:32:50 +00:00
|
|
|
import { IState, IStore } from '../app/types';
|
|
|
|
import { getCurrentConference } from '../base/conference/functions';
|
2019-06-25 22:25:43 +00:00
|
|
|
import {
|
|
|
|
PARTICIPANT_JOINED,
|
2019-06-26 20:14:32 +00:00
|
|
|
PARTICIPANT_LEFT,
|
2022-09-21 08:32:50 +00:00
|
|
|
PARTICIPANT_UPDATED
|
|
|
|
} from '../base/participants/actionTypes';
|
|
|
|
import { PARTICIPANT_ROLE } from '../base/participants/constants';
|
|
|
|
import {
|
2022-09-27 07:10:28 +00:00
|
|
|
getLocalParticipant,
|
2019-06-26 20:14:32 +00:00
|
|
|
getParticipantById,
|
2022-10-06 11:12:57 +00:00
|
|
|
getParticipantDisplayName,
|
|
|
|
isScreenShareParticipant,
|
|
|
|
isWhiteboardParticipant
|
2022-09-21 08:32:50 +00:00
|
|
|
} from '../base/participants/functions';
|
|
|
|
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
|
|
|
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
|
2021-09-10 11:05:16 +00:00
|
|
|
import { PARTICIPANTS_PANE_OPEN } from '../participants-pane/actionTypes';
|
2018-06-14 09:14:32 +00:00
|
|
|
|
2022-02-03 13:17:34 +00:00
|
|
|
import {
|
|
|
|
CLEAR_NOTIFICATIONS,
|
|
|
|
HIDE_NOTIFICATION,
|
|
|
|
SHOW_NOTIFICATION
|
|
|
|
} from './actionTypes';
|
2019-06-25 22:25:43 +00:00
|
|
|
import {
|
|
|
|
clearNotifications,
|
2022-02-03 13:17:34 +00:00
|
|
|
hideNotification,
|
2019-06-26 20:14:32 +00:00
|
|
|
showNotification,
|
2021-12-02 13:17:07 +00:00
|
|
|
showParticipantJoinedNotification,
|
|
|
|
showParticipantLeftNotification
|
2019-06-25 22:25:43 +00:00
|
|
|
} from './actions';
|
2022-02-03 13:17:34 +00:00
|
|
|
import {
|
|
|
|
NOTIFICATION_TIMEOUT_TYPE,
|
|
|
|
RAISE_HAND_NOTIFICATION_ID
|
|
|
|
} from './constants';
|
|
|
|
import { areThereNotifications, joinLeaveNotificationsDisabled } from './functions';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Map of timers.
|
|
|
|
*
|
|
|
|
* @type {Map}
|
|
|
|
*/
|
|
|
|
const timers = new Map();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function that creates a timeout id for specific notification.
|
|
|
|
*
|
|
|
|
* @param {Object} notification - Notification for which we want to create a timeout.
|
|
|
|
* @param {Function} dispatch - The Redux dispatch function.
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2022-09-21 08:32:50 +00:00
|
|
|
const createTimeoutId = (notification: { timeout: number; uid: string; }, dispatch: IStore['dispatch']) => {
|
2022-02-03 13:17:34 +00:00
|
|
|
const {
|
|
|
|
timeout,
|
|
|
|
uid
|
|
|
|
} = notification;
|
|
|
|
|
|
|
|
if (timeout) {
|
|
|
|
const timerID = setTimeout(() => {
|
|
|
|
dispatch(hideNotification(uid));
|
|
|
|
}, timeout);
|
|
|
|
|
|
|
|
timers.set(uid, timerID);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns notifications state.
|
|
|
|
*
|
|
|
|
* @param {Object} state - Global state.
|
|
|
|
* @returns {Array<Object>} - Notifications state.
|
|
|
|
*/
|
2022-09-21 08:32:50 +00:00
|
|
|
const getNotifications = (state: IState) => {
|
2022-02-03 13:17:34 +00:00
|
|
|
const _visible = areThereNotifications(state);
|
|
|
|
const { notifications } = state['features/notifications'];
|
|
|
|
|
|
|
|
return _visible ? notifications : [];
|
|
|
|
};
|
2019-06-26 20:14:32 +00:00
|
|
|
|
2019-06-25 22:25:43 +00:00
|
|
|
/**
|
|
|
|
* Middleware that captures actions to display notifications.
|
|
|
|
*
|
|
|
|
* @param {Store} store - The redux store.
|
|
|
|
* @returns {Function}
|
|
|
|
*/
|
|
|
|
MiddlewareRegistry.register(store => next => action => {
|
2022-02-03 13:17:34 +00:00
|
|
|
const { dispatch, getState } = store;
|
|
|
|
const state = getState();
|
|
|
|
|
2019-06-25 22:25:43 +00:00
|
|
|
switch (action.type) {
|
2022-02-03 13:17:34 +00:00
|
|
|
case CLEAR_NOTIFICATIONS: {
|
|
|
|
if (navigator.product !== 'ReactNative') {
|
|
|
|
const _notifications = getNotifications(state);
|
|
|
|
|
|
|
|
for (const notification of _notifications) {
|
|
|
|
if (timers.has(notification.uid)) {
|
|
|
|
const timeout = timers.get(notification.uid);
|
|
|
|
|
|
|
|
clearTimeout(timeout);
|
|
|
|
timers.delete(notification.uid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
timers.clear();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SHOW_NOTIFICATION: {
|
|
|
|
if (navigator.product !== 'ReactNative') {
|
|
|
|
if (timers.has(action.uid)) {
|
|
|
|
const timer = timers.get(action.uid);
|
|
|
|
|
|
|
|
clearTimeout(timer);
|
|
|
|
timers.delete(action.uid);
|
|
|
|
}
|
|
|
|
|
|
|
|
createTimeoutId(action, dispatch);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case HIDE_NOTIFICATION: {
|
|
|
|
if (navigator.product !== 'ReactNative') {
|
|
|
|
const timer = timers.get(action.uid);
|
|
|
|
|
|
|
|
clearTimeout(timer);
|
|
|
|
timers.delete(action.uid);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2019-06-25 22:25:43 +00:00
|
|
|
case PARTICIPANT_JOINED: {
|
2019-06-26 20:14:32 +00:00
|
|
|
const result = next(action);
|
2019-06-25 22:25:43 +00:00
|
|
|
const { participant: p } = action;
|
2021-09-01 15:54:07 +00:00
|
|
|
const { conference } = state['features/base/conference'];
|
2019-06-25 22:25:43 +00:00
|
|
|
|
2022-09-30 14:50:45 +00:00
|
|
|
// Do not display notifications for the virtual screenshare and whiteboard tiles.
|
2022-04-18 16:34:07 +00:00
|
|
|
if (conference
|
|
|
|
&& !p.local
|
2022-10-06 11:12:57 +00:00
|
|
|
&& !isScreenShareParticipant(p)
|
|
|
|
&& !isWhiteboardParticipant(p)
|
2022-04-18 16:34:07 +00:00
|
|
|
&& !joinLeaveNotificationsDisabled()
|
|
|
|
&& !p.isReplacing) {
|
2020-02-18 16:31:04 +00:00
|
|
|
dispatch(showParticipantJoinedNotification(
|
2021-09-01 15:54:07 +00:00
|
|
|
getParticipantDisplayName(state, p.id)
|
2019-06-25 22:25:43 +00:00
|
|
|
));
|
|
|
|
}
|
2019-06-26 20:14:32 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case PARTICIPANT_LEFT: {
|
2020-01-14 09:41:16 +00:00
|
|
|
if (!joinLeaveNotificationsDisabled()) {
|
|
|
|
const participant = getParticipantById(
|
|
|
|
store.getState(),
|
|
|
|
action.participant.id
|
|
|
|
);
|
2019-06-26 20:14:32 +00:00
|
|
|
|
2022-04-29 14:32:16 +00:00
|
|
|
// Do not display notifications for the virtual screenshare tiles.
|
2022-04-18 16:34:07 +00:00
|
|
|
if (participant
|
|
|
|
&& !participant.local
|
2022-10-06 11:12:57 +00:00
|
|
|
&& !isScreenShareParticipant(participant)
|
|
|
|
&& !isWhiteboardParticipant(participant)
|
2022-04-18 16:34:07 +00:00
|
|
|
&& !action.participant.isReplaced) {
|
2021-12-02 13:17:07 +00:00
|
|
|
dispatch(showParticipantLeftNotification(
|
|
|
|
getParticipantDisplayName(state, participant.id)
|
|
|
|
));
|
2020-01-14 09:41:16 +00:00
|
|
|
}
|
2019-06-26 20:14:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return next(action);
|
2019-06-25 22:25:43 +00:00
|
|
|
}
|
2020-02-18 16:31:04 +00:00
|
|
|
case PARTICIPANT_UPDATED: {
|
2021-09-21 15:38:06 +00:00
|
|
|
const { disableModeratorIndicator } = state['features/base/config'];
|
|
|
|
|
|
|
|
if (disableModeratorIndicator) {
|
2020-02-18 16:31:04 +00:00
|
|
|
return next(action);
|
|
|
|
}
|
|
|
|
|
|
|
|
const { id, role } = action.participant;
|
2021-09-10 11:05:16 +00:00
|
|
|
const localParticipant = getLocalParticipant(state);
|
|
|
|
|
2021-11-10 17:49:53 +00:00
|
|
|
if (localParticipant?.id !== id) {
|
2021-09-10 11:05:16 +00:00
|
|
|
return next(action);
|
|
|
|
}
|
|
|
|
|
2020-03-19 17:40:36 +00:00
|
|
|
const oldParticipant = getParticipantById(state, id);
|
|
|
|
const oldRole = oldParticipant?.role;
|
2020-02-18 16:31:04 +00:00
|
|
|
|
2020-03-19 17:40:36 +00:00
|
|
|
if (oldRole && oldRole !== role && role === PARTICIPANT_ROLE.MODERATOR) {
|
2020-02-18 16:31:04 +00:00
|
|
|
|
|
|
|
store.dispatch(showNotification({
|
2021-09-10 11:05:16 +00:00
|
|
|
titleKey: 'notify.moderator'
|
2020-02-18 16:31:04 +00:00
|
|
|
},
|
2021-11-24 11:05:27 +00:00
|
|
|
NOTIFICATION_TIMEOUT_TYPE.SHORT));
|
2020-02-18 16:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return next(action);
|
|
|
|
}
|
2021-09-10 11:05:16 +00:00
|
|
|
case PARTICIPANTS_PANE_OPEN: {
|
2022-02-03 13:17:34 +00:00
|
|
|
store.dispatch(hideNotification(RAISE_HAND_NOTIFICATION_ID));
|
2021-09-10 11:05:16 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-06-25 22:25:43 +00:00
|
|
|
}
|
|
|
|
|
2019-06-26 20:14:32 +00:00
|
|
|
return next(action);
|
2019-06-25 22:25:43 +00:00
|
|
|
});
|
2018-06-14 09:14:32 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* StateListenerRegistry provides a reliable way to detect the leaving of a
|
|
|
|
* conference, where we need to clean up the notifications.
|
|
|
|
*/
|
|
|
|
StateListenerRegistry.register(
|
|
|
|
/* selector */ state => getCurrentConference(state),
|
|
|
|
/* listener */ (conference, { dispatch }) => {
|
|
|
|
if (!conference) {
|
|
|
|
dispatch(clearNotifications());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|