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.

OverflowMenu.js 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. // @flow
  2. import React, { PureComponent } from 'react';
  3. import { Platform, TouchableOpacity, View } from 'react-native';
  4. import Collapsible from 'react-native-collapsible';
  5. import { ColorSchemeRegistry } from '../../../base/color-scheme';
  6. import { BottomSheet, hideDialog, isDialogOpen } from '../../../base/dialog';
  7. import { IconDragHandle } from '../../../base/icons';
  8. import { CHAT_ENABLED, IOS_RECORDING_ENABLED, getFeatureFlag } from '../../../base/flags';
  9. import { connect } from '../../../base/redux';
  10. import { StyleType } from '../../../base/styles';
  11. import { SharedDocumentButton } from '../../../etherpad';
  12. import { InfoDialogButton, InviteButton } from '../../../invite';
  13. import { AudioRouteButton } from '../../../mobile/audio-mode';
  14. import { LiveStreamButton, RecordButton } from '../../../recording';
  15. import { RoomLockButton } from '../../../room-lock';
  16. import { ClosedCaptionButton } from '../../../subtitles';
  17. import { TileViewButton } from '../../../video-layout';
  18. import HelpButton from '../HelpButton';
  19. import AudioOnlyButton from './AudioOnlyButton';
  20. import MoreOptionsButton from './MoreOptionsButton';
  21. import RaiseHandButton from './RaiseHandButton';
  22. import ToggleCameraButton from './ToggleCameraButton';
  23. import styles from './styles';
  24. /**
  25. * The type of the React {@code Component} props of {@link OverflowMenu}.
  26. */
  27. type Props = {
  28. /**
  29. * The color-schemed stylesheet of the dialog feature.
  30. */
  31. _bottomSheetStyles: StyleType,
  32. /**
  33. * Whether the chat feature has been enabled. The meeting info button will be displayed in its place when disabled.
  34. */
  35. _chatEnabled: boolean,
  36. /**
  37. * True if the overflow menu is currently visible, false otherwise.
  38. */
  39. _isOpen: boolean,
  40. /**
  41. * Whether the recoding button should be enabled or not.
  42. */
  43. _recordingEnabled: boolean,
  44. /**
  45. * Used for hiding the dialog when the selection was completed.
  46. */
  47. dispatch: Function
  48. };
  49. type State = {
  50. /**
  51. * True if the bottom scheet is scrolled to the top.
  52. */
  53. scrolledToTop: boolean,
  54. /**
  55. * True if the 'more' button set needas to be rendered.
  56. */
  57. showMore: boolean
  58. }
  59. /**
  60. * The exported React {@code Component}. We need it to execute
  61. * {@link hideDialog}.
  62. *
  63. * XXX It does not break our coding style rule to not utilize globals for state,
  64. * because it is merely another name for {@code export}'s {@code default}.
  65. */
  66. let OverflowMenu_; // eslint-disable-line prefer-const
  67. /**
  68. * Implements a React {@code Component} with some extra actions in addition to
  69. * those in the toolbar.
  70. */
  71. class OverflowMenu extends PureComponent<Props, State> {
  72. /**
  73. * Initializes a new {@code OverflowMenu} instance.
  74. *
  75. * @inheritdoc
  76. */
  77. constructor(props: Props) {
  78. super(props);
  79. this.state = {
  80. scrolledToTop: true,
  81. showMore: false
  82. };
  83. // Bind event handlers so they are only bound once per instance.
  84. this._onCancel = this._onCancel.bind(this);
  85. this._onSwipe = this._onSwipe.bind(this);
  86. this._onToggleMenu = this._onToggleMenu.bind(this);
  87. this._renderMenuExpandToggle = this._renderMenuExpandToggle.bind(this);
  88. }
  89. /**
  90. * Implements React's {@link Component#render()}.
  91. *
  92. * @inheritdoc
  93. * @returns {ReactElement}
  94. */
  95. render() {
  96. const { _bottomSheetStyles } = this.props;
  97. const { showMore } = this.state;
  98. const buttonProps = {
  99. afterClick: this._onCancel,
  100. showLabel: true,
  101. styles: _bottomSheetStyles.buttons
  102. };
  103. const moreOptionsButtonProps = {
  104. ...buttonProps,
  105. afterClick: this._onToggleMenu,
  106. visible: !showMore
  107. };
  108. return (
  109. <BottomSheet
  110. onCancel = { this._onCancel }
  111. onSwipe = { this._onSwipe }
  112. renderHeader = { this._renderMenuExpandToggle }>
  113. <AudioRouteButton { ...buttonProps } />
  114. <ToggleCameraButton { ...buttonProps } />
  115. <AudioOnlyButton { ...buttonProps } />
  116. <MoreOptionsButton { ...moreOptionsButtonProps } />
  117. <Collapsible collapsed = { !showMore }>
  118. <RoomLockButton { ...buttonProps } />
  119. <ClosedCaptionButton { ...buttonProps } />
  120. {
  121. this.props._recordingEnabled
  122. && <RecordButton { ...buttonProps } />
  123. }
  124. <LiveStreamButton { ...buttonProps } />
  125. <TileViewButton { ...buttonProps } />
  126. <InviteButton { ...buttonProps } />
  127. {
  128. this.props._chatEnabled
  129. && <InfoDialogButton { ...buttonProps } />
  130. }
  131. <RaiseHandButton { ...buttonProps } />
  132. <SharedDocumentButton { ...buttonProps } />
  133. <HelpButton { ...buttonProps } />
  134. </Collapsible>
  135. </BottomSheet>
  136. );
  137. }
  138. _renderMenuExpandToggle: () => React$Element<any>;
  139. /**
  140. * Function to render the menu toggle in the bottom sheet header area.
  141. *
  142. * @returns {React$Element}
  143. */
  144. _renderMenuExpandToggle() {
  145. return (
  146. <View
  147. style = { [
  148. this.props._bottomSheetStyles.sheet,
  149. styles.expandMenuContainer
  150. ] }>
  151. <TouchableOpacity onPress = { this._onToggleMenu }>
  152. { /* $FlowFixMeProps */ }
  153. <IconDragHandle style = { this.props._bottomSheetStyles.expandIcon } />
  154. </TouchableOpacity>
  155. </View>
  156. );
  157. }
  158. _onCancel: () => boolean;
  159. /**
  160. * Hides this {@code OverflowMenu}.
  161. *
  162. * @private
  163. * @returns {boolean}
  164. */
  165. _onCancel() {
  166. if (this.props._isOpen) {
  167. this.props.dispatch(hideDialog(OverflowMenu_));
  168. return true;
  169. }
  170. return false;
  171. }
  172. _onSwipe: string => void;
  173. /**
  174. * Callback to be invoked when swipe gesture is detected on the menu. Returns true
  175. * if the swipe gesture is handled by the menu, false otherwise.
  176. *
  177. * @param {string} direction - Direction of 'up' or 'down'.
  178. * @returns {boolean}
  179. */
  180. _onSwipe(direction) {
  181. const { showMore } = this.state;
  182. switch (direction) {
  183. case 'up':
  184. !showMore && this.setState({
  185. showMore: true
  186. });
  187. return !showMore;
  188. case 'down':
  189. showMore && this.setState({
  190. showMore: false
  191. });
  192. return showMore;
  193. }
  194. }
  195. _onToggleMenu: () => void;
  196. /**
  197. * Callback to be invoked when the expand menu button is pressed.
  198. *
  199. * @returns {void}
  200. */
  201. _onToggleMenu() {
  202. this.setState({
  203. showMore: !this.state.showMore
  204. });
  205. }
  206. }
  207. /**
  208. * Function that maps parts of Redux state tree into component props.
  209. *
  210. * @param {Object} state - Redux state.
  211. * @private
  212. * @returns {Props}
  213. */
  214. function _mapStateToProps(state) {
  215. return {
  216. _bottomSheetStyles: ColorSchemeRegistry.get(state, 'BottomSheet'),
  217. _chatEnabled: getFeatureFlag(state, CHAT_ENABLED, true),
  218. _isOpen: isDialogOpen(state, OverflowMenu_),
  219. _recordingEnabled: Platform.OS !== 'ios' || getFeatureFlag(state, IOS_RECORDING_ENABLED)
  220. };
  221. }
  222. OverflowMenu_ = connect(_mapStateToProps)(OverflowMenu);
  223. export default OverflowMenu_;