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.

AbstractNotificationsContainer.js 4.6KB

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