You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

reducer.ts 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import ReducerRegistry from '../base/redux/ReducerRegistry';
  2. import {
  3. CLEAR_NOTIFICATIONS,
  4. HIDE_NOTIFICATION,
  5. SET_NOTIFICATIONS_ENABLED,
  6. SHOW_NOTIFICATION
  7. } from './actionTypes';
  8. import { NOTIFICATION_TYPE_PRIORITIES } from './constants';
  9. /**
  10. * The initial state of the feature notifications.
  11. *
  12. * @type {array}
  13. */
  14. const DEFAULT_STATE = {
  15. enabled: true,
  16. notifications: []
  17. };
  18. interface INotification {
  19. component: Object;
  20. props: {
  21. appearance?: string;
  22. descriptionKey?: string;
  23. titleKey: string;
  24. };
  25. timeout: number;
  26. uid: string;
  27. }
  28. export interface INotificationsState {
  29. enabled: boolean;
  30. notifications: INotification[];
  31. }
  32. /**
  33. * Reduces redux actions which affect the display of notifications.
  34. *
  35. * @param {Object} state - The current redux state.
  36. * @param {Object} action - The redux action to reduce.
  37. * @returns {Object} The next redux state which is the result of reducing the
  38. * specified {@code action}.
  39. */
  40. ReducerRegistry.register<INotificationsState>('features/notifications',
  41. (state = DEFAULT_STATE, action): INotificationsState => {
  42. switch (action.type) {
  43. case CLEAR_NOTIFICATIONS:
  44. return {
  45. ...state,
  46. notifications: []
  47. };
  48. case HIDE_NOTIFICATION:
  49. return {
  50. ...state,
  51. notifications: state.notifications.filter(
  52. notification => notification.uid !== action.uid)
  53. };
  54. case SET_NOTIFICATIONS_ENABLED:
  55. return {
  56. ...state,
  57. enabled: action.enabled
  58. };
  59. case SHOW_NOTIFICATION:
  60. return {
  61. ...state,
  62. notifications:
  63. _insertNotificationByPriority(state.notifications, {
  64. component: action.component,
  65. props: action.props,
  66. timeout: action.timeout,
  67. uid: action.uid
  68. })
  69. };
  70. }
  71. return state;
  72. });
  73. /**
  74. * Creates a new notification queue with the passed in notification placed at
  75. * the end of other notifications with higher or the same priority.
  76. *
  77. * @param {Object[]} notifications - The queue of notifications to be displayed.
  78. * @param {Object} notification - The new notification to add to the queue.
  79. * @private
  80. * @returns {Object[]} A new array with an updated order of the notification
  81. * queue.
  82. */
  83. function _insertNotificationByPriority(notifications: INotification[], notification: INotification) {
  84. const newNotificationPriority
  85. = NOTIFICATION_TYPE_PRIORITIES[notification.props.appearance ?? ''] || 0;
  86. // Default to putting the new notification at the end of the queue.
  87. let insertAtLocation = notifications.length;
  88. // Find where to insert the new notification based on priority. Do not
  89. // insert at the front of the queue so that the user can finish acting on
  90. // any notification currently being read.
  91. for (let i = 1; i < notifications.length; i++) {
  92. const queuedNotification = notifications[i];
  93. const queuedNotificationPriority
  94. = NOTIFICATION_TYPE_PRIORITIES[queuedNotification.props.appearance ?? '']
  95. || 0;
  96. if (queuedNotificationPriority < newNotificationPriority) {
  97. insertAtLocation = i;
  98. break;
  99. }
  100. }
  101. // Create a copy to avoid mutation and insert the notification.
  102. const copyOfNotifications = notifications.slice();
  103. copyOfNotifications.splice(insertAtLocation, 0, notification);
  104. return copyOfNotifications;
  105. }