Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

NotificationsContainer.js 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // @flow
  2. import React, { Component } from 'react';
  3. import { connect } from '../../../base/redux';
  4. import { hideNotification } from '../../actions';
  5. import { areThereNotifications } from '../../functions';
  6. import Notification from './Notification';
  7. type Props = {
  8. /**
  9. * The notifications to be displayed, with the first index being the
  10. * notification at the top and the rest shown below it in order.
  11. */
  12. _notifications: Array<Object>,
  13. /**
  14. * Invoked to update the redux store in order to remove notifications.
  15. */
  16. dispatch: Function
  17. };
  18. /**
  19. * Implements a React {@link Component} which displays notifications and handles
  20. * automatic dismissmal after a notification is shown for a defined timeout
  21. * period.
  22. *
  23. * @augments {Component}
  24. */
  25. class NotificationsContainer extends Component<Props> {
  26. /**
  27. * A timeout id returned by setTimeout.
  28. */
  29. _notificationDismissTimeout: ?TimeoutID;
  30. /**
  31. * Initializes a new {@code NotificationsContainer} instance.
  32. *
  33. * @inheritdoc
  34. */
  35. constructor(props: Props) {
  36. super(props);
  37. /**
  38. * The timeout set for automatically dismissing a displayed
  39. * notification. This value is set on the instance and not state to
  40. * avoid additional re-renders.
  41. *
  42. * @type {number|null}
  43. */
  44. this._notificationDismissTimeout = null;
  45. // Bind event handlers so they are only bound once for every instance.
  46. this._onDismissed = this._onDismissed.bind(this);
  47. }
  48. /**
  49. * Sets a timeout (if applicable).
  50. *
  51. * @inheritdoc
  52. */
  53. componentDidMount() {
  54. // Set the initial dismiss timeout (if any)
  55. this._manageDismissTimeout();
  56. }
  57. /**
  58. * Sets a timeout if the currently displayed notification has changed.
  59. *
  60. * @inheritdoc
  61. */
  62. componentDidUpdate(prevProps: Props) {
  63. this._manageDismissTimeout(prevProps);
  64. }
  65. /**
  66. * Sets/clears the dismiss timeout for the top notification.
  67. *
  68. * @param {P} [prevProps] - The previous properties (if called from
  69. * {@code componentDidUpdate}).
  70. * @returns {void}
  71. * @private
  72. */
  73. _manageDismissTimeout(prevProps: ?Props) {
  74. const { _notifications } = this.props;
  75. if (_notifications.length) {
  76. const notification = _notifications[0];
  77. const previousNotification
  78. = prevProps && prevProps._notifications.length
  79. ? prevProps._notifications[0]
  80. : undefined;
  81. if (notification !== previousNotification) {
  82. this._clearNotificationDismissTimeout();
  83. if (notification && notification.timeout) {
  84. const {
  85. timeout,
  86. uid
  87. } = notification;
  88. this._notificationDismissTimeout = setTimeout(() => {
  89. // Perform a no-op if a timeout is not specified.
  90. this._onDismissed(uid);
  91. }, timeout);
  92. }
  93. }
  94. } else if (this._notificationDismissTimeout) {
  95. // Clear timeout when all notifications are cleared (e.g external
  96. // call to clear them)
  97. this._clearNotificationDismissTimeout();
  98. }
  99. }
  100. /**
  101. * Clear any dismissal timeout that is still active.
  102. *
  103. * @inheritdoc
  104. */
  105. componentWillUnmount() {
  106. this._clearNotificationDismissTimeout();
  107. }
  108. /**
  109. * Clears the running notification dismiss timeout, if any.
  110. *
  111. * @returns {void}
  112. */
  113. _clearNotificationDismissTimeout() {
  114. this._notificationDismissTimeout && clearTimeout(this._notificationDismissTimeout);
  115. this._notificationDismissTimeout = null;
  116. }
  117. _onDismissed: number => void;
  118. /**
  119. * Emits an action to remove the notification from the redux store so it
  120. * stops displaying.
  121. *
  122. * @param {number} uid - The id of the notification to be removed.
  123. * @private
  124. * @returns {void}
  125. */
  126. _onDismissed(uid) {
  127. const { _notifications } = this.props;
  128. // Clear the timeout only if it's the top notification that's being
  129. // dismissed (the timeout is set only for the top one).
  130. if (!_notifications.length || _notifications[0].uid === uid) {
  131. this._clearNotificationDismissTimeout();
  132. }
  133. this.props.dispatch(hideNotification(uid));
  134. }
  135. /**
  136. * Implements React's {@link Component#render()}.
  137. *
  138. * @inheritdoc
  139. */
  140. render() {
  141. const { _notifications } = this.props;
  142. return (
  143. <>
  144. {
  145. _notifications.map((notification, index) => {
  146. const { props, uid } = notification;
  147. return (
  148. <Notification
  149. { ...props }
  150. key = { index }
  151. onDismissed = { this._onDismissed }
  152. uid = { uid } />
  153. );
  154. })
  155. }
  156. </>
  157. );
  158. }
  159. _onDismissed: number => void;
  160. }
  161. /**
  162. * Maps (parts of) the Redux state to the associated NotificationsContainer's
  163. * props.
  164. *
  165. * @param {Object} state - The Redux state.
  166. * @private
  167. * @returns {Props}
  168. */
  169. export function mapStateToProps(state: Object) {
  170. const { notifications } = state['features/notifications'];
  171. const _visible = areThereNotifications(state);
  172. return {
  173. _notifications: _visible ? notifications : []
  174. };
  175. }
  176. export default connect(mapStateToProps)(NotificationsContainer);