2022-08-26 09:54:16 +00:00
|
|
|
import ReducerRegistry from '../base/redux/ReducerRegistry';
|
2017-07-28 17:56:49 +00:00
|
|
|
|
|
|
|
import {
|
2018-06-14 09:14:32 +00:00
|
|
|
CLEAR_NOTIFICATIONS,
|
2017-07-28 17:56:49 +00:00
|
|
|
HIDE_NOTIFICATION,
|
2017-08-02 18:15:55 +00:00
|
|
|
SET_NOTIFICATIONS_ENABLED,
|
2017-07-28 17:56:49 +00:00
|
|
|
SHOW_NOTIFICATION
|
|
|
|
} from './actionTypes';
|
2017-11-27 16:47:01 +00:00
|
|
|
import { NOTIFICATION_TYPE_PRIORITIES } from './constants';
|
2017-07-28 17:56:49 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The initial state of the feature notifications.
|
|
|
|
*
|
|
|
|
* @type {array}
|
|
|
|
*/
|
2017-08-02 18:15:55 +00:00
|
|
|
const DEFAULT_STATE = {
|
|
|
|
enabled: true,
|
|
|
|
notifications: []
|
|
|
|
};
|
2017-07-28 17:56:49 +00:00
|
|
|
|
2022-08-26 09:54:16 +00:00
|
|
|
interface INotification {
|
|
|
|
component: Object;
|
|
|
|
props: {
|
|
|
|
appearance?: string;
|
|
|
|
descriptionKey?: string;
|
|
|
|
titleKey: string;
|
|
|
|
};
|
|
|
|
timeout: number;
|
|
|
|
uid: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface INotificationsState {
|
|
|
|
enabled: boolean;
|
|
|
|
notifications: INotification[];
|
|
|
|
}
|
|
|
|
|
2017-07-28 17:56:49 +00:00
|
|
|
/**
|
|
|
|
* Reduces redux actions which affect the display of notifications.
|
|
|
|
*
|
|
|
|
* @param {Object} state - The current redux state.
|
|
|
|
* @param {Object} action - The redux action to reduce.
|
|
|
|
* @returns {Object} The next redux state which is the result of reducing the
|
|
|
|
* specified {@code action}.
|
|
|
|
*/
|
2022-09-05 09:05:07 +00:00
|
|
|
ReducerRegistry.register<INotificationsState>('features/notifications',
|
|
|
|
(state = DEFAULT_STATE, action): INotificationsState => {
|
2017-07-28 17:56:49 +00:00
|
|
|
switch (action.type) {
|
2018-06-14 09:14:32 +00:00
|
|
|
case CLEAR_NOTIFICATIONS:
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
notifications: []
|
|
|
|
};
|
2017-07-28 17:56:49 +00:00
|
|
|
case HIDE_NOTIFICATION:
|
2017-08-02 18:15:55 +00:00
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
notifications: state.notifications.filter(
|
|
|
|
notification => notification.uid !== action.uid)
|
|
|
|
};
|
|
|
|
|
|
|
|
case SET_NOTIFICATIONS_ENABLED:
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
enabled: action.enabled
|
|
|
|
};
|
2017-07-28 17:56:49 +00:00
|
|
|
|
|
|
|
case SHOW_NOTIFICATION:
|
2017-08-02 18:15:55 +00:00
|
|
|
return {
|
2017-07-28 17:56:49 +00:00
|
|
|
...state,
|
2017-11-27 16:47:01 +00:00
|
|
|
notifications:
|
|
|
|
_insertNotificationByPriority(state.notifications, {
|
2017-08-02 18:15:55 +00:00
|
|
|
component: action.component,
|
|
|
|
props: action.props,
|
|
|
|
timeout: action.timeout,
|
|
|
|
uid: action.uid
|
2017-11-27 16:47:01 +00:00
|
|
|
})
|
2017-08-02 18:15:55 +00:00
|
|
|
};
|
2017-07-28 17:56:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return state;
|
|
|
|
});
|
2017-11-27 16:47:01 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new notification queue with the passed in notification placed at
|
|
|
|
* the end of other notifications with higher or the same priority.
|
|
|
|
*
|
|
|
|
* @param {Object[]} notifications - The queue of notifications to be displayed.
|
|
|
|
* @param {Object} notification - The new notification to add to the queue.
|
|
|
|
* @private
|
|
|
|
* @returns {Object[]} A new array with an updated order of the notification
|
|
|
|
* queue.
|
|
|
|
*/
|
2022-08-26 09:54:16 +00:00
|
|
|
function _insertNotificationByPriority(notifications: INotification[], notification: INotification) {
|
2023-02-21 09:26:04 +00:00
|
|
|
|
|
|
|
// Create a copy to avoid mutation.
|
|
|
|
const copyOfNotifications = notifications.slice();
|
|
|
|
|
|
|
|
// Get the index of any queued notification that has the same id as the new notification
|
|
|
|
let insertAtLocation = copyOfNotifications.findIndex(
|
|
|
|
(queuedNotification: INotification) =>
|
|
|
|
queuedNotification?.uid === notification?.uid
|
|
|
|
);
|
|
|
|
|
|
|
|
if (insertAtLocation !== -1) {
|
|
|
|
copyOfNotifications.splice(insertAtLocation, 1, notification);
|
|
|
|
|
|
|
|
return copyOfNotifications;
|
|
|
|
}
|
|
|
|
|
2017-11-27 16:47:01 +00:00
|
|
|
const newNotificationPriority
|
2022-08-26 09:54:16 +00:00
|
|
|
= NOTIFICATION_TYPE_PRIORITIES[notification.props.appearance ?? ''] || 0;
|
2017-11-27 16:47:01 +00:00
|
|
|
|
|
|
|
// Find where to insert the new notification based on priority. Do not
|
|
|
|
// insert at the front of the queue so that the user can finish acting on
|
|
|
|
// any notification currently being read.
|
|
|
|
for (let i = 1; i < notifications.length; i++) {
|
|
|
|
const queuedNotification = notifications[i];
|
|
|
|
const queuedNotificationPriority
|
2022-08-26 09:54:16 +00:00
|
|
|
= NOTIFICATION_TYPE_PRIORITIES[queuedNotification.props.appearance ?? '']
|
2023-02-21 09:26:04 +00:00
|
|
|
|| 0;
|
2017-11-27 16:47:01 +00:00
|
|
|
|
|
|
|
if (queuedNotificationPriority < newNotificationPriority) {
|
|
|
|
insertAtLocation = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
copyOfNotifications.splice(insertAtLocation, 0, notification);
|
|
|
|
|
|
|
|
return copyOfNotifications;
|
|
|
|
}
|