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.native.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. import React, { Component } from 'react';
  2. import { View } from 'react-native';
  3. import { connect } from 'react-redux';
  4. import { toggleAudioOnly } from '../../base/conference';
  5. import {
  6. MEDIA_TYPE,
  7. setAudioMuted,
  8. setVideoMuted,
  9. toggleCameraFacingMode,
  10. VIDEO_MUTISM_AUTHORITY
  11. } from '../../base/media';
  12. import { Container } from '../../base/react';
  13. import { ColorPalette } from '../../base/styles';
  14. import { beginRoomLockRequest } from '../../room-lock';
  15. import { beginShareRoom } from '../../share-room';
  16. import {
  17. abstractMapDispatchToProps,
  18. abstractMapStateToProps
  19. } from '../functions';
  20. import styles from './styles';
  21. import ToolbarButton from './ToolbarButton';
  22. /**
  23. * The indicator which determines (at bundle time) whether there should be a
  24. * {@code ToolbarButton} in {@code Toolbox} to expose the functionality of the
  25. * feature share-room in the user interface of the app.
  26. *
  27. * @private
  28. * @type {boolean}
  29. */
  30. const _SHARE_ROOM_TOOLBAR_BUTTON = true;
  31. /**
  32. * Implements the conference toolbox on React Native.
  33. */
  34. class Toolbox extends Component {
  35. /**
  36. * Toolbox component's property types.
  37. *
  38. * @static
  39. */
  40. static propTypes = {
  41. /**
  42. * Flag showing that audio is muted.
  43. */
  44. _audioMuted: React.PropTypes.bool,
  45. /**
  46. * Flag showing whether the audio-only mode is in use.
  47. */
  48. _audioOnly: React.PropTypes.bool,
  49. /**
  50. * Flag showing whether room is locked.
  51. */
  52. _locked: React.PropTypes.bool,
  53. /**
  54. * Handler for hangup.
  55. */
  56. _onHangup: React.PropTypes.func,
  57. /**
  58. * Sets the lock i.e. password protection of the conference/room.
  59. */
  60. _onRoomLock: React.PropTypes.func,
  61. /**
  62. * Begins the UI procedure to share the conference/room URL.
  63. */
  64. _onShareRoom: React.PropTypes.func,
  65. /**
  66. * Toggles the audio-only flag of the conference.
  67. */
  68. _onToggleAudioOnly: React.PropTypes.func,
  69. /**
  70. * Switches between the front/user-facing and back/environment-facing
  71. * cameras.
  72. */
  73. _onToggleCameraFacingMode: React.PropTypes.func,
  74. /**
  75. * Flag showing whether video is muted.
  76. */
  77. _videoMuted: React.PropTypes.bool,
  78. /**
  79. * Flag showing whether toolbar is visible.
  80. */
  81. _visible: React.PropTypes.bool,
  82. dispatch: React.PropTypes.func
  83. };
  84. /**
  85. * Initializes a new {@code Toolbox} instance.
  86. *
  87. * @param {Object} props - The read-only React {@code Component} props with
  88. * which the new instance is to be initialized.
  89. */
  90. constructor(props) {
  91. super(props);
  92. // Bind event handlers so they are only bound once per instance.
  93. this._onToggleAudio = this._onToggleAudio.bind(this);
  94. this._onToggleVideo = this._onToggleVideo.bind(this);
  95. }
  96. /**
  97. * Implements React's {@link Component#render()}.
  98. *
  99. * @inheritdoc
  100. * @returns {ReactElement}
  101. */
  102. render() {
  103. return (
  104. <Container
  105. style = { styles.toolbarContainer }
  106. visible = { this.props._visible }>
  107. {
  108. this._renderPrimaryToolbar()
  109. }
  110. {
  111. this._renderSecondaryToolbar()
  112. }
  113. </Container>
  114. );
  115. }
  116. /**
  117. * Gets the styles for a button that toggles the mute state of a specific
  118. * media type.
  119. *
  120. * @param {string} mediaType - The {@link MEDIA_TYPE} associated with the
  121. * button to get styles for.
  122. * @protected
  123. * @returns {{
  124. * iconName: string,
  125. * iconStyle: Object,
  126. * style: Object
  127. * }}
  128. */
  129. _getMuteButtonStyles(mediaType) {
  130. let iconName;
  131. let iconStyle;
  132. let style;
  133. if (this.props[`_${mediaType}Muted`]) {
  134. iconName = this[`${mediaType}MutedIcon`];
  135. iconStyle = styles.whitePrimaryToolbarButtonIcon;
  136. style = styles.whitePrimaryToolbarButton;
  137. } else {
  138. iconName = this[`${mediaType}Icon`];
  139. iconStyle = styles.primaryToolbarButtonIcon;
  140. style = styles.primaryToolbarButton;
  141. }
  142. return {
  143. iconName,
  144. iconStyle,
  145. style
  146. };
  147. }
  148. /**
  149. * Dispatches an action to toggle the mute state of the audio/microphone.
  150. *
  151. * @private
  152. * @returns {void}
  153. */
  154. _onToggleAudio() {
  155. // The user sees the reality i.e. the state of base/tracks and intends
  156. // to change reality by tapping on the respective button i.e. the user
  157. // sets the state of base/media. Whether the user's intention will turn
  158. // into reality is a whole different story which is of no concern to the
  159. // tapping.
  160. this.props.dispatch(
  161. setAudioMuted(
  162. !this.props._audioMuted,
  163. VIDEO_MUTISM_AUTHORITY.USER,
  164. /* ensureTrack */ true));
  165. }
  166. /**
  167. * Dispatches an action to toggle the mute state of the video/camera.
  168. *
  169. * @private
  170. * @returns {void}
  171. */
  172. _onToggleVideo() {
  173. // The user sees the reality i.e. the state of base/tracks and intends
  174. // to change reality by tapping on the respective button i.e. the user
  175. // sets the state of base/media. Whether the user's intention will turn
  176. // into reality is a whole different story which is of no concern to the
  177. // tapping.
  178. this.props.dispatch(
  179. setVideoMuted(
  180. !this.props._videoMuted,
  181. VIDEO_MUTISM_AUTHORITY.USER,
  182. /* ensureTrack */ true));
  183. }
  184. /**
  185. * Renders the toolbar which contains the primary buttons such as hangup,
  186. * audio and video mute.
  187. *
  188. * @private
  189. * @returns {ReactElement}
  190. */
  191. _renderPrimaryToolbar() {
  192. const audioButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.AUDIO);
  193. const videoButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.VIDEO);
  194. /* eslint-disable react/jsx-handler-names */
  195. return (
  196. <View style = { styles.primaryToolbar }>
  197. <ToolbarButton
  198. iconName = { audioButtonStyles.iconName }
  199. iconStyle = { audioButtonStyles.iconStyle }
  200. onClick = { this._onToggleAudio }
  201. style = { audioButtonStyles.style } />
  202. <ToolbarButton
  203. iconName = 'hangup'
  204. iconStyle = { styles.whitePrimaryToolbarButtonIcon }
  205. onClick = { this.props._onHangup }
  206. style = { styles.hangup }
  207. underlayColor = { ColorPalette.buttonUnderlay } />
  208. <ToolbarButton
  209. disabled = { this.props._audioOnly }
  210. iconName = { videoButtonStyles.iconName }
  211. iconStyle = { videoButtonStyles.iconStyle }
  212. onClick = { this._onToggleVideo }
  213. style = { videoButtonStyles.style } />
  214. </View>
  215. );
  216. /* eslint-enable react/jsx-handler-names */
  217. }
  218. /**
  219. * Renders the toolbar which contains the secondary buttons such as toggle
  220. * camera facing mode.
  221. *
  222. * @private
  223. * @returns {ReactElement}
  224. */
  225. _renderSecondaryToolbar() {
  226. const iconStyle = styles.secondaryToolbarButtonIcon;
  227. const style = styles.secondaryToolbarButton;
  228. const underlayColor = 'transparent';
  229. const {
  230. _audioOnly: audioOnly,
  231. _videoMuted: videoMuted
  232. } = this.props;
  233. /* eslint-disable react/jsx-curly-spacing,react/jsx-handler-names */
  234. return (
  235. <View style = { styles.secondaryToolbar }>
  236. <ToolbarButton
  237. disabled = { audioOnly || videoMuted }
  238. iconName = 'switch-camera'
  239. iconStyle = { iconStyle }
  240. onClick = { this.props._onToggleCameraFacingMode }
  241. style = { style }
  242. underlayColor = { underlayColor } />
  243. <ToolbarButton
  244. iconName = {
  245. this.props._locked ? 'security-locked' : 'security'
  246. }
  247. iconStyle = { iconStyle }
  248. onClick = { this.props._onRoomLock }
  249. style = { style }
  250. underlayColor = { underlayColor } />
  251. <ToolbarButton
  252. iconName = { audioOnly ? 'visibility-off' : 'visibility' }
  253. iconStyle = { iconStyle }
  254. onClick = { this.props._onToggleAudioOnly }
  255. style = { style }
  256. underlayColor = { underlayColor } />
  257. {
  258. _SHARE_ROOM_TOOLBAR_BUTTON
  259. && <ToolbarButton
  260. iconName = 'link'
  261. iconStyle = { iconStyle }
  262. onClick = { this.props._onShareRoom }
  263. style = { style }
  264. underlayColor = { underlayColor } />
  265. }
  266. </View>
  267. );
  268. /* eslint-enable react/jsx-curly-spacing,react/jsx-handler-names */
  269. }
  270. }
  271. /**
  272. * Additional properties for various icons, which are now platform-dependent.
  273. * This is done to have common logic of generating styles for web and native.
  274. * TODO As soon as we have common font sets for web and native, this will no
  275. * longer be required.
  276. */
  277. Object.assign(Toolbox.prototype, {
  278. audioIcon: 'microphone',
  279. audioMutedIcon: 'mic-disabled',
  280. videoIcon: 'camera',
  281. videoMutedIcon: 'camera-disabled'
  282. });
  283. /**
  284. * Maps actions to React component props.
  285. *
  286. * @param {Function} dispatch - Redux action dispatcher.
  287. * @returns {{
  288. * _onRoomLock: Function,
  289. * _onToggleAudioOnly: Function,
  290. * _onToggleCameraFacingMode: Function,
  291. * }}
  292. * @private
  293. */
  294. function _mapDispatchToProps(dispatch) {
  295. return {
  296. ...abstractMapDispatchToProps(dispatch),
  297. /**
  298. * Sets the lock i.e. password protection of the conference/room.
  299. *
  300. * @private
  301. * @returns {void}
  302. * @type {Function}
  303. */
  304. _onRoomLock() {
  305. dispatch(beginRoomLockRequest());
  306. },
  307. /**
  308. * Begins the UI procedure to share the conference/room URL.
  309. *
  310. * @private
  311. * @returns {void}
  312. * @type {Function}
  313. */
  314. _onShareRoom() {
  315. dispatch(beginShareRoom());
  316. },
  317. /**
  318. * Toggles the audio-only flag of the conference.
  319. *
  320. * @private
  321. * @returns {void}
  322. * @type {Function}
  323. */
  324. _onToggleAudioOnly() {
  325. dispatch(toggleAudioOnly());
  326. },
  327. /**
  328. * Switches between the front/user-facing and back/environment-facing
  329. * cameras.
  330. *
  331. * @private
  332. * @returns {void}
  333. * @type {Function}
  334. */
  335. _onToggleCameraFacingMode() {
  336. dispatch(toggleCameraFacingMode());
  337. }
  338. };
  339. }
  340. /**
  341. * Maps part of Redux store to React component props.
  342. *
  343. * @param {Object} state - Redux store.
  344. * @returns {{
  345. * _audioOnly: boolean,
  346. * _locked: boolean
  347. * }}
  348. * @private
  349. */
  350. function _mapStateToProps(state) {
  351. const conference = state['features/base/conference'];
  352. return {
  353. ...abstractMapStateToProps(state),
  354. /**
  355. * The indicator which determines whether the conference is in
  356. * audio-only mode.
  357. *
  358. * @protected
  359. * @type {boolean}
  360. */
  361. _audioOnly: Boolean(conference.audioOnly),
  362. /**
  363. * The indicator which determines whether the conference is
  364. * locked/password-protected.
  365. *
  366. * @protected
  367. * @type {boolean}
  368. */
  369. _locked: Boolean(conference.locked)
  370. };
  371. }
  372. export default connect(_mapStateToProps, _mapDispatchToProps)(Toolbox);