ref(notifications): bring hiding of notifications into redux
This commit is contained in:
parent
74ddae4a6a
commit
cd66a7fcb7
|
@ -40,7 +40,8 @@ import {
|
||||||
showToolbox
|
showToolbox
|
||||||
} from '../../react/features/toolbox';
|
} from '../../react/features/toolbox';
|
||||||
import {
|
import {
|
||||||
maybeShowNotificationWithDoNotDisplay
|
maybeShowNotificationWithDoNotDisplay,
|
||||||
|
setNotificationsEnabled
|
||||||
} from '../../react/features/notifications';
|
} from '../../react/features/notifications';
|
||||||
|
|
||||||
var EventEmitter = require("events");
|
var EventEmitter = require("events");
|
||||||
|
@ -51,17 +52,6 @@ import FollowMe from "../FollowMe";
|
||||||
var eventEmitter = new EventEmitter();
|
var eventEmitter = new EventEmitter();
|
||||||
UI.eventEmitter = eventEmitter;
|
UI.eventEmitter = eventEmitter;
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether an overlay is visible or not.
|
|
||||||
*
|
|
||||||
* FIXME: This is temporary solution. Don't use this variable!
|
|
||||||
* Should be removed when all the code is move to react.
|
|
||||||
*
|
|
||||||
* @type {boolean}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
UI.overlayVisible = false;
|
|
||||||
|
|
||||||
let etherpadManager;
|
let etherpadManager;
|
||||||
let sharedVideoManager;
|
let sharedVideoManager;
|
||||||
|
|
||||||
|
@ -335,7 +325,7 @@ UI.start = function () {
|
||||||
$("body").addClass("filmstrip-only");
|
$("body").addClass("filmstrip-only");
|
||||||
UI.showToolbar();
|
UI.showToolbar();
|
||||||
Filmstrip.setFilmstripOnly();
|
Filmstrip.setFilmstripOnly();
|
||||||
messageHandler.enableNotifications(false);
|
APP.store.dispatch(setNotificationsEnabled(false));
|
||||||
JitsiPopover.enabled = false;
|
JitsiPopover.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1307,19 +1297,6 @@ UI.onSharedVideoStop = function (id, attributes) {
|
||||||
sharedVideoManager.onSharedVideoStop(id, attributes);
|
sharedVideoManager.onSharedVideoStop(id, attributes);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates if any the "top" overlays are currently visible. The check includes
|
|
||||||
* the call/ring overlay, the suspended overlay, the GUM permissions overlay,
|
|
||||||
* and the page-reload overlay.
|
|
||||||
*
|
|
||||||
* @returns {*|boolean} {true} if an overlay is visible; {false}, otherwise
|
|
||||||
*/
|
|
||||||
UI.isOverlayVisible = function () {
|
|
||||||
return (
|
|
||||||
this.overlayVisible
|
|
||||||
|| APP.store.getState()['features/jwt'].callOverlayVisible);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles user's features changes.
|
* Handles user's features changes.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -22,6 +22,7 @@ import VideoLayout from '../videolayout/VideoLayout';
|
||||||
import Feedback from '../feedback/Feedback.js';
|
import Feedback from '../feedback/Feedback.js';
|
||||||
|
|
||||||
import { setToolboxEnabled } from '../../../react/features/toolbox';
|
import { setToolboxEnabled } from '../../../react/features/toolbox';
|
||||||
|
import { setNotificationsEnabled } from '../../../react/features/notifications';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The dialog for user input.
|
* The dialog for user input.
|
||||||
|
@ -309,7 +310,7 @@ var Recording = {
|
||||||
VideoLayout.setLocalVideoVisible(false);
|
VideoLayout.setLocalVideoVisible(false);
|
||||||
Feedback.enableFeedback(false);
|
Feedback.enableFeedback(false);
|
||||||
APP.store.dispatch(setToolboxEnabled(false));
|
APP.store.dispatch(setToolboxEnabled(false));
|
||||||
APP.UI.messageHandler.enableNotifications(false);
|
APP.store.dispatch(setNotificationsEnabled(false));
|
||||||
APP.UI.messageHandler.enablePopups(false);
|
APP.UI.messageHandler.enablePopups(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,6 @@ import {
|
||||||
showNotification
|
showNotification
|
||||||
} from '../../../react/features/notifications';
|
} from '../../../react/features/notifications';
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag for enable/disable of the notifications.
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
let notificationsEnabled = true;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag for enabling/disabling popups.
|
* Flag for enabling/disabling popups.
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
|
@ -456,11 +450,6 @@ var messageHandler = {
|
||||||
*/
|
*/
|
||||||
participantNotification: function(displayName, displayNameKey, cls,
|
participantNotification: function(displayName, displayNameKey, cls,
|
||||||
messageKey, messageArguments, timeout = 2500) {
|
messageKey, messageArguments, timeout = 2500) {
|
||||||
// If we're in ringing state we skip all notifications.
|
|
||||||
if (!notificationsEnabled || APP.UI.isOverlayVisible()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
APP.store.dispatch(
|
APP.store.dispatch(
|
||||||
showNotification(
|
showNotification(
|
||||||
Notification,
|
Notification,
|
||||||
|
@ -485,22 +474,10 @@ var messageHandler = {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
notify: function(titleKey, messageKey, messageArguments) {
|
notify: function(titleKey, messageKey, messageArguments) {
|
||||||
|
|
||||||
// If we're in ringing state we skip all notifications.
|
|
||||||
if(!notificationsEnabled || APP.UI.isOverlayVisible())
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.participantNotification(
|
this.participantNotification(
|
||||||
null, titleKey, null, messageKey, messageArguments);
|
null, titleKey, null, messageKey, messageArguments);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables / disables notifications.
|
|
||||||
*/
|
|
||||||
enableNotifications: function (enable) {
|
|
||||||
notificationsEnabled = enable;
|
|
||||||
},
|
|
||||||
|
|
||||||
enablePopups: function (enable) {
|
enablePopups: function (enable) {
|
||||||
popupEnabled = enable;
|
popupEnabled = enable;
|
||||||
},
|
},
|
||||||
|
|
|
@ -79,7 +79,7 @@ class Conference extends Component {
|
||||||
{ filmStripOnly ? null : <Toolbox /> }
|
{ filmStripOnly ? null : <Toolbox /> }
|
||||||
|
|
||||||
<DialogContainer />
|
<DialogContainer />
|
||||||
{ filmStripOnly ? null : <NotificationsContainer /> }
|
<NotificationsContainer />
|
||||||
<OverlayContainer />
|
<OverlayContainer />
|
||||||
|
|
||||||
{/*
|
{/*
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/**
|
||||||
* The type of (redux) action which signals that a specific notification should
|
* The type of (redux) action which signals that a specific notification should
|
||||||
* not be displayed anymore.
|
* not be displayed anymore.
|
||||||
*
|
*
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
*/
|
*/
|
||||||
export const HIDE_NOTIFICATION = Symbol('HIDE_NOTIFICATION');
|
export const HIDE_NOTIFICATION = Symbol('HIDE_NOTIFICATION');
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* The type of (redux) action which signals that a notification component should
|
* The type of (redux) action which signals that a notification component should
|
||||||
* be displayed.
|
* be displayed.
|
||||||
*
|
*
|
||||||
|
@ -22,3 +22,14 @@ export const HIDE_NOTIFICATION = Symbol('HIDE_NOTIFICATION');
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
export const SHOW_NOTIFICATION = Symbol('SHOW_NOTIFICATION');
|
export const SHOW_NOTIFICATION = Symbol('SHOW_NOTIFICATION');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of (redux) action which signals that notifications should not
|
||||||
|
* display.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* type: SET_NOTIFICATIONS_ENABLED,
|
||||||
|
* enabled: Boolean
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export const SET_NOTIFICATIONS_ENABLED = Symbol('SET_NOTIFICATIONS_ENABLED');
|
||||||
|
|
|
@ -2,6 +2,7 @@ import jitsiLocalStorage from '../../../modules/util/JitsiLocalStorage';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HIDE_NOTIFICATION,
|
HIDE_NOTIFICATION,
|
||||||
|
SET_NOTIFICATIONS_ENABLED,
|
||||||
SHOW_NOTIFICATION
|
SHOW_NOTIFICATION
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
import { NotificationWithToggle } from './components';
|
import { NotificationWithToggle } from './components';
|
||||||
|
@ -23,6 +24,22 @@ export function hideNotification(uid) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops notifications from being displayed.
|
||||||
|
*
|
||||||
|
* @param {boolean} enabled - Whether or not notifications should display.
|
||||||
|
* @returns {{
|
||||||
|
* type: SET_NOTIFICATIONS_ENABLED,
|
||||||
|
* enabled: boolean
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
export function setNotificationsEnabled(enabled) {
|
||||||
|
return {
|
||||||
|
type: SET_NOTIFICATIONS_ENABLED,
|
||||||
|
enabled
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queues a notification for display.
|
* Queues a notification for display.
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,6 +24,12 @@ class NotificationsContainer extends Component {
|
||||||
*/
|
*/
|
||||||
_notifications: React.PropTypes.array,
|
_notifications: React.PropTypes.array,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not notifications should be displayed at all. If not,
|
||||||
|
* notifications will be dismissed immediately.
|
||||||
|
*/
|
||||||
|
_showNotifications: React.PropTypes.bool,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked to update the redux store in order to remove notifications.
|
* Invoked to update the redux store in order to remove notifications.
|
||||||
*/
|
*/
|
||||||
|
@ -59,18 +65,27 @@ class NotificationsContainer extends Component {
|
||||||
* returns {void}
|
* returns {void}
|
||||||
*/
|
*/
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
const { _notifications } = this.props;
|
const { _notifications, _showNotifications } = this.props;
|
||||||
|
|
||||||
if (_notifications.length && !this._notificationDismissTimeout) {
|
if (_notifications.length) {
|
||||||
const notification = _notifications[0];
|
const notification = _notifications[0];
|
||||||
const { timeout, uid } = notification;
|
|
||||||
|
|
||||||
this._notificationDismissTimeout = setTimeout(() => {
|
if (!_showNotifications) {
|
||||||
// Perform a no-op if a timeout is not specified.
|
this._onDismissed(notification.uid);
|
||||||
if (Number.isInteger(timeout)) {
|
} else if (this._notificationDismissTimeout) {
|
||||||
this._onDismissed(uid);
|
|
||||||
}
|
// No-op because there should already be a notification that
|
||||||
}, timeout);
|
// is waiting for dismissal.
|
||||||
|
} else {
|
||||||
|
const { timeout, uid } = notification;
|
||||||
|
|
||||||
|
this._notificationDismissTimeout = setTimeout(() => {
|
||||||
|
// Perform a no-op if a timeout is not specified.
|
||||||
|
if (Number.isInteger(timeout)) {
|
||||||
|
this._onDismissed(uid);
|
||||||
|
}
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,28 +106,9 @@ class NotificationsContainer extends Component {
|
||||||
* @returns {ReactElement}
|
* @returns {ReactElement}
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { _notifications } = this.props;
|
|
||||||
|
|
||||||
const flags = _notifications.map(notification => {
|
|
||||||
const Notification = notification.component;
|
|
||||||
const { props, uid } = notification;
|
|
||||||
|
|
||||||
// The id attribute is necessary as {@code FlagGroup} looks for
|
|
||||||
// either id or key to set a key on notifications, but accessing
|
|
||||||
// props.key will cause React to print an error.
|
|
||||||
return (
|
|
||||||
<Notification
|
|
||||||
{ ...props }
|
|
||||||
id = { uid }
|
|
||||||
key = { uid }
|
|
||||||
uid = { uid } />
|
|
||||||
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlagGroup onDismissed = { this._onDismissed }>
|
<FlagGroup onDismissed = { this._onDismissed }>
|
||||||
{ flags }
|
{ this._renderFlags() }
|
||||||
</FlagGroup>
|
</FlagGroup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -131,6 +127,38 @@ class NotificationsContainer extends Component {
|
||||||
|
|
||||||
this.props.dispatch(hideNotification(flagUid));
|
this.props.dispatch(hideNotification(flagUid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders notifications to display as ReactElements. An empty array will
|
||||||
|
* be returned if notifications are disabled.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {ReactElement[]}
|
||||||
|
*/
|
||||||
|
_renderFlags() {
|
||||||
|
const { _notifications, _showNotifications } = this.props;
|
||||||
|
|
||||||
|
if (!_showNotifications) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _notifications.map(notification => {
|
||||||
|
const Notification = notification.component;
|
||||||
|
const { props, uid } = notification;
|
||||||
|
|
||||||
|
// The id attribute is necessary as {@code FlagGroup} looks for
|
||||||
|
// either id or key to set a key on notifications, but accessing
|
||||||
|
// props.key will cause React to print an error.
|
||||||
|
return (
|
||||||
|
<Notification
|
||||||
|
{ ...props }
|
||||||
|
id = { uid }
|
||||||
|
key = { uid }
|
||||||
|
uid = { uid } />
|
||||||
|
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,8 +172,25 @@ class NotificationsContainer extends Component {
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
|
// TODO: Per existing behavior, notifications should not display when an
|
||||||
|
// overlay is visible. This logic for checking overlay display can likely be
|
||||||
|
// simplified.
|
||||||
|
const {
|
||||||
|
connectionEstablished,
|
||||||
|
haveToReload,
|
||||||
|
isMediaPermissionPromptVisible,
|
||||||
|
suspendDetected
|
||||||
|
} = state['features/overlay'];
|
||||||
|
const isAnyOverlayVisible = (connectionEstablished && haveToReload)
|
||||||
|
|| isMediaPermissionPromptVisible
|
||||||
|
|| suspendDetected
|
||||||
|
|| state['features/jwt'].callOverlayVisible;
|
||||||
|
|
||||||
|
const { enabled, notifications } = state['features/notifications'];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_notifications: state['features/notifications']
|
_notifications: notifications,
|
||||||
|
_showNotifications: enabled && !isAnyOverlayVisible
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { ReducerRegistry } from '../base/redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HIDE_NOTIFICATION,
|
HIDE_NOTIFICATION,
|
||||||
|
SET_NOTIFICATIONS_ENABLED,
|
||||||
SHOW_NOTIFICATION
|
SHOW_NOTIFICATION
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
|
|
||||||
|
@ -10,7 +11,10 @@ import {
|
||||||
*
|
*
|
||||||
* @type {array}
|
* @type {array}
|
||||||
*/
|
*/
|
||||||
const DEFAULT_STATE = [];
|
const DEFAULT_STATE = {
|
||||||
|
enabled: true,
|
||||||
|
notifications: []
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reduces redux actions which affect the display of notifications.
|
* Reduces redux actions which affect the display of notifications.
|
||||||
|
@ -24,19 +28,31 @@ ReducerRegistry.register('features/notifications',
|
||||||
(state = DEFAULT_STATE, action) => {
|
(state = DEFAULT_STATE, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case HIDE_NOTIFICATION:
|
case HIDE_NOTIFICATION:
|
||||||
return state.filter(
|
return {
|
||||||
notification => notification.uid !== action.uid);
|
...state,
|
||||||
|
notifications: state.notifications.filter(
|
||||||
|
notification => notification.uid !== action.uid)
|
||||||
|
};
|
||||||
|
|
||||||
|
case SET_NOTIFICATIONS_ENABLED:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
enabled: action.enabled
|
||||||
|
};
|
||||||
|
|
||||||
case SHOW_NOTIFICATION:
|
case SHOW_NOTIFICATION:
|
||||||
return [
|
return {
|
||||||
...state,
|
...state,
|
||||||
{
|
notifications: [
|
||||||
component: action.component,
|
...state.notifications,
|
||||||
props: action.props,
|
{
|
||||||
timeout: action.timeout,
|
component: action.component,
|
||||||
uid: action.uid
|
props: action.props,
|
||||||
}
|
timeout: action.timeout,
|
||||||
];
|
uid: action.uid
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
|
|
@ -11,7 +11,6 @@ import UserMediaPermissionsFilmstripOnlyOverlay
|
||||||
from './UserMediaPermissionsFilmstripOnlyOverlay';
|
from './UserMediaPermissionsFilmstripOnlyOverlay';
|
||||||
import UserMediaPermissionsOverlay from './UserMediaPermissionsOverlay';
|
import UserMediaPermissionsOverlay from './UserMediaPermissionsOverlay';
|
||||||
|
|
||||||
declare var APP: Object;
|
|
||||||
declare var interfaceConfig: Object;
|
declare var interfaceConfig: Object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,23 +132,6 @@ class OverlayContainer extends Component {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* React Component method that executes once component is updated.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {void}
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
componentDidUpdate() {
|
|
||||||
if (typeof APP === 'object') {
|
|
||||||
APP.UI.overlayVisible
|
|
||||||
= (this.props._connectionEstablished
|
|
||||||
&& this.props._haveToReload)
|
|
||||||
|| this.props._suspendDetected
|
|
||||||
|| this.props._isMediaPermissionPromptVisible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements React's {@link Component#render()}.
|
* Implements React's {@link Component#render()}.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue