feat(notifications) revisit timeouts and make them configurable

This commit is contained in:
Tudor D. Pop 2021-11-24 13:05:27 +02:00 committed by GitHub
parent 64e504f349
commit a618697e34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 220 additions and 206 deletions

View File

@ -123,7 +123,11 @@ import {
maybeOpenFeedbackDialog, maybeOpenFeedbackDialog,
submitFeedback submitFeedback
} from './react/features/feedback'; } from './react/features/feedback';
import { isModerationNotificationDisplayed, showNotification } from './react/features/notifications'; import {
isModerationNotificationDisplayed,
showNotification,
NOTIFICATION_TIMEOUT_TYPE
} from './react/features/notifications';
import { mediaPermissionPromptVisibilityChanged, toggleSlowGUMOverlay } from './react/features/overlay'; import { mediaPermissionPromptVisibilityChanged, toggleSlowGUMOverlay } from './react/features/overlay';
import { suspendDetected } from './react/features/power-monitor'; import { suspendDetected } from './react/features/power-monitor';
import { import {
@ -357,7 +361,10 @@ class ConferenceConnector {
case JitsiConferenceErrors.FOCUS_DISCONNECTED: { case JitsiConferenceErrors.FOCUS_DISCONNECTED: {
const [ focus, retrySec ] = params; const [ focus, retrySec ] = params;
APP.UI.notifyFocusDisconnected(focus, retrySec); APP.store.dispatch(showNotification({
descriptionKey: focus,
titleKey: retrySec
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
break; break;
} }
@ -755,7 +762,7 @@ export default {
APP.store.dispatch(showNotification({ APP.store.dispatch(showNotification({
descriptionKey: 'notify.startSilentDescription', descriptionKey: 'notify.startSilentDescription',
titleKey: 'notify.startSilentTitle' titleKey: 'notify.startSilentTitle'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
} }
// XXX The API will take care of disconnecting from the XMPP // XXX The API will take care of disconnecting from the XMPP
@ -2364,7 +2371,12 @@ export default {
} }
Promise.allSettled(promises) Promise.allSettled(promises)
.then(() => APP.UI.notifyInitiallyMuted()); .then(() => {
APP.store.dispatch(showNotification({
titleKey: 'notify.mutedTitle',
descriptionKey: 'notify.muted'
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
});
}); });
room.on( room.on(

View File

@ -375,6 +375,13 @@ var config = {
// resizeDesktopForPresenter: false // resizeDesktopForPresenter: false
// }, // },
// Notification timeouts
// notificationTimeouts: {
// short: 2500,
// medium: 5000,
// long: 10000
// },
// // Options for the recording limit notification. // // Options for the recording limit notification.
// recordingLimit: { // recordingLimit: {
// //

View File

@ -225,13 +225,6 @@ var interfaceConfig = {
*/ */
// ANDROID_APP_PACKAGE: 'org.jitsi.meet', // ANDROID_APP_PACKAGE: 'org.jitsi.meet',
/**
* Override the behavior of some notifications to remain displayed until
* explicitly dismissed through a user action. The value is how long, in
* milliseconds, those notifications should remain displayed.
*/
// ENFORCE_NOTIFICATION_AUTO_DISMISS_TIMEOUT: 15000,
// List of undocumented settings // List of undocumented settings
/** /**
INDICATOR_FONT_SIZES INDICATOR_FONT_SIZES

View File

@ -10,7 +10,12 @@ import { isMobileBrowser } from '../../react/features/base/environment/utils';
import { setColorAlpha } from '../../react/features/base/util'; import { setColorAlpha } from '../../react/features/base/util';
import { setDocumentUrl } from '../../react/features/etherpad'; import { setDocumentUrl } from '../../react/features/etherpad';
import { setFilmstripVisible } from '../../react/features/filmstrip'; import { setFilmstripVisible } from '../../react/features/filmstrip';
import { joinLeaveNotificationsDisabled, setNotificationsEnabled } from '../../react/features/notifications'; import {
joinLeaveNotificationsDisabled,
setNotificationsEnabled,
showNotification,
NOTIFICATION_TIMEOUT_TYPE
} from '../../react/features/notifications';
import { import {
dockToolbox, dockToolbox,
setToolboxEnabled, setToolboxEnabled,
@ -215,12 +220,10 @@ UI.updateUserStatus = (user, status) => {
const displayName = user.getDisplayName(); const displayName = user.getDisplayName();
messageHandler.participantNotification( APP.store.dispatch(showNotification({
displayName, titleKey: `${displayName} connected`,
'', descriptionKey: 'dialOut.statusMessage'
'connected', }, NOTIFICATION_TIMEOUT_TYPE.SHORT));
'dialOut.statusMessage',
{ status: UIUtil.escapeHtml(status) });
}; };
/** /**
@ -333,18 +336,6 @@ UI.notifyMaxUsersLimitReached = function() {
}); });
}; };
/**
* Notify user that he was automatically muted when joned the conference.
*/
UI.notifyInitiallyMuted = function() {
messageHandler.participantNotification(
null,
'notify.mutedTitle',
'connected',
'notify.muted',
null);
};
UI.handleLastNEndpoints = function(leavingIds, enteringIds) { UI.handleLastNEndpoints = function(leavingIds, enteringIds) {
VideoLayout.onLastNEndpointsChanged(leavingIds, enteringIds); VideoLayout.onLastNEndpointsChanged(leavingIds, enteringIds);
}; };
@ -363,15 +354,6 @@ UI.notifyTokenAuthFailed = function() {
}); });
}; };
UI.notifyFocusDisconnected = function(focus, retrySec) {
messageHandler.participantNotification(
null, 'notify.focus',
'disconnected', 'notify.focusFail',
{ component: focus,
ms: retrySec }
);
};
/** /**
* Update list of available physical devices. * Update list of available physical devices.
*/ */

View File

@ -1,9 +1,8 @@
/* global APP */ /* global APP */
import { import {
NOTIFICATION_TIMEOUT, NOTIFICATION_TIMEOUT_TYPE,
showErrorNotification, showErrorNotification,
showNotification,
showWarningNotification showWarningNotification
} from '../../../react/features/notifications'; } from '../../../react/features/notifications';
@ -48,7 +47,7 @@ const messageHandler = {
* showErrorNotification action. * showErrorNotification action.
*/ */
showError(props) { showError(props) {
APP.store.dispatch(showErrorNotification(props)); APP.store.dispatch(showErrorNotification(props, NOTIFICATION_TIMEOUT_TYPE.LONG));
}, },
/** /**
@ -58,35 +57,7 @@ const messageHandler = {
* showWarningNotification action. * showWarningNotification action.
*/ */
showWarning(props) { showWarning(props) {
APP.store.dispatch(showWarningNotification(props)); APP.store.dispatch(showWarningNotification(props, NOTIFICATION_TIMEOUT_TYPE.LONG));
},
/**
* Displays a notification about participant action.
* @param displayName the display name of the participant that is
* associated with the notification.
* @param displayNameKey the key from the language file for the display
* name. Only used if displayName is not provided.
* @param cls css class for the notification
* @param messageKey the key from the language file for the text of the
* message.
* @param messageArguments object with the arguments for the message.
* @param optional configurations for the notification (e.g. timeout)
*/
participantNotification( // eslint-disable-line max-params
displayName,
displayNameKey,
cls,
messageKey,
messageArguments,
timeout = NOTIFICATION_TIMEOUT) {
APP.store.dispatch(showNotification({
descriptionArguments: messageArguments,
descriptionKey: messageKey,
titleKey: displayNameKey,
title: displayName
},
timeout));
} }
}; };

View File

@ -25,7 +25,7 @@ import {
toURLString toURLString
} from '../base/util'; } from '../base/util';
import { isVpaasMeeting } from '../jaas/functions'; import { isVpaasMeeting } from '../jaas/functions';
import { clearNotifications, showNotification } from '../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, clearNotifications, showNotification } from '../notifications';
import { setFatalError } from '../overlay'; import { setFatalError } from '../overlay';
import { import {
@ -328,7 +328,7 @@ export function maybeRedirectToWelcomePage(options: Object = {}) {
dispatch(showNotification({ dispatch(showNotification({
titleArguments: { appName: getName() }, titleArguments: { appName: getName() },
titleKey: 'dialog.thankYou' titleKey: 'dialog.thankYou'
})); }, NOTIFICATION_TIMEOUT_TYPE.STICKY));
} }
// if Welcome page is enabled redirect to welcome page after 3 sec, if // if Welcome page is enabled redirect to welcome page after 3 sec, if

View File

@ -17,6 +17,7 @@ import {
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux'; import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import { playSound, registerSound, unregisterSound } from '../base/sounds'; import { playSound, registerSound, unregisterSound } from '../base/sounds';
import { import {
NOTIFICATION_TIMEOUT_TYPE,
hideNotification, hideNotification,
showNotification showNotification
} from '../notifications'; } from '../notifications';
@ -106,7 +107,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
sticky: true, sticky: true,
titleKey, titleKey,
uid uid
})); }, NOTIFICATION_TIMEOUT_TYPE.STICKY));
break; break;
} }
@ -222,7 +223,7 @@ StateListenerRegistry.register(
sticky: true, sticky: true,
customActionNameKey: 'notify.unmute', customActionNameKey: 'notify.unmute',
customActionHandler: () => dispatch(muteLocal(false, MEDIA_TYPE.AUDIO)) customActionHandler: () => dispatch(muteLocal(false, MEDIA_TYPE.AUDIO))
})); }, NOTIFICATION_TIMEOUT_TYPE.STICKY));
dispatch(playSound(ASKED_TO_UNMUTE_SOUND_ID)); dispatch(playSound(ASKED_TO_UNMUTE_SOUND_ID));
} }
}); });

View File

@ -9,7 +9,7 @@ import {
} from '../../analytics'; } from '../../analytics';
import { reloadNow } from '../../app/actions'; import { reloadNow } from '../../app/actions';
import { openDisplayNamePrompt } from '../../display-name'; import { openDisplayNamePrompt } from '../../display-name';
import { showErrorNotification } from '../../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showErrorNotification } from '../../notifications';
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED, connectionDisconnected } from '../connection'; import { CONNECTION_ESTABLISHED, CONNECTION_FAILED, connectionDisconnected } from '../connection';
import { validateJwt } from '../jwt'; import { validateJwt } from '../jwt';
import { JitsiConferenceErrors } from '../lib-jitsi-meet'; import { JitsiConferenceErrors } from '../lib-jitsi-meet';
@ -129,7 +129,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
dispatch(showErrorNotification({ dispatch(showErrorNotification({
description: reason, description: reason,
titleKey: 'dialog.sessTerminated' titleKey: 'dialog.sessTerminated'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
break; break;
} }
@ -138,7 +138,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
dispatch(showErrorNotification({ dispatch(showErrorNotification({
description: 'Restart initiated because of a bridge failure', description: 'Restart initiated because of a bridge failure',
titleKey: 'dialog.sessionRestarted' titleKey: 'dialog.sessionRestarted'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
} }
break; break;
@ -151,7 +151,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
descriptionArguments: { msg }, descriptionArguments: { msg },
descriptionKey: msg ? 'dialog.connectErrorWithMsg' : 'dialog.connectError', descriptionKey: msg ? 'dialog.connectErrorWithMsg' : 'dialog.connectError',
titleKey: 'connection.CONNFAIL' titleKey: 'connection.CONNFAIL'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
break; break;
} }

View File

@ -172,6 +172,7 @@ export default [
'maxFullResolutionParticipants', 'maxFullResolutionParticipants',
'mouseMoveCallbackInterval', 'mouseMoveCallbackInterval',
'notifications', 'notifications',
'notificationTimeouts',
'openSharedDocumentOnJoin', 'openSharedDocumentOnJoin',
'opusMaxAverageBitrate', 'opusMaxAverageBitrate',
'p2p', 'p2p',

View File

@ -26,7 +26,6 @@ export default [
'DISPLAY_WELCOME_PAGE_CONTENT', 'DISPLAY_WELCOME_PAGE_CONTENT',
'ENABLE_DIAL_OUT', 'ENABLE_DIAL_OUT',
'ENABLE_FEEDBACK_ANIMATION', 'ENABLE_FEEDBACK_ANIMATION',
'ENFORCE_NOTIFICATION_AUTO_DISMISS_TIMEOUT',
'FILM_STRIP_MAX_HEIGHT', 'FILM_STRIP_MAX_HEIGHT',
'GENERATE_ROOMNAMES_ON_WELCOME_PAGE', 'GENERATE_ROOMNAMES_ON_WELCOME_PAGE',
'HIDE_INVITE_MORE_HEADER', 'HIDE_INVITE_MORE_HEADER',

View File

@ -2,7 +2,7 @@
import UIEvents from '../../../../service/UI/UIEvents'; import UIEvents from '../../../../service/UI/UIEvents';
import { processExternalDeviceRequest } from '../../device-selection'; import { processExternalDeviceRequest } from '../../device-selection';
import { showNotification, showWarningNotification } from '../../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showNotification, showWarningNotification } from '../../notifications';
import { replaceAudioTrackById, replaceVideoTrackById, setDeviceStatusWarning } from '../../prejoin/actions'; import { replaceAudioTrackById, replaceVideoTrackById, setDeviceStatusWarning } from '../../prejoin/actions';
import { isPrejoinPageVisible } from '../../prejoin/functions'; import { isPrejoinPageVisible } from '../../prejoin/functions';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app'; import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app';
@ -50,8 +50,6 @@ const JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP = {
} }
}; };
const WARNING_DISPLAY_TIMER = 4000;
/** /**
* A listener for device permissions changed reported from lib-jitsi-meet. * A listener for device permissions changed reported from lib-jitsi-meet.
*/ */
@ -134,7 +132,7 @@ MiddlewareRegistry.register(store => next => action => {
description: additionalCameraErrorMsg, description: additionalCameraErrorMsg,
descriptionKey: cameraErrorMsg, descriptionKey: cameraErrorMsg,
titleKey titleKey
}, WARNING_DISPLAY_TIMER)); }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
if (isPrejoinPageVisible(store.getState())) { if (isPrejoinPageVisible(store.getState())) {
store.dispatch(setDeviceStatusWarning(titleKey)); store.dispatch(setDeviceStatusWarning(titleKey));
@ -163,7 +161,7 @@ MiddlewareRegistry.register(store => next => action => {
description: additionalMicErrorMsg, description: additionalMicErrorMsg,
descriptionKey: micErrorMsg, descriptionKey: micErrorMsg,
titleKey titleKey
}, WARNING_DISPLAY_TIMER)); }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
if (isPrejoinPageVisible(store.getState())) { if (isPrejoinPageVisible(store.getState())) {
store.dispatch(setDeviceStatusWarning(titleKey)); store.dispatch(setDeviceStatusWarning(titleKey));
@ -298,7 +296,7 @@ function _checkAndNotifyForNewDevice(store, newDevices, oldDevices) {
titleKey, titleKey,
customActionNameKey: 'notify.newDeviceAction', customActionNameKey: 'notify.newDeviceAction',
customActionHandler: _useDevice.bind(undefined, store, devicesArray) customActionHandler: _useDevice.bind(undefined, store, devicesArray)
})); }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
} }
}); });
} }

View File

@ -1,4 +1,4 @@
import { NOTIFICATION_TIMEOUT, showNotification } from '../../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../../notifications';
import { set } from '../redux'; import { set } from '../redux';
import { import {
@ -478,7 +478,7 @@ export function participantMutedUs(participant, track) {
titleArguments: { titleArguments: {
participantDisplayName: getParticipantDisplayName(getState, participant.getId()) participantDisplayName: getParticipantDisplayName(getState, participant.getId())
} }
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
}; };
} }
@ -510,7 +510,7 @@ export function participantKicked(kicker, kicked) {
getParticipantDisplayName(getState, kicker.getId()) getParticipantDisplayName(getState, kicker.getId())
}, },
titleKey: 'notify.kickParticipant' titleKey: 'notify.kickParticipant'
}, NOTIFICATION_TIMEOUT * 2)); // leave more time for this }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
}; };
} }

View File

@ -6,7 +6,7 @@ import UIEvents from '../../../../service/UI/UIEvents';
import { approveParticipant } from '../../av-moderation/actions'; import { approveParticipant } from '../../av-moderation/actions';
import { toggleE2EE } from '../../e2ee/actions'; import { toggleE2EE } from '../../e2ee/actions';
import { MAX_MODE } from '../../e2ee/constants'; import { MAX_MODE } from '../../e2ee/constants';
import { NOTIFICATION_TIMEOUT, showNotification } from '../../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../../notifications';
import { isForceMuted } from '../../participants-pane/functions'; import { isForceMuted } from '../../participants-pane/functions';
import { CALLING, INVITED } from '../../presence-status'; import { CALLING, INVITED } from '../../presence-status';
import { RAISE_HAND_SOUND_ID } from '../../reactions/constants'; import { RAISE_HAND_SOUND_ID } from '../../reactions/constants';
@ -562,7 +562,7 @@ function _raiseHandUpdated({ dispatch, getState }, conference, participantId, ne
raiseHandNotification: true, raiseHandNotification: true,
concatText: true, concatText: true,
...action ...action
}, NOTIFICATION_TIMEOUT * (shouldDisplayAllowAction ? 2 : 1))); }, shouldDisplayAllowAction ? NOTIFICATION_TIMEOUT_TYPE.MEDIUM : NOTIFICATION_TIMEOUT_TYPE.SHORT));
dispatch(playSound(RAISE_HAND_SOUND_ID)); dispatch(playSound(RAISE_HAND_SOUND_ID));
} }
} }

View File

@ -4,7 +4,7 @@ import {
createTrackMutedEvent, createTrackMutedEvent,
sendAnalytics sendAnalytics
} from '../../analytics'; } from '../../analytics';
import { showErrorNotification, showNotification } from '../../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showErrorNotification, showNotification } from '../../notifications';
import { JitsiTrackErrors, JitsiTrackEvents, createLocalTrack } from '../lib-jitsi-meet'; import { JitsiTrackErrors, JitsiTrackEvents, createLocalTrack } from '../lib-jitsi-meet';
import { import {
CAMERA_FACING_MODE, CAMERA_FACING_MODE,
@ -245,7 +245,7 @@ export function showNoDataFromSourceVideoError(jitsiTrack) {
const notificationAction = await dispatch(showErrorNotification({ const notificationAction = await dispatch(showErrorNotification({
descriptionKey: 'dialog.cameraNotSendingData', descriptionKey: 'dialog.cameraNotSendingData',
titleKey: 'dialog.cameraNotSendingDataTitle' titleKey: 'dialog.cameraNotSendingDataTitle'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
notificationInfo = { notificationInfo = {
uid: notificationAction.uid uid: notificationAction.uid
@ -397,7 +397,7 @@ export function trackAdded(track) {
const notificationAction = await dispatch(showNotification({ const notificationAction = await dispatch(showNotification({
descriptionKey: 'dialog.micNotSendingData', descriptionKey: 'dialog.micNotSendingData',
titleKey: 'dialog.micNotSendingDataTitle' titleKey: 'dialog.micNotSendingDataTitle'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
// Set the notification ID so that other parts of the application know that this was // Set the notification ID so that other parts of the application know that this was
// displayed in the context of the current device. // displayed in the context of the current device.
@ -406,7 +406,9 @@ export function trackAdded(track) {
noDataFromSourceNotificationInfo = { uid: notificationAction.uid }; noDataFromSourceNotificationInfo = { uid: notificationAction.uid };
} else { } else {
const timeout = setTimeout(() => dispatch(showNoDataFromSourceVideoError(track)), 5000); const timeout = setTimeout(() => dispatch(
showNoDataFromSourceVideoError(track)),
NOTIFICATION_TIMEOUT_TYPE.MEDIUM);
noDataFromSourceNotificationInfo = { timeout }; noDataFromSourceNotificationInfo = { timeout };
} }

View File

@ -4,6 +4,7 @@ import type { Dispatch } from 'redux';
import { getParticipantDisplayName } from '../base/participants'; import { getParticipantDisplayName } from '../base/participants';
import { import {
NOTIFICATION_TIMEOUT_TYPE,
NOTIFICATION_TYPE, NOTIFICATION_TYPE,
showNotification showNotification
} from '../notifications'; } from '../notifications';
@ -34,6 +35,6 @@ export function notifyKickedOut(participant: Object, _: ?Function) { // eslint-d
descriptionArguments: args, descriptionArguments: args,
titleKey: 'dialog.kickTitle', titleKey: 'dialog.kickTitle',
titleArguments: args titleArguments: args
})); }, NOTIFICATION_TIMEOUT_TYPE.STICKY));
}; };
} }

View File

@ -1,6 +1,6 @@
import { isSuboptimalBrowser } from '../base/environment'; import { isSuboptimalBrowser } from '../base/environment';
import { translateToHTML } from '../base/i18n'; import { translateToHTML } from '../base/i18n';
import { showWarningNotification } from '../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showWarningNotification } from '../notifications';
export * from './functions.any'; export * from './functions.any';
@ -24,7 +24,7 @@ export function maybeShowSuboptimalExperienceNotification(dispatch, t) {
recommendedBrowserPageLink: `${window.location.origin}/static/recommendedBrowsers.html` recommendedBrowserPageLink: `${window.location.origin}/static/recommendedBrowsers.html`
} }
) )
} }, NOTIFICATION_TIMEOUT_TYPE.LONG
) )
); );
} }

View File

@ -4,7 +4,7 @@ import { Component } from 'react';
import { createInviteDialogEvent, sendAnalytics } from '../../../analytics'; import { createInviteDialogEvent, sendAnalytics } from '../../../analytics';
import { import {
NOTIFICATION_TIMEOUT, NOTIFICATION_TIMEOUT_TYPE,
showNotification showNotification
} from '../../../notifications'; } from '../../../notifications';
import { invite } from '../../actions'; import { invite } from '../../actions';
@ -203,7 +203,7 @@ export default class AbstractAddPeopleDialog<P: Props, S: State>
if (notificationProps) { if (notificationProps) {
dispatch( dispatch(
showNotification(notificationProps, NOTIFICATION_TIMEOUT)); showNotification(notificationProps, NOTIFICATION_TIMEOUT_TYPE.SHORT));
} }
} }

View File

@ -9,7 +9,11 @@ import { getFirstLoadableAvatarUrl, getParticipantDisplayName } from '../base/pa
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux'; import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import { playSound, registerSound, unregisterSound } from '../base/sounds'; import { playSound, registerSound, unregisterSound } from '../base/sounds';
import { isTestModeEnabled } from '../base/testing'; import { isTestModeEnabled } from '../base/testing';
import { NOTIFICATION_TYPE, showNotification } from '../notifications'; import {
NOTIFICATION_TIMEOUT_TYPE,
NOTIFICATION_TYPE,
showNotification
} from '../notifications';
import { shouldAutoKnock } from '../prejoin/functions'; import { shouldAutoKnock } from '../prejoin/functions';
import { KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED } from './actionTypes'; import { KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED } from './actionTypes';
@ -66,10 +70,12 @@ StateListenerRegistry.register(
conference.on(JitsiConferenceEvents.LOBBY_USER_JOINED, (id, name) => { conference.on(JitsiConferenceEvents.LOBBY_USER_JOINED, (id, name) => {
batch(() => { batch(() => {
dispatch(participantIsKnockingOrUpdated({ dispatch(
participantIsKnockingOrUpdated({
id, id,
name name
})); })
);
dispatch(playSound(KNOCKING_PARTICIPANT_SOUND_ID)); dispatch(playSound(KNOCKING_PARTICIPANT_SOUND_ID));
if (typeof APP !== 'undefined') { if (typeof APP !== 'undefined') {
APP.API.notifyKnockingParticipant({ APP.API.notifyKnockingParticipant({
@ -81,10 +87,12 @@ StateListenerRegistry.register(
}); });
conference.on(JitsiConferenceEvents.LOBBY_USER_UPDATED, (id, participant) => { conference.on(JitsiConferenceEvents.LOBBY_USER_UPDATED, (id, participant) => {
dispatch(participantIsKnockingOrUpdated({ dispatch(
participantIsKnockingOrUpdated({
...participant, ...participant,
id id
})); })
);
}); });
conference.on(JitsiConferenceEvents.LOBBY_USER_LEFT, id => { conference.on(JitsiConferenceEvents.LOBBY_USER_LEFT, id => {
@ -98,7 +106,8 @@ StateListenerRegistry.register(
}) })
); );
} }
}); }
);
/** /**
* Function to handle the conference failed event and navigate the user to the lobby screen * Function to handle the conference failed event and navigate the user to the lobby screen
@ -135,11 +144,13 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
dispatch(hideLobbyScreen()); dispatch(hideLobbyScreen());
if (error.name === JitsiConferenceErrors.CONFERENCE_ACCESS_DENIED) { if (error.name === JitsiConferenceErrors.CONFERENCE_ACCESS_DENIED) {
dispatch(showNotification({ dispatch(
showNotification({
appearance: NOTIFICATION_TYPE.ERROR, appearance: NOTIFICATION_TYPE.ERROR,
hideErrorSupportLink: true, hideErrorSupportLink: true,
titleKey: 'lobby.joinRejectedMessage' titleKey: 'lobby.joinRejectedMessage'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG)
);
} }
return next(action); return next(action);
@ -174,10 +185,12 @@ function _findLoadableAvatarForKnockingParticipant(store, { id }) {
if (!disableThirdPartyRequests && updatedParticipant && !updatedParticipant.loadableAvatarUrl) { if (!disableThirdPartyRequests && updatedParticipant && !updatedParticipant.loadableAvatarUrl) {
getFirstLoadableAvatarUrl(updatedParticipant, store).then(loadableAvatarUrl => { getFirstLoadableAvatarUrl(updatedParticipant, store).then(loadableAvatarUrl => {
if (loadableAvatarUrl) { if (loadableAvatarUrl) {
dispatch(participantIsKnockingOrUpdated({ dispatch(
participantIsKnockingOrUpdated({
loadableAvatarUrl, loadableAvatarUrl,
id id
})); })
);
} }
}); });
} }
@ -217,5 +230,10 @@ function _maybeSendLobbyNotification(origin, message, { dispatch, getState }) {
break; break;
} }
dispatch(showNotification(notificationProps, isTestModeEnabled(getState()) ? undefined : 5000)); dispatch(
showNotification(
notificationProps,
isTestModeEnabled(getState()) ? NOTIFICATION_TIMEOUT_TYPE.STICKY : NOTIFICATION_TIMEOUT_TYPE.MEDIUM
)
);
} }

View File

@ -8,6 +8,7 @@ import { i18next } from '../base/i18n';
import { SET_AUDIO_MUTED } from '../base/media/actionTypes'; import { SET_AUDIO_MUTED } from '../base/media/actionTypes';
import { MiddlewareRegistry } from '../base/redux'; import { MiddlewareRegistry } from '../base/redux';
import { SETTINGS_UPDATED } from '../base/settings/actionTypes'; import { SETTINGS_UPDATED } from '../base/settings/actionTypes';
import { NOTIFICATION_TIMEOUT_TYPE } from '../notifications';
import { showNotification } from '../notifications/actions'; import { showNotification } from '../notifications/actions';
import { localRecordingEngaged, localRecordingUnengaged } from './actions'; import { localRecordingEngaged, localRecordingUnengaged } from './actions';
@ -48,14 +49,14 @@ MiddlewareRegistry.register(({ getState, dispatch }) => next => action => {
dispatch(showNotification({ dispatch(showNotification({
titleKey: 'localRecording.localRecording', titleKey: 'localRecording.localRecording',
description: i18next.t(messageKey, messageParams) description: i18next.t(messageKey, messageParams)
}, 10000)); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
}; };
recordingController.onNotify = (messageKey, messageParams) => { recordingController.onNotify = (messageKey, messageParams) => {
dispatch(showNotification({ dispatch(showNotification({
titleKey: 'localRecording.localRecording', titleKey: 'localRecording.localRecording',
description: i18next.t(messageKey, messageParams) description: i18next.t(messageKey, messageParams)
}, 10000)); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
}; };
typeof APP === 'object' && typeof APP.keyboardshortcut === 'object' typeof APP === 'object' && typeof APP.keyboardshortcut === 'object'

View File

@ -12,7 +12,7 @@ import JitsiMeetJS, { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import { MiddlewareRegistry } from '../base/redux'; import { MiddlewareRegistry } from '../base/redux';
import { updateSettings } from '../base/settings'; import { updateSettings } from '../base/settings';
import { playSound, registerSound, unregisterSound } from '../base/sounds'; import { playSound, registerSound, unregisterSound } from '../base/sounds';
import { hideNotification, showNotification } from '../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, hideNotification, showNotification } from '../notifications';
import { setNoAudioSignalNotificationUid } from './actions'; import { setNoAudioSignalNotificationUid } from './actions';
import DialInLink from './components/DialInLink'; import DialInLink from './components/DialInLink';
@ -114,7 +114,7 @@ async function _handleNoAudioSignalNotification({ dispatch, getState }, action)
descriptionKey, descriptionKey,
customActionNameKey, customActionNameKey,
customActionHandler customActionHandler
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
dispatch(playSound(NO_AUDIO_SIGNAL_SOUND_ID)); dispatch(playSound(NO_AUDIO_SIGNAL_SOUND_ID));

View File

@ -5,7 +5,7 @@ import { CONFERENCE_JOINED } from '../base/conference';
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet'; import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import { MiddlewareRegistry } from '../base/redux'; import { MiddlewareRegistry } from '../base/redux';
import { playSound, registerSound, unregisterSound } from '../base/sounds'; import { playSound, registerSound, unregisterSound } from '../base/sounds';
import { hideNotification, showNotification } from '../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, hideNotification, showNotification } from '../notifications';
import { setNoisyAudioInputNotificationUid } from './actions'; import { setNoisyAudioInputNotificationUid } from './actions';
import { NOISY_AUDIO_INPUT_SOUND_ID } from './constants'; import { NOISY_AUDIO_INPUT_SOUND_ID } from './constants';
@ -41,7 +41,7 @@ MiddlewareRegistry.register(store => next => action => {
const notification = await dispatch(showNotification({ const notification = await dispatch(showNotification({
titleKey: 'toolbar.noisyAudioInputTitle', titleKey: 'toolbar.noisyAudioInputTitle',
descriptionKey: 'toolbar.noisyAudioInputDesc' descriptionKey: 'toolbar.noisyAudioInputDesc'
})); }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
dispatch(playSound(NOISY_AUDIO_INPUT_SOUND_ID)); dispatch(playSound(NOISY_AUDIO_INPUT_SOUND_ID));

View File

@ -14,11 +14,31 @@ import {
SHOW_NOTIFICATION SHOW_NOTIFICATION
} from './actionTypes'; } from './actionTypes';
import { import {
NOTIFICATION_TIMEOUT_TYPE,
NOTIFICATION_TIMEOUT, NOTIFICATION_TIMEOUT,
NOTIFICATION_TYPE, NOTIFICATION_TYPE,
SILENT_JOIN_THRESHOLD SILENT_JOIN_THRESHOLD
} from './constants'; } from './constants';
/**
* Function that returns notification timeout value based on notification timeout type.
*
* @param {string} type - Notification type.
* @param {Object} notificationTimeouts - Config notification timeouts.
* @returns {number}
*/
function getNotificationTimeout(type: ?string, notificationTimeouts: ?Object) {
if (type === NOTIFICATION_TIMEOUT_TYPE.SHORT) {
return notificationTimeouts?.short ?? NOTIFICATION_TIMEOUT.SHORT;
} else if (type === NOTIFICATION_TIMEOUT_TYPE.MEDIUM) {
return notificationTimeouts?.medium ?? NOTIFICATION_TIMEOUT.MEDIUM;
} else if (type === NOTIFICATION_TIMEOUT_TYPE.LONG) {
return notificationTimeouts?.long ?? NOTIFICATION_TIMEOUT.LONG;
}
return NOTIFICATION_TIMEOUT.STICKY;
}
/** /**
* Clears (removes) all the notifications. * Clears (removes) all the notifications.
* *
@ -82,26 +102,26 @@ export function setNotificationsEnabled(enabled: boolean) {
* Queues an error notification for display. * Queues an error notification for display.
* *
* @param {Object} props - The props needed to show the notification component. * @param {Object} props - The props needed to show the notification component.
* @param {string} type - Notification type.
* @returns {Object} * @returns {Object}
*/ */
export function showErrorNotification(props: Object) { export function showErrorNotification(props: Object, type: ?string) {
return showNotification({ return showNotification({
...props, ...props,
appearance: NOTIFICATION_TYPE.ERROR appearance: NOTIFICATION_TYPE.ERROR
}); }, type);
} }
/** /**
* Queues a notification for display. * Queues a notification for display.
* *
* @param {Object} props - The props needed to show the notification component. * @param {Object} props - The props needed to show the notification component.
* @param {number} timeout - How long the notification should display before * @param {string} type - Notification type.
* automatically being hidden.
* @returns {Function} * @returns {Function}
*/ */
export function showNotification(props: Object = {}, timeout: ?number) { export function showNotification(props: Object = {}, type: ?string) {
return function(dispatch: Function, getState: Function) { return function(dispatch: Function, getState: Function) {
const { notifications } = getState()['features/base/config']; const { notifications, notificationTimeouts } = getState()['features/base/config'];
const enabledFlag = getFeatureFlag(getState(), NOTIFICATIONS_ENABLED, true); const enabledFlag = getFeatureFlag(getState(), NOTIFICATIONS_ENABLED, true);
const shouldDisplay = enabledFlag const shouldDisplay = enabledFlag
@ -113,7 +133,7 @@ export function showNotification(props: Object = {}, timeout: ?number) {
return dispatch({ return dispatch({
type: SHOW_NOTIFICATION, type: SHOW_NOTIFICATION,
props, props,
timeout, timeout: getNotificationTimeout(type, notificationTimeouts),
uid: props.uid || window.Date.now().toString() uid: props.uid || window.Date.now().toString()
}); });
} }
@ -124,15 +144,15 @@ export function showNotification(props: Object = {}, timeout: ?number) {
* Queues a warning notification for display. * Queues a warning notification for display.
* *
* @param {Object} props - The props needed to show the notification component. * @param {Object} props - The props needed to show the notification component.
* @param {number} timeout - How long the notification should display before * @param {string} type - Notification type.
* automatically being hidden.
* @returns {Object} * @returns {Object}
*/ */
export function showWarningNotification(props: Object, timeout: ?number) { export function showWarningNotification(props: Object, type: ?string) {
return showNotification({ return showNotification({
...props, ...props,
appearance: NOTIFICATION_TYPE.WARNING appearance: NOTIFICATION_TYPE.WARNING
}, timeout); }, type);
} }
/** /**
@ -192,7 +212,7 @@ const _throttledNotifyParticipantConnected = throttle((dispatch: Dispatch<any>,
if (notificationProps) { if (notificationProps) {
dispatch( dispatch(
showNotification(notificationProps, NOTIFICATION_TIMEOUT)); showNotification(notificationProps, NOTIFICATION_TIMEOUT_TYPE.SHORT));
} }
joinedParticipantsNames = []; joinedParticipantsNames = [];

View File

@ -13,8 +13,6 @@ import { areThereNotifications } from '../../functions';
import Notification from './Notification'; import Notification from './Notification';
declare var interfaceConfig: Object;
type Props = { type Props = {
/** /**
@ -33,12 +31,6 @@ type Props = {
*/ */
_notifications: Array<Object>, _notifications: Array<Object>,
/**
* The length, in milliseconds, to use as a default timeout for all
* dismissible timeouts that do not have a timeout specified.
*/
autoDismissTimeout: number,
/** /**
* JSS classes object. * JSS classes object.
*/ */
@ -260,14 +252,14 @@ class NotificationsContainer extends Component<Props> {
* @returns {void} * @returns {void}
*/ */
_updateTimeouts() { _updateTimeouts() {
const { _notifications, autoDismissTimeout } = this.props; const { _notifications } = this.props;
for (const notification of _notifications) { for (const notification of _notifications) {
if ((notification.timeout || typeof autoDismissTimeout === 'number') if (notification.timeout
&& notification.props.isDismissAllowed !== false && notification.props.isDismissAllowed !== false
&& !this._timeouts.has(notification.uid)) { && !this._timeouts.has(notification.uid)) {
const { const {
timeout = autoDismissTimeout, timeout,
uid uid
} = notification; } = notification;
const timerID = setTimeout(() => { const timerID = setTimeout(() => {
@ -296,8 +288,7 @@ function _mapStateToProps(state) {
return { return {
_iAmSipGateway: Boolean(iAmSipGateway), _iAmSipGateway: Boolean(iAmSipGateway),
_isChatOpen: isChatOpen, _isChatOpen: isChatOpen,
_notifications: _visible ? notifications : [], _notifications: _visible ? notifications : []
autoDismissTimeout: interfaceConfig.ENFORCE_NOTIFICATION_AUTO_DISMISS_TIMEOUT
}; };
} }

View File

@ -3,7 +3,22 @@
/** /**
* The standard time when auto-disappearing notifications should disappear. * The standard time when auto-disappearing notifications should disappear.
*/ */
export const NOTIFICATION_TIMEOUT = 2500; export const NOTIFICATION_TIMEOUT = {
SHORT: 2500,
MEDIUM: 5000,
LONG: 10000,
STICKY: false
};
/**
* Notification timeout type.
*/
export const NOTIFICATION_TIMEOUT_TYPE = {
SHORT: 'short',
MEDIUM: 'medium',
LONG: 'long',
STICKY: 'sticky'
};
/** /**
* The set of possible notification types. * The set of possible notification types.

View File

@ -19,7 +19,7 @@ import {
showNotification, showNotification,
showParticipantJoinedNotification showParticipantJoinedNotification
} from './actions'; } from './actions';
import { NOTIFICATION_TIMEOUT } from './constants'; import { NOTIFICATION_TIMEOUT_TYPE } from './constants';
import { joinLeaveNotificationsDisabled } from './functions'; import { joinLeaveNotificationsDisabled } from './functions';
declare var interfaceConfig: Object; declare var interfaceConfig: Object;
@ -54,15 +54,12 @@ MiddlewareRegistry.register(store => next => action => {
action.participant.id action.participant.id
); );
if (typeof interfaceConfig === 'object' if (participant && !participant.local && !action.participant.isReplaced) {
&& participant
&& !participant.local
&& !action.participant.isReplaced) {
store.dispatch(showNotification({ store.dispatch(showNotification({
descriptionKey: 'notify.disconnected', descriptionKey: 'notify.disconnected',
titleKey: 'notify.somebody', titleKey: 'notify.somebody',
title: participant.name title: participant.name
}, NOTIFICATION_TIMEOUT)); }, NOTIFICATION_TIMEOUT_TYPE.SHORT));
} }
} }
@ -91,7 +88,7 @@ MiddlewareRegistry.register(store => next => action => {
store.dispatch(showNotification({ store.dispatch(showNotification({
titleKey: 'notify.moderator' titleKey: 'notify.moderator'
}, },
NOTIFICATION_TIMEOUT)); NOTIFICATION_TIMEOUT_TYPE.SHORT));
} }
return next(action); return next(action);

View File

@ -4,7 +4,7 @@ import React from 'react';
import { APP_WILL_MOUNT } from '../base/app'; import { APP_WILL_MOUNT } from '../base/app';
import { MiddlewareRegistry } from '../base/redux'; import { MiddlewareRegistry } from '../base/redux';
import { showErrorNotification } from '../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showErrorNotification } from '../notifications';
import { OldElectronAPPNotificationDescription } from './components'; import { OldElectronAPPNotificationDescription } from './components';
import { isOldJitsiMeetElectronApp } from './functions'; import { isOldJitsiMeetElectronApp } from './functions';
@ -36,7 +36,7 @@ function _appWillMount(store, next, action) {
dispatch(showErrorNotification({ dispatch(showErrorNotification({
titleKey: 'notify.OldElectronAPPTitle', titleKey: 'notify.OldElectronAPPTitle',
description: <OldElectronAPPNotificationDescription /> description: <OldElectronAPPNotificationDescription />
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
} }
return next(action); return next(action);

View File

@ -4,7 +4,7 @@ import { getCurrentConference } from '../base/conference';
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet'; import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import { StateListenerRegistry } from '../base/redux'; import { StateListenerRegistry } from '../base/redux';
import { import {
NOTIFICATION_TIMEOUT, NOTIFICATION_TIMEOUT_TYPE,
NOTIFICATION_TYPE, NOTIFICATION_TYPE,
showNotification showNotification
} from '../notifications'; } from '../notifications';
@ -83,7 +83,7 @@ StateListenerRegistry.register(
appearance: NOTIFICATION_TYPE.NORMAL, appearance: NOTIFICATION_TYPE.NORMAL,
titleKey: 'polls.notification.title', titleKey: 'polls.notification.title',
descriptionKey: 'polls.notification.description' descriptionKey: 'polls.notification.description'
}, NOTIFICATION_TIMEOUT)); }, NOTIFICATION_TIMEOUT_TYPE.SHORT));
break; break;
} }

View File

@ -19,7 +19,7 @@ import {
} from '../base/tracks'; } from '../base/tracks';
import { openURLInBrowser } from '../base/util'; import { openURLInBrowser } from '../base/util';
import { executeDialOutRequest, executeDialOutStatusRequest, getDialInfoPageURL } from '../invite/functions'; import { executeDialOutRequest, executeDialOutStatusRequest, getDialInfoPageURL } from '../invite/functions';
import { showErrorNotification } from '../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showErrorNotification } from '../notifications';
import { import {
PREJOIN_JOINING_IN_PROGRESS, PREJOIN_JOINING_IN_PROGRESS,
@ -114,7 +114,7 @@ function pollForStatus(
case DIAL_OUT_STATUS.DISCONNECTED: { case DIAL_OUT_STATUS.DISCONNECTED: {
dispatch(showErrorNotification({ dispatch(showErrorNotification({
titleKey: 'prejoin.errorDialOutDisconnected' titleKey: 'prejoin.errorDialOutDisconnected'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
return onFail(); return onFail();
} }
@ -122,7 +122,7 @@ function pollForStatus(
case DIAL_OUT_STATUS.FAILED: { case DIAL_OUT_STATUS.FAILED: {
dispatch(showErrorNotification({ dispatch(showErrorNotification({
titleKey: 'prejoin.errorDialOutFailed' titleKey: 'prejoin.errorDialOutFailed'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
return onFail(); return onFail();
} }
@ -130,7 +130,7 @@ function pollForStatus(
} catch (err) { } catch (err) {
dispatch(showErrorNotification({ dispatch(showErrorNotification({
titleKey: 'prejoin.errorDialOutStatus' titleKey: 'prejoin.errorDialOutStatus'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
logger.error('Error getting dial out status', err); logger.error('Error getting dial out status', err);
onFail(); onFail();
} }
@ -183,7 +183,7 @@ export function dialOut(onSuccess: Function, onFail: Function) {
} }
} }
dispatch(showErrorNotification(notification)); dispatch(showErrorNotification(notification, NOTIFICATION_TIMEOUT_TYPE.LONG));
logger.error('Error dialing out', err); logger.error('Error dialing out', err);
onFail(); onFail();
} }

View File

@ -9,7 +9,7 @@ import { MiddlewareRegistry } from '../base/redux';
import { SETTINGS_UPDATED, updateSettings } from '../base/settings'; import { SETTINGS_UPDATED, updateSettings } from '../base/settings';
import { playSound, registerSound, unregisterSound } from '../base/sounds'; import { playSound, registerSound, unregisterSound } from '../base/sounds';
import { getDisabledSounds } from '../base/sounds/functions.any'; import { getDisabledSounds } from '../base/sounds/functions.any';
import { NOTIFICATION_TIMEOUT, showNotification } from '../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../notifications';
import { import {
ADD_REACTION_BUFFER, ADD_REACTION_BUFFER,
@ -169,7 +169,7 @@ MiddlewareRegistry.register(store => next => action => {
customActionHandler: () => dispatch(updateSettings({ customActionHandler: () => dispatch(updateSettings({
soundsReactions: false soundsReactions: false
})) }))
}, NOTIFICATION_TIMEOUT)); }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
break; break;
} }
} }

View File

@ -6,7 +6,7 @@ import { getLocalParticipant, getParticipantDisplayName } from '../base/particip
import { copyText } from '../base/util/helpers'; import { copyText } from '../base/util/helpers';
import { getVpaasTenant, isVpaasMeeting } from '../jaas/functions'; import { getVpaasTenant, isVpaasMeeting } from '../jaas/functions';
import { import {
NOTIFICATION_TIMEOUT, NOTIFICATION_TIMEOUT_TYPE,
hideNotification, hideNotification,
showErrorNotification, showErrorNotification,
showNotification, showNotification,
@ -98,7 +98,7 @@ export function showPendingRecordingNotification(streamType: string) {
const notification = await dispatch(showNotification({ const notification = await dispatch(showNotification({
isDismissAllowed: false, isDismissAllowed: false,
...dialogProps ...dialogProps
})); }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
if (notification) { if (notification) {
dispatch(_setPendingRecordingNotificationUid(notification.uid, streamType)); dispatch(_setPendingRecordingNotificationUid(notification.uid, streamType));
@ -113,7 +113,7 @@ export function showPendingRecordingNotification(streamType: string) {
* @returns {showErrorNotification} * @returns {showErrorNotification}
*/ */
export function showRecordingError(props: Object) { export function showRecordingError(props: Object) {
return showErrorNotification(props); return showErrorNotification(props, NOTIFICATION_TIMEOUT_TYPE.LONG);
} }
/** /**
@ -149,7 +149,7 @@ export function showStoppedRecordingNotification(streamType: string, participant
titleKey: 'dialog.recording' titleKey: 'dialog.recording'
}; };
return showNotification(dialogProps, NOTIFICATION_TIMEOUT); return showNotification(dialogProps, NOTIFICATION_TIMEOUT_TYPE.SHORT);
} }
/** /**
@ -214,14 +214,14 @@ export function showStartedRecordingNotification(
} catch (err) { } catch (err) {
dispatch(showErrorNotification({ dispatch(showErrorNotification({
titleKey: 'recording.errorFetchingLink' titleKey: 'recording.errorFetchingLink'
})); }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
return logger.error('Could not fetch recording link', err); return logger.error('Could not fetch recording link', err);
} }
} }
} }
dispatch(showNotification(dialogProps, NOTIFICATION_TIMEOUT)); dispatch(showNotification(dialogProps, NOTIFICATION_TIMEOUT_TYPE.SHORT));
}; };
} }

View File

@ -1,7 +1,7 @@
// @flow // @flow
import JitsiMeetJS from '../base/lib-jitsi-meet'; import JitsiMeetJS from '../base/lib-jitsi-meet';
import { showNotification } from '../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../notifications';
export * from './actions.any'; export * from './actions.any';
@ -37,6 +37,6 @@ export function showRecordingLimitNotification(streamType: string) {
descriptionKey, descriptionKey,
titleKey, titleKey,
maxLines: 2 maxLines: 2
}, 10000)); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
}; };
} }

View File

@ -3,7 +3,7 @@
import React from 'react'; import React from 'react';
import JitsiMeetJS from '../base/lib-jitsi-meet'; import JitsiMeetJS from '../base/lib-jitsi-meet';
import { showNotification } from '../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../notifications';
import { RecordingLimitNotificationDescription } from './components'; import { RecordingLimitNotificationDescription } from './components';
@ -23,5 +23,5 @@ export function showRecordingLimitNotification(streamType: string) {
return showNotification({ return showNotification({
description: <RecordingLimitNotificationDescription isLiveStreaming = { isLiveStreaming } />, description: <RecordingLimitNotificationDescription isLiveStreaming = { isLiveStreaming } />,
titleKey: isLiveStreaming ? 'dialog.liveStreaming' : 'dialog.recording' titleKey: isLiveStreaming ? 'dialog.liveStreaming' : 'dialog.recording'
}, 10000); }, NOTIFICATION_TIMEOUT_TYPE.LONG);
} }

View File

@ -13,7 +13,7 @@ import {
getNewAccessToken, getNewAccessToken,
updateDropboxToken updateDropboxToken
} from '../../../dropbox'; } from '../../../dropbox';
import { showErrorNotification } from '../../../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showErrorNotification } from '../../../notifications';
import { toggleRequestingSubtitles } from '../../../subtitles'; import { toggleRequestingSubtitles } from '../../../subtitles';
import { setSelectedRecordingService } from '../../actions'; import { setSelectedRecordingService } from '../../actions';
import { RECORDING_TYPES } from '../../constants'; import { RECORDING_TYPES } from '../../constants';
@ -298,7 +298,7 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
} else { } else {
dispatch(showErrorNotification({ dispatch(showErrorNotification({
titleKey: 'dialog.noDropboxToken' titleKey: 'dialog.noDropboxToken'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
return; return;
} }

View File

@ -4,7 +4,7 @@ import { openDialog } from '../base/dialog';
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet'; import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import { getParticipantDisplayName, getPinnedParticipant, pinParticipant } from '../base/participants'; import { getParticipantDisplayName, getPinnedParticipant, pinParticipant } from '../base/participants';
import { getLocalVideoTrack } from '../base/tracks'; import { getLocalVideoTrack } from '../base/tracks';
import { showNotification } from '../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../notifications';
import { import {
CAPTURE_EVENTS, CAPTURE_EVENTS,
@ -190,7 +190,7 @@ export function processPermissionRequestReply(participantId: string, event: Obje
descriptionArguments: { user: getParticipantDisplayName(state, participantId) }, descriptionArguments: { user: getParticipantDisplayName(state, participantId) },
descriptionKey, descriptionKey,
titleKey: 'dialog.remoteControlTitle' titleKey: 'dialog.remoteControlTitle'
})); }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
if (permissionGranted) { if (permissionGranted) {
// the remote control permissions has been granted // the remote control permissions has been granted
@ -269,7 +269,7 @@ export function stopController(notifyRemoteParty: boolean = false) {
dispatch(showNotification({ dispatch(showNotification({
descriptionKey: 'dialog.remoteControlStopMessage', descriptionKey: 'dialog.remoteControlStopMessage',
titleKey: 'dialog.remoteControlTitle' titleKey: 'dialog.remoteControlTitle'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
}; };
} }
@ -425,7 +425,7 @@ export function stopReceiver(dontNotifyLocalParty: boolean = false, dontNotifyRe
dispatch(showNotification({ dispatch(showNotification({
descriptionKey: 'dialog.remoteControlStopMessage', descriptionKey: 'dialog.remoteControlStopMessage',
titleKey: 'dialog.remoteControlTitle' titleKey: 'dialog.remoteControlTitle'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
} }
}; };
} }
@ -564,7 +564,7 @@ export function grant(participantId: string) {
dispatch(showNotification({ dispatch(showNotification({
descriptionKey: 'dialog.startRemoteControlErrorMessage', descriptionKey: 'dialog.startRemoteControlErrorMessage',
titleKey: 'dialog.remoteControlTitle' titleKey: 'dialog.remoteControlTitle'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
dispatch(stopReceiver(true)); dispatch(stopReceiver(true));
}); });

View File

@ -11,7 +11,7 @@ import { hideDialog } from '../base/dialog';
import { JitsiConferenceErrors } from '../base/lib-jitsi-meet'; import { JitsiConferenceErrors } from '../base/lib-jitsi-meet';
import { MiddlewareRegistry } from '../base/redux'; import { MiddlewareRegistry } from '../base/redux';
import { import {
NOTIFICATION_TIMEOUT, NOTIFICATION_TIMEOUT_TYPE,
showNotification showNotification
} from '../notifications'; } from '../notifications';
@ -54,12 +54,12 @@ MiddlewareRegistry.register(store => next => action => {
store.dispatch( store.dispatch(
showNotification({ showNotification({
titleKey: 'notify.passwordSetRemotely' titleKey: 'notify.passwordSetRemotely'
}, NOTIFICATION_TIMEOUT)); }, NOTIFICATION_TIMEOUT_TYPE.SHORT));
} else if (previousLockedState === LOCKED_REMOTELY && !currentLockedState) { } else if (previousLockedState === LOCKED_REMOTELY && !currentLockedState) {
store.dispatch( store.dispatch(
showNotification({ showNotification({
titleKey: 'notify.passwordRemovedRemotely' titleKey: 'notify.passwordRemovedRemotely'
}, NOTIFICATION_TIMEOUT)); }, NOTIFICATION_TIMEOUT_TYPE.SHORT));
} }
return result; return result;

View File

@ -9,6 +9,7 @@ import { getCurrentConference } from '../../../base/conference';
import { MEDIA_TYPE } from '../../../base/media'; import { MEDIA_TYPE } from '../../../base/media';
import { getLocalParticipant } from '../../../base/participants'; import { getLocalParticipant } from '../../../base/participants';
import { isLocalTrackMuted } from '../../../base/tracks'; import { isLocalTrackMuted } from '../../../base/tracks';
import { NOTIFICATION_TIMEOUT_TYPE } from '../../../notifications';
import { showWarningNotification } from '../../../notifications/actions'; import { showWarningNotification } from '../../../notifications/actions';
import { dockToolbox } from '../../../toolbox/actions.web'; import { dockToolbox } from '../../../toolbox/actions.web';
import { muteLocal } from '../../../video-menu/actions.any'; import { muteLocal } from '../../../video-menu/actions.any';
@ -424,7 +425,7 @@ export function _mapDispatchToProps(dispatch: Function): $Shape<Props> {
_displayWarning: () => { _displayWarning: () => {
dispatch(showWarningNotification({ dispatch(showWarningNotification({
titleKey: 'dialog.shareVideoLinkError' titleKey: 'dialog.shareVideoLinkError'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
}, },
_dockToolbox: value => { _dockToolbox: value => {
dispatch(dockToolbox(value)); dispatch(dockToolbox(value));

View File

@ -1,5 +1,6 @@
// @flow // @flow
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications';
import { showWarningNotification } from '../../notifications/actions'; import { showWarningNotification } from '../../notifications/actions';
import { timeout } from '../../virtual-background/functions'; import { timeout } from '../../virtual-background/functions';
import logger from '../../virtual-background/logger'; import logger from '../../virtual-background/logger';
@ -62,13 +63,13 @@ export async function createVirtualBackgroundEffect(virtualBackground: Object, d
logger.error('Failed to download tflite model!'); logger.error('Failed to download tflite model!');
dispatch(showWarningNotification({ dispatch(showWarningNotification({
titleKey: 'virtualBackground.backgroundEffectError' titleKey: 'virtualBackground.backgroundEffectError'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
} else { } else {
logger.error('Looks like WebAssembly is disabled or not supported on this browser'); logger.error('Looks like WebAssembly is disabled or not supported on this browser');
dispatch(showWarningNotification({ dispatch(showWarningNotification({
titleKey: 'virtualBackground.webAssemblyWarning', titleKey: 'virtualBackground.webAssemblyWarning',
description: 'WebAssembly disabled or not supported by this browser' description: 'WebAssembly disabled or not supported by this browser'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
} }
return; return;

View File

@ -8,6 +8,7 @@ import { getLocalParticipant, raiseHand } from '../base/participants';
import { MiddlewareRegistry } from '../base/redux'; import { MiddlewareRegistry } from '../base/redux';
import { playSound, registerSound, unregisterSound } from '../base/sounds'; import { playSound, registerSound, unregisterSound } from '../base/sounds';
import { import {
NOTIFICATION_TIMEOUT_TYPE,
hideNotification, hideNotification,
showNotification showNotification
} from '../notifications'; } from '../notifications';
@ -50,7 +51,7 @@ MiddlewareRegistry.register(store => next => action => {
titleKey: 'toolbar.talkWhileMutedPopup', titleKey: 'toolbar.talkWhileMutedPopup',
customActionNameKey: forceMuted ? 'notify.raiseHandAction' : 'notify.unmute', customActionNameKey: forceMuted ? 'notify.raiseHandAction' : 'notify.unmute',
customActionHandler: () => dispatch(forceMuted ? raiseHand(true) : setAudioMuted(false)) customActionHandler: () => dispatch(forceMuted ? raiseHand(true) : setAudioMuted(false))
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
const { soundsTalkWhileMuted } = getState()['features/base/settings']; const { soundsTalkWhileMuted } = getState()['features/base/settings'];

View File

@ -1,7 +1,7 @@
// @flow // @flow
import { import {
NOTIFICATION_TIMEOUT, NOTIFICATION_TIMEOUT_TYPE,
hideNotification, hideNotification,
showErrorNotification, showErrorNotification,
showNotification showNotification
@ -74,7 +74,7 @@ export function showPendingTranscribingNotification() {
descriptionKey: 'transcribing.pending', descriptionKey: 'transcribing.pending',
isDismissAllowed: false, isDismissAllowed: false,
titleKey: 'dialog.transcribing' titleKey: 'dialog.transcribing'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
if (notification) { if (notification) {
dispatch(setPendingTranscribingNotificationUid(notification.uid)); dispatch(setPendingTranscribingNotificationUid(notification.uid));
@ -127,7 +127,7 @@ export function showStoppedTranscribingNotification() {
return showNotification({ return showNotification({
descriptionKey: 'transcribing.off', descriptionKey: 'transcribing.off',
titleKey: 'dialog.transcribing' titleKey: 'dialog.transcribing'
}, NOTIFICATION_TIMEOUT); }, NOTIFICATION_TIMEOUT_TYPE.SHORT);
} }
@ -140,5 +140,5 @@ export function showTranscribingError() {
return showErrorNotification({ return showErrorNotification({
descriptionKey: 'transcribing.error', descriptionKey: 'transcribing.error',
titleKey: 'transcribing.failedToStart' titleKey: 'transcribing.failedToStart'
}); }, NOTIFICATION_TIMEOUT_TYPE.LONG);
} }

View File

@ -7,6 +7,7 @@ import {
} from '../base/lib-jitsi-meet'; } from '../base/lib-jitsi-meet';
import { MiddlewareRegistry } from '../base/redux'; import { MiddlewareRegistry } from '../base/redux';
import { import {
NOTIFICATION_TIMEOUT_TYPE,
showErrorNotification, showErrorNotification,
showNotification, showNotification,
showWarningNotification showWarningNotification
@ -103,7 +104,7 @@ function _inviteRooms(rooms, conference, dispatch) {
dispatch(showErrorNotification({ dispatch(showErrorNotification({
descriptionKey: 'videoSIPGW.errorInvite', descriptionKey: 'videoSIPGW.errorInvite',
titleKey: 'videoSIPGW.errorInviteTitle' titleKey: 'videoSIPGW.errorInviteTitle'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
return; return;
} }
@ -111,7 +112,7 @@ function _inviteRooms(rooms, conference, dispatch) {
dispatch(showWarningNotification({ dispatch(showWarningNotification({
titleKey: 'videoSIPGW.errorAlreadyInvited', titleKey: 'videoSIPGW.errorAlreadyInvited',
titleArguments: { displayName } titleArguments: { displayName }
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
return; return;
} }
@ -148,7 +149,7 @@ function _sessionStateChanged(
titleArguments: { titleArguments: {
displayName: event.displayName displayName: event.displayName
} }
}, 2000); }, NOTIFICATION_TIMEOUT_TYPE.SHORT);
} }
case JitsiSIPVideoGWStatus.STATE_FAILED: { case JitsiSIPVideoGWStatus.STATE_FAILED: {
return showErrorNotification({ return showErrorNotification({
@ -157,14 +158,14 @@ function _sessionStateChanged(
displayName: event.displayName displayName: event.displayName
}, },
descriptionKey: 'videoSIPGW.errorInviteFailed' descriptionKey: 'videoSIPGW.errorInviteFailed'
}); }, NOTIFICATION_TIMEOUT_TYPE.LONG);
} }
case JitsiSIPVideoGWStatus.STATE_OFF: { case JitsiSIPVideoGWStatus.STATE_OFF: {
if (event.failureReason === JitsiSIPVideoGWStatus.STATUS_BUSY) { if (event.failureReason === JitsiSIPVideoGWStatus.STATUS_BUSY) {
return showErrorNotification({ return showErrorNotification({
descriptionKey: 'videoSIPGW.busy', descriptionKey: 'videoSIPGW.busy',
titleKey: 'videoSIPGW.busyTitle' titleKey: 'videoSIPGW.busyTitle'
}); }, NOTIFICATION_TIMEOUT_TYPE.LONG);
} else if (event.failureReason) { } else if (event.failureReason) {
logger.error(`Unknown sip videogw error ${event.newState} ${ logger.error(`Unknown sip videogw error ${event.newState} ${
event.failureReason}`); event.failureReason}`);

View File

@ -16,7 +16,7 @@ import { connect } from '../../base/redux';
import { updateSettings } from '../../base/settings'; import { updateSettings } from '../../base/settings';
import { Tooltip } from '../../base/tooltip'; import { Tooltip } from '../../base/tooltip';
import { getLocalVideoTrack } from '../../base/tracks'; import { getLocalVideoTrack } from '../../base/tracks';
import { showErrorNotification } from '../../notifications'; import { NOTIFICATION_TIMEOUT_TYPE, showErrorNotification } from '../../notifications';
import { toggleBackgroundEffect, virtualBackgroundTrackChanged } from '../actions'; import { toggleBackgroundEffect, virtualBackgroundTrackChanged } from '../actions';
import { IMAGES, BACKGROUNDS_LIMIT, VIRTUAL_BACKGROUND_TYPE, type Image } from '../constants'; import { IMAGES, BACKGROUNDS_LIMIT, VIRTUAL_BACKGROUND_TYPE, type Image } from '../constants';
import { toDataURL } from '../functions'; import { toDataURL } from '../functions';
@ -219,7 +219,7 @@ function VirtualBackground({
if (!isCancelled) { if (!isCancelled) {
dispatch(showErrorNotification({ dispatch(showErrorNotification({
titleKey: 'virtualBackground.desktopShareError' titleKey: 'virtualBackground.desktopShareError'
})); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
logger.error('Could not create desktop share as a virtual background!'); logger.error('Could not create desktop share as a virtual background!');
} }

View File

@ -10,6 +10,7 @@ import Video from '../../base/media/components/Video';
import { connect, equals } from '../../base/redux'; import { connect, equals } from '../../base/redux';
import { getCurrentCameraDeviceId } from '../../base/settings'; import { getCurrentCameraDeviceId } from '../../base/settings';
import { createLocalTracksF } from '../../base/tracks/functions'; import { createLocalTracksF } from '../../base/tracks/functions';
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications';
import { showWarningNotification } from '../../notifications/actions'; import { showWarningNotification } from '../../notifications/actions';
import { toggleBackgroundEffect } from '../actions'; import { toggleBackgroundEffect } from '../actions';
import { VIRTUAL_BACKGROUND_TYPE } from '../constants'; import { VIRTUAL_BACKGROUND_TYPE } from '../constants';
@ -140,7 +141,7 @@ class VirtualBackgroundPreview extends PureComponent<Props, State> {
showWarningNotification({ showWarningNotification({
titleKey: 'virtualBackground.backgroundEffectError', titleKey: 'virtualBackground.backgroundEffectError',
description: 'Failed to access camera device.' description: 'Failed to access camera device.'
}) }, NOTIFICATION_TIMEOUT_TYPE.LONG)
); );
logger.error('Failed to access camera device. Error on apply background effect.'); logger.error('Failed to access camera device. Error on apply background effect.');