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.

InfoDialogButton.web.js 8.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. /* global interfaceConfig */
  2. import InlineDialog from '@atlaskit/inline-dialog';
  3. import PropTypes from 'prop-types';
  4. import React, { Component } from 'react';
  5. import { connect } from 'react-redux';
  6. import { createToolbarEvent, sendAnalytics } from '../../analytics';
  7. import { translate } from '../../base/i18n';
  8. import {
  9. ToolbarButton,
  10. ToolbarButtonV2,
  11. TOOLTIP_TO_POPUP_POSITION
  12. } from '../../toolbox';
  13. import { setInfoDialogVisibility, updateDialInNumbers } from '../actions';
  14. import { InfoDialog } from './info-dialog';
  15. const { INITIAL_TOOLBAR_TIMEOUT } = interfaceConfig;
  16. /**
  17. * A configuration object to describe how {@code ToolbarButton} should render
  18. * the button.
  19. *
  20. * @type {object}
  21. */
  22. const DEFAULT_BUTTON_CONFIGURATION = {
  23. buttonName: 'info',
  24. classNames: [ 'button', 'icon-info' ],
  25. enabled: true,
  26. id: 'toolbar_button_info',
  27. tooltipKey: 'info.tooltip'
  28. };
  29. /**
  30. * A React Component for displaying a button which opens a dialog with
  31. * information about the conference and with ways to invite people.
  32. *
  33. * @extends Component
  34. */
  35. class InfoDialogButton extends Component {
  36. /**
  37. * {@code InfoDialogButton} component's property types.
  38. *
  39. * @static
  40. */
  41. static propTypes = {
  42. /**
  43. * Phone numbers for dialing into the conference.
  44. */
  45. _dialInNumbers: PropTypes.oneOfType([
  46. PropTypes.object,
  47. PropTypes.array
  48. ]),
  49. /**
  50. * Whether or not the {@code InfoDialog} should close by itself after a
  51. * a timeout.
  52. */
  53. _shouldAutoClose: PropTypes.bool,
  54. /**
  55. * Whether or not {@code InfoDialog} should be displayed.
  56. */
  57. _showDialog: PropTypes.bool,
  58. /**
  59. * Whether or not the toolbox, in which this component exists, are
  60. * visible.
  61. */
  62. _toolboxVisible: PropTypes.bool,
  63. /**
  64. * Invoked to toggle display of the info dialog
  65. */
  66. dispatch: PropTypes.func,
  67. /**
  68. * Invoked to obtain translated strings.
  69. */
  70. t: PropTypes.func,
  71. /**
  72. * From which side tooltips should display. Will be re-used for
  73. * displaying the inline dialog for video quality adjustment.
  74. */
  75. tooltipPosition: PropTypes.string
  76. };
  77. /**
  78. * Initializes new {@code ToolbarButtonWithDialog} instance.
  79. *
  80. * @param {Object} props - The read-only properties with which the new
  81. * instance is to be initialized.
  82. */
  83. constructor(props) {
  84. super(props);
  85. /**
  86. * The timeout to automatically hide the {@code InfoDialog} if it has
  87. * not been interacted with.
  88. *
  89. * @type {timeoutID}
  90. */
  91. this._autoHideDialogTimeout = null;
  92. // Bind event handlers so they are only bound once for every instance.
  93. this._onDialogClose = this._onDialogClose.bind(this);
  94. this._onDialogMouseOver = this._onDialogMouseOver.bind(this);
  95. this._onDialogToggle = this._onDialogToggle.bind(this);
  96. }
  97. /**
  98. * Set a timeout to automatically hide the {@code InfoDialog}.
  99. *
  100. * @inheritdoc
  101. */
  102. componentDidMount() {
  103. if (this.props._shouldAutoClose) {
  104. this._setAutoCloseTimeout();
  105. }
  106. if (!this.props._dialInNumbers) {
  107. this.props.dispatch(updateDialInNumbers());
  108. }
  109. }
  110. /**
  111. * Set or clear the timeout to automatically hide the {@code InfoDialog}.
  112. *
  113. * @inheritdoc
  114. */
  115. componentDidUpdate(prevProps) {
  116. // If the _shouldAutoClose flag has been updated to be true then make
  117. // sure to set _autoHideDialogTimeout.
  118. if (this.props._shouldAutoClose && !prevProps._shouldAutoClose) {
  119. this._setAutoCloseTimeout();
  120. } else {
  121. this._clearAutoCloseTimeout();
  122. }
  123. }
  124. /**
  125. * Update the visibility of the {@code InfoDialog}.
  126. *
  127. * @inheritdoc
  128. */
  129. componentWillReceiveProps(nextProps) {
  130. // Ensure the dialog is closed when the toolbox becomes hidden.
  131. if (nextProps._showDialog && !nextProps._toolboxVisible) {
  132. this._onDialogClose();
  133. }
  134. }
  135. /**
  136. * Clear the timeout to automatically show the {@code InfoDialog}.
  137. *
  138. * @inheritdoc
  139. */
  140. componentWillUnmount() {
  141. this._clearAutoCloseTimeout();
  142. }
  143. /**
  144. * Implements React's {@link Component#render()}.
  145. *
  146. * @inheritdoc
  147. * @returns {ReactElement}
  148. */
  149. render() {
  150. return interfaceConfig._USE_NEW_TOOLBOX
  151. ? this._renderNewToolbarButton()
  152. : this._renderOldToolbarButton();
  153. }
  154. /**
  155. * Cancels the timeout to automatically hide the {@code InfoDialog}.
  156. *
  157. * @private
  158. * @returns {void}
  159. */
  160. _clearAutoCloseTimeout() {
  161. clearTimeout(this._autoHideDialogTimeout);
  162. this._autoHideDialogTimeout = null;
  163. }
  164. /**
  165. * Hides {@code InfoDialog}.
  166. *
  167. * @private
  168. * @returns {void}
  169. */
  170. _onDialogClose() {
  171. this.props.dispatch(setInfoDialogVisibility(false));
  172. }
  173. /**
  174. * Cancels the timeout to automatically hide the {@code InfoDialog}.
  175. *
  176. * @private
  177. * @returns {void}
  178. */
  179. _onDialogMouseOver() {
  180. this._clearAutoCloseTimeout();
  181. }
  182. /**
  183. * Toggles the display of {@code InfoDialog}.
  184. *
  185. * @private
  186. * @returns {void}
  187. */
  188. _onDialogToggle() {
  189. sendAnalytics(createToolbarEvent('info'));
  190. this.props.dispatch(setInfoDialogVisibility(!this.props._showDialog));
  191. }
  192. /**
  193. * Renders a React Element for the {@code InfoDialog} using legacy
  194. * {@code ToolbarButton}.
  195. *
  196. * @private
  197. * @returns {ReactElement}
  198. */
  199. _renderOldToolbarButton() {
  200. const { _showDialog, _toolboxVisible, tooltipPosition } = this.props;
  201. const buttonConfiguration = {
  202. ...DEFAULT_BUTTON_CONFIGURATION,
  203. classNames: [
  204. ...DEFAULT_BUTTON_CONFIGURATION.classNames,
  205. _showDialog ? 'toggled button-active' : ''
  206. ]
  207. };
  208. return (
  209. <InlineDialog
  210. content = { <InfoDialog
  211. autoUpdateNumbers = { false }
  212. onClose = { this._onDialogClose }
  213. onMouseOver = { this._onDialogMouseOver } /> }
  214. isOpen = { _toolboxVisible && _showDialog }
  215. onClose = { this._onDialogClose }
  216. position = { TOOLTIP_TO_POPUP_POSITION[tooltipPosition] }>
  217. <ToolbarButton
  218. button = { buttonConfiguration }
  219. onClick = { this._onDialogToggle }
  220. tooltipPosition = { tooltipPosition } />
  221. </InlineDialog>
  222. );
  223. }
  224. /**
  225. * Renders a React Element for the {@code InfoDialog} using the newer
  226. * {@code ToolbarButtonV2}.
  227. *
  228. * @private
  229. * @returns {ReactElement}
  230. */
  231. _renderNewToolbarButton() {
  232. const { _showDialog, _toolboxVisible, t } = this.props;
  233. const iconClass = `icon-info ${_showDialog ? 'toggled' : ''}`;
  234. return (
  235. <div className = 'toolbox-button-wth-dialog'>
  236. <InlineDialog
  237. content = { <InfoDialog
  238. autoUpdateNumbers = { false }
  239. onClose = { this._onDialogClose }
  240. onMouseOver = { this._onDialogMouseOver } /> }
  241. isOpen = { _toolboxVisible && _showDialog }
  242. onClose = { this._onDialogClose }
  243. position = { 'top right' }>
  244. <ToolbarButtonV2
  245. iconName = { iconClass }
  246. onClick = { this._onDialogToggle }
  247. tooltip = { t('info.tooltip') } />
  248. </InlineDialog>
  249. </div>
  250. );
  251. }
  252. /**
  253. * Set a timeout to automatically hide the {@code InfoDialog}.
  254. *
  255. * @private
  256. * @returns {void}
  257. */
  258. _setAutoCloseTimeout() {
  259. this._clearAutoCloseTimeout();
  260. this._autoHideDialogTimeout = setTimeout(() => {
  261. if (this.props._showDialog) {
  262. this._onDialogClose();
  263. }
  264. }, INITIAL_TOOLBAR_TIMEOUT);
  265. }
  266. }
  267. /**
  268. * Maps (parts of) the Redux state to the associated {@code InfoDialogButton}
  269. * component's props.
  270. *
  271. * @param {Object} state - The Redux state.
  272. * @private
  273. * @returns {{
  274. * _dialInNumbers: Array,
  275. * _shouldAutoClose: boolean,
  276. * _showDialog: boolean,
  277. * _toolboxVisible: boolean
  278. * }}
  279. */
  280. function _mapStateToProps(state) {
  281. const {
  282. infoDialogVisible,
  283. infoDialogWillAutoClose,
  284. numbers
  285. } = state['features/invite'];
  286. return {
  287. _dialInNumbers: numbers,
  288. _shouldAutoClose: infoDialogWillAutoClose,
  289. _showDialog: infoDialogVisible,
  290. _toolboxVisible: state['features/toolbox'].visible
  291. };
  292. }
  293. export default translate(connect(_mapStateToProps)(InfoDialogButton));