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.

Toolbox.js 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // @flow
  2. import React, { Component } from 'react';
  3. import { View } from 'react-native';
  4. import { connect } from 'react-redux';
  5. import { Container } from '../../../base/react';
  6. import {
  7. isNarrowAspectRatio,
  8. makeAspectRatioAware
  9. } from '../../../base/responsive-ui';
  10. import { InviteButton } from '../../../invite';
  11. import AudioMuteButton from '../AudioMuteButton';
  12. import HangupButton from '../HangupButton';
  13. import OverflowMenuButton from './OverflowMenuButton';
  14. import styles, {
  15. hangupButtonStyles,
  16. toolbarButtonStyles,
  17. toolbarToggledButtonStyles
  18. } from './styles';
  19. import VideoMuteButton from '../VideoMuteButton';
  20. /**
  21. * The number of buttons to render in {@link Toolbox}.
  22. *
  23. * @private
  24. * @type number
  25. */
  26. const _BUTTON_COUNT = 4;
  27. /**
  28. * Factor relating the hangup button and other toolbar buttons.
  29. *
  30. * @private
  31. * @type number
  32. */
  33. const _BUTTON_SIZE_FACTOR = 0.85;
  34. /**
  35. * The type of {@link Toolbox}'s React {@code Component} props.
  36. */
  37. type Props = {
  38. /**
  39. * The indicator which determines whether the toolbox is visible.
  40. */
  41. _visible: boolean,
  42. /**
  43. * The redux {@code dispatch} function.
  44. */
  45. dispatch: Function
  46. };
  47. /**
  48. * The type of {@link Toolbox}'s React {@code Component} state.
  49. */
  50. type State = {
  51. /**
  52. * The detected width for this component.
  53. */
  54. width: number
  55. };
  56. /**
  57. * Implements the conference toolbox on React Native.
  58. */
  59. class Toolbox extends Component<Props, State> {
  60. state = {
  61. width: 0
  62. };
  63. /**
  64. * Initializes a new {@code Toolbox} instance.
  65. *
  66. * @inheritdoc
  67. */
  68. constructor(props: Props) {
  69. super(props);
  70. // Bind event handlers so they are only bound once per instance.
  71. this._onLayout = this._onLayout.bind(this);
  72. }
  73. /**
  74. * Renders the toolbar. It will only be rendered if the {@code Toolbox} is
  75. * visible and we have already calculated the available width. This avoids
  76. * a weird effect in which the toolbar is displayed and then changes size.
  77. *
  78. * @returns {ReactElement}
  79. */
  80. _renderToolbar() {
  81. const { _visible } = this.props;
  82. const { width } = this.state;
  83. if (!_visible || !width) {
  84. return null;
  85. }
  86. let buttonStyles = toolbarButtonStyles;
  87. let toggledButtonStyles = toolbarToggledButtonStyles;
  88. const buttonSize = this._calculateButtonSize();
  89. if (buttonSize > 0) {
  90. const extraButtonStyle = {
  91. borderRadius: buttonSize / 2,
  92. height: buttonSize,
  93. width: buttonSize
  94. };
  95. buttonStyles = {
  96. ...buttonStyles,
  97. style: [ buttonStyles.style, extraButtonStyle ]
  98. };
  99. toggledButtonStyles = {
  100. ...toggledButtonStyles,
  101. style: [ toggledButtonStyles.style, extraButtonStyle ]
  102. };
  103. }
  104. return (
  105. <View
  106. pointerEvents = 'box-none'
  107. style = { styles.toolbar }>
  108. <InviteButton styles = { buttonStyles } />
  109. <AudioMuteButton
  110. styles = { buttonStyles }
  111. toggledStyles = { toggledButtonStyles } />
  112. <HangupButton styles = { hangupButtonStyles } />
  113. <VideoMuteButton
  114. styles = { buttonStyles }
  115. toggledStyles = { toggledButtonStyles } />
  116. <OverflowMenuButton
  117. styles = { buttonStyles }
  118. toggledStyles = { toggledButtonStyles } />
  119. </View>
  120. );
  121. }
  122. /**
  123. * Implements React's {@link Component#render()}.
  124. *
  125. * @inheritdoc
  126. * @returns {ReactElement}
  127. */
  128. render() {
  129. const toolboxStyle
  130. = isNarrowAspectRatio(this)
  131. ? styles.toolboxNarrow
  132. : styles.toolboxWide;
  133. const { _visible } = this.props;
  134. return (
  135. <Container
  136. onLayout = { this._onLayout }
  137. style = { toolboxStyle }
  138. visible = { _visible }>
  139. { this._renderToolbar() }
  140. </Container>
  141. );
  142. }
  143. /**
  144. * Calculates how large our toolbar buttons can be, given the available
  145. * width. In the future we might want to have a size threshold, and once
  146. * it's passed a completely different style could be used, akin to the web.
  147. *
  148. * @private
  149. * @returns {number}
  150. */
  151. _calculateButtonSize() {
  152. const { width } = this.state;
  153. const hangupButtonSize = styles.hangupButton.width;
  154. let buttonSize
  155. = (width
  156. - hangupButtonSize
  157. - (_BUTTON_COUNT
  158. * styles.toolbarButton.marginHorizontal * 2))
  159. / _BUTTON_COUNT;
  160. // Make sure it's an even number.
  161. buttonSize = 2 * Math.round(buttonSize / 2);
  162. // The button should be at most 80% of the hangup button's size.
  163. return Math.min(
  164. buttonSize,
  165. hangupButtonSize * _BUTTON_SIZE_FACTOR);
  166. }
  167. _onLayout: (Object) => void;
  168. /**
  169. * Handles the "on layout" View's event and stores the width as state.
  170. *
  171. * @param {Object} event - The "on layout" event object/structure passed
  172. * by react-native.
  173. * @private
  174. * @returns {void}
  175. */
  176. _onLayout({ nativeEvent: { layout: { width } } }) {
  177. this.setState({ width });
  178. }
  179. }
  180. /**
  181. * Maps parts of the redux state to {@link Toolbox} (React {@code Component})
  182. * props.
  183. *
  184. * @param {Object} state - The redux state of which parts are to be mapped to
  185. * {@code Toolbox} props.
  186. * @private
  187. * @returns {{
  188. * _visible: boolean
  189. * }}
  190. */
  191. function _mapStateToProps(state: Object): Object {
  192. const { enabled, visible } = state['features/toolbox'];
  193. return {
  194. _visible: enabled && visible
  195. };
  196. }
  197. export default connect(_mapStateToProps)(makeAspectRatioAware(Toolbox));