您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

NotificationsContainer.web.js 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import { FlagGroup } from '@atlaskit/flag';
  2. import PropTypes from 'prop-types';
  3. import React, { Component } from 'react';
  4. import { connect } from 'react-redux';
  5. import { hideNotification } from '../actions';
  6. /**
  7. * Implements a React {@link Component} which displays notifications and handles
  8. * automatic dismissmal after a notification is shown for a defined timeout
  9. * period.
  10. *
  11. * @extends {Component}
  12. */
  13. class NotificationsContainer extends Component {
  14. /**
  15. * {@code NotificationsContainer} component's property types.
  16. *
  17. * @static
  18. */
  19. static propTypes = {
  20. /**
  21. * The notifications to be displayed, with the first index being the
  22. * notification at the top and the rest shown below it in order.
  23. */
  24. _notifications: PropTypes.array,
  25. /**
  26. * Whether or not notifications should be displayed at all. If not,
  27. * notifications will be dismissed immediately.
  28. */
  29. _showNotifications: PropTypes.bool,
  30. /**
  31. * Invoked to update the redux store in order to remove notifications.
  32. */
  33. dispatch: PropTypes.func
  34. };
  35. /**
  36. * Initializes a new {@code NotificationsContainer} instance.
  37. *
  38. * @param {Object} props - The read-only React Component props with which
  39. * the new instance is to be initialized.
  40. */
  41. constructor(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 if the currently displayed notification has changed.
  56. *
  57. * @inheritdoc
  58. * returns {void}
  59. */
  60. componentDidUpdate() {
  61. const { _notifications, _showNotifications } = this.props;
  62. if (_notifications.length) {
  63. const notification = _notifications[0];
  64. if (!_showNotifications) {
  65. this._onDismissed(notification.uid);
  66. } else if (this._notificationDismissTimeout) {
  67. // No-op because there should already be a notification that
  68. // is waiting for dismissal.
  69. } else {
  70. const { timeout, uid } = notification;
  71. this._notificationDismissTimeout = setTimeout(() => {
  72. // Perform a no-op if a timeout is not specified.
  73. if (Number.isInteger(timeout)) {
  74. this._onDismissed(uid);
  75. }
  76. }, timeout);
  77. }
  78. }
  79. }
  80. /**
  81. * Clear any dismissal timeout that is still active.
  82. *
  83. * @inheritdoc
  84. * returns {void}
  85. */
  86. componentWillUnmount() {
  87. clearTimeout(this._notificationDismissTimeout);
  88. }
  89. /**
  90. * Implements React's {@link Component#render()}.
  91. *
  92. * @inheritdoc
  93. * @returns {ReactElement}
  94. */
  95. render() {
  96. return (
  97. <FlagGroup onDismissed = { this._onDismissed }>
  98. { this._renderFlags() }
  99. </FlagGroup>
  100. );
  101. }
  102. /**
  103. * Emits an action to remove the notification from the redux store so it
  104. * stops displaying.
  105. *
  106. * @param {number} flagUid - The id of the notification to be removed.
  107. * @private
  108. * @returns {void}
  109. */
  110. _onDismissed(flagUid) {
  111. clearTimeout(this._notificationDismissTimeout);
  112. this._notificationDismissTimeout = null;
  113. this.props.dispatch(hideNotification(flagUid));
  114. }
  115. /**
  116. * Renders notifications to display as ReactElements. An empty array will
  117. * be returned if notifications are disabled.
  118. *
  119. * @private
  120. * @returns {ReactElement[]}
  121. */
  122. _renderFlags() {
  123. const { _notifications, _showNotifications } = this.props;
  124. if (!_showNotifications) {
  125. return [];
  126. }
  127. return _notifications.map(notification => {
  128. const Notification = notification.component;
  129. const { props, uid } = notification;
  130. // The id attribute is necessary as {@code FlagGroup} looks for
  131. // either id or key to set a key on notifications, but accessing
  132. // props.key will cause React to print an error.
  133. return (
  134. <Notification
  135. { ...props }
  136. id = { uid }
  137. key = { uid }
  138. uid = { uid } />
  139. );
  140. });
  141. }
  142. }
  143. /**
  144. * Maps (parts of) the Redux state to the associated NotificationsContainer's
  145. * props.
  146. *
  147. * @param {Object} state - The Redux state.
  148. * @private
  149. * @returns {{
  150. * _notifications: Array
  151. * }}
  152. */
  153. function _mapStateToProps(state) {
  154. // TODO: Per existing behavior, notifications should not display when an
  155. // overlay is visible. This logic for checking overlay display can likely be
  156. // simplified.
  157. const {
  158. connectionEstablished,
  159. haveToReload,
  160. isMediaPermissionPromptVisible,
  161. suspendDetected
  162. } = state['features/overlay'];
  163. const isAnyOverlayVisible = (connectionEstablished && haveToReload)
  164. || isMediaPermissionPromptVisible
  165. || suspendDetected
  166. || state['features/base/jwt'].callOverlayVisible;
  167. const { enabled, notifications } = state['features/notifications'];
  168. return {
  169. _notifications: notifications,
  170. _showNotifications: enabled && !isAnyOverlayVisible
  171. };
  172. }
  173. export default connect(_mapStateToProps)(NotificationsContainer);