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.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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 { getParticipantCount } from '../../base/participants';
  9. import {
  10. ToolbarButton,
  11. ToolbarButtonV2,
  12. TOOLTIP_TO_POPUP_POSITION
  13. } from '../../toolbox';
  14. import { updateDialInNumbers } from '../actions';
  15. import { InfoDialog } from './info-dialog';
  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. * The amount of time, in milliseconds, to wait until automatically showing
  31. * the {@code InfoDialog}. This is essentially a hack as automatic showing
  32. * should happen in a lonely call and some time is needed to populate
  33. * participants already in the call.
  34. */
  35. const INFO_DIALOG_AUTO_SHOW_TIMEOUT = 1500;
  36. /**
  37. * A React Component for displaying a button which opens a dialog with
  38. * information about the conference and with ways to invite people.
  39. *
  40. * @extends Component
  41. */
  42. class InfoDialogButton extends Component {
  43. /**
  44. * {@code InfoDialogButton} component's property types.
  45. *
  46. * @static
  47. */
  48. static propTypes = {
  49. /**
  50. * Phone numbers for dialing into the conference.
  51. */
  52. _dialInNumbers: PropTypes.oneOfType([
  53. PropTypes.object,
  54. PropTypes.array
  55. ]),
  56. /**
  57. * Whether or not the {@code InfoDialog} should display automatically
  58. * after {@link INFO_DIALOG_AUTO_SHOW_TIMEOUT}.
  59. */
  60. _disableAutoShow: PropTypes.bool,
  61. /**
  62. * The number of real participants in the call. If in a lonely call,
  63. * the {@code InfoDialog} will be automatically shown.
  64. */
  65. _participantCount: PropTypes.number,
  66. /**
  67. * Whether or not the toolbox, in which this component exists, are
  68. * visible.
  69. */
  70. _toolboxVisible: PropTypes.bool,
  71. /**
  72. * Invoked to toggle display of the info dialog
  73. */
  74. dispatch: PropTypes.func,
  75. /**
  76. * Invoked to obtain translated strings.
  77. */
  78. t: PropTypes.func,
  79. /**
  80. * From which side tooltips should display. Will be re-used for
  81. * displaying the inline dialog for video quality adjustment.
  82. */
  83. tooltipPosition: PropTypes.string
  84. };
  85. /**
  86. * Initializes new {@code ToolbarButtonWithDialog} instance.
  87. *
  88. * @param {Object} props - The read-only properties with which the new
  89. * instance is to be initialized.
  90. */
  91. constructor(props) {
  92. super(props);
  93. /**
  94. * The timeout to automatically show the {@code InfoDialog} if it has
  95. * not been shown yet in a lonely call.
  96. *
  97. * @type {timeoutID}
  98. */
  99. this._autoShowTimeout = null;
  100. this.state = {
  101. /**
  102. * Whether or not {@code InfoDialog} should be visible.
  103. */
  104. showDialog: false
  105. };
  106. // Bind event handlers so they are only bound once for every instance.
  107. this._onDialogClose = this._onDialogClose.bind(this);
  108. this._onDialogToggle = this._onDialogToggle.bind(this);
  109. }
  110. /**
  111. * Set a timeout to automatically hide the {@code InfoDialog}.
  112. *
  113. * @inheritdoc
  114. */
  115. componentDidMount() {
  116. this._autoShowTimeout = setTimeout(() => {
  117. this._maybeAutoShowDialog();
  118. }, INFO_DIALOG_AUTO_SHOW_TIMEOUT);
  119. if (!this.props._dialInNumbers) {
  120. this.props.dispatch(updateDialInNumbers());
  121. }
  122. }
  123. /**
  124. * Update the visibility of the {@code InfoDialog}.
  125. *
  126. * @inheritdoc
  127. */
  128. componentWillReceiveProps(nextProps) {
  129. // Ensure the dialog is closed when the toolbox becomes hidden.
  130. if (this.state.showDialog && !nextProps._toolboxVisible) {
  131. this._onDialogClose();
  132. }
  133. }
  134. /**
  135. * Clear the timeout to automatically show the {@code InfoDialog}.
  136. *
  137. * @inheritdoc
  138. */
  139. componentWillUnmount() {
  140. clearTimeout(this._autoShowTimeout);
  141. }
  142. /**
  143. * Implements React's {@link Component#render()}.
  144. *
  145. * @inheritdoc
  146. * @returns {ReactElement}
  147. */
  148. render() {
  149. return interfaceConfig._USE_NEW_TOOLBOX
  150. ? this._renderNewToolbarButton()
  151. : this._renderOldToolbarButton();
  152. }
  153. /**
  154. * Callback invoked after a timeout to trigger display of the
  155. * {@code InfoDialog} if certain conditions are met.
  156. *
  157. * @private
  158. * @returns {void}
  159. */
  160. _maybeAutoShowDialog() {
  161. if (this.props._participantCount < 2 && !this.props._disableAutoShow) {
  162. this.setState({ showDialog: true });
  163. }
  164. }
  165. /**
  166. * Hides {@code InfoDialog}.
  167. *
  168. * @private
  169. * @returns {void}
  170. */
  171. _onDialogClose() {
  172. this.setState({ showDialog: false });
  173. }
  174. /**
  175. * Toggles the display of {@code InfoDialog}.
  176. *
  177. * @private
  178. * @returns {void}
  179. */
  180. _onDialogToggle() {
  181. sendAnalytics(createToolbarEvent('info'));
  182. this.setState({ showDialog: !this.state.showDialog });
  183. }
  184. /**
  185. * Renders a React Element for the {@code InfoDialog} using legacy
  186. * {@code ToolbarButton}.
  187. *
  188. * @private
  189. * @returns {ReactElement}
  190. */
  191. _renderOldToolbarButton() {
  192. const { tooltipPosition } = this.props;
  193. const { showDialog } = this.state;
  194. const buttonConfiguration = {
  195. ...DEFAULT_BUTTON_CONFIGURATION,
  196. classNames: [
  197. ...DEFAULT_BUTTON_CONFIGURATION.classNames,
  198. showDialog ? 'toggled button-active' : ''
  199. ]
  200. };
  201. return (
  202. <InlineDialog
  203. content = { <InfoDialog onClose = { this._onDialogClose } /> }
  204. isOpen = { showDialog }
  205. onClose = { this._onDialogClose }
  206. position = { TOOLTIP_TO_POPUP_POSITION[tooltipPosition] }>
  207. <ToolbarButton
  208. button = { buttonConfiguration }
  209. onClick = { this._onDialogToggle }
  210. tooltipPosition = { tooltipPosition } />
  211. </InlineDialog>
  212. );
  213. }
  214. /**
  215. * Renders a React Element for the {@code InfoDialog} using the newer
  216. * {@code ToolbarButtonV2}.
  217. *
  218. * @private
  219. * @returns {ReactElement}
  220. */
  221. _renderNewToolbarButton() {
  222. const { t } = this.props;
  223. const { showDialog } = this.state;
  224. const iconClass = `icon-info ${showDialog ? 'toggled' : ''}`;
  225. return (
  226. <div className = 'toolbox-button-wth-dialog'>
  227. <InlineDialog
  228. content = {
  229. <InfoDialog onClose = { this._onDialogClose } /> }
  230. isOpen = { showDialog }
  231. onClose = { this._onDialogClose }
  232. position = { 'top right' }>
  233. <ToolbarButtonV2
  234. accessibilityLabel = 'Info'
  235. iconName = { iconClass }
  236. onClick = { this._onDialogToggle }
  237. tooltip = { t('info.tooltip') } />
  238. </InlineDialog>
  239. </div>
  240. );
  241. }
  242. }
  243. /**
  244. * Maps (parts of) the Redux state to the associated {@code InfoDialogButton}
  245. * component's props.
  246. *
  247. * @param {Object} state - The Redux state.
  248. * @private
  249. * @returns {{
  250. * _dialInNumbers: Array,
  251. * _disableAutoShow: bolean,
  252. * _participantCount: number,
  253. * _toolboxVisible: boolean
  254. * }}
  255. */
  256. function _mapStateToProps(state) {
  257. return {
  258. _dialInNumbers: state['features/invite'].numbers,
  259. _disableAutoShow: state['features/base/config'].iAmRecorder,
  260. _participantCount:
  261. getParticipantCount(state['features/base/participants']),
  262. _toolboxVisible: state['features/toolbox'].visible
  263. };
  264. }
  265. export default translate(connect(_mapStateToProps)(InfoDialogButton));