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.

NotificationsContainer.js 6.0KB

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