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.

VideoMuteButton.js 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // @flow
  2. import {
  3. ACTION_SHORTCUT_TRIGGERED,
  4. VIDEO_MUTE,
  5. createShortcutEvent,
  6. createToolbarEvent,
  7. sendAnalytics
  8. } from '../../analytics';
  9. import { setAudioOnly } from '../../base/audio-only';
  10. import { translate } from '../../base/i18n';
  11. import {
  12. VIDEO_MUTISM_AUTHORITY,
  13. setVideoMuted
  14. } from '../../base/media';
  15. import { connect } from '../../base/redux';
  16. import { AbstractVideoMuteButton } from '../../base/toolbox';
  17. import type { AbstractButtonProps } from '../../base/toolbox';
  18. import { getLocalVideoType, isLocalVideoTrackMuted } from '../../base/tracks';
  19. import UIEvents from '../../../../service/UI/UIEvents';
  20. declare var APP: Object;
  21. /**
  22. * The type of the React {@code Component} props of {@link VideoMuteButton}.
  23. */
  24. type Props = AbstractButtonProps & {
  25. /**
  26. * Whether the current conference is in audio only mode or not.
  27. */
  28. _audioOnly: boolean,
  29. /**
  30. * MEDIA_TYPE of the local video.
  31. */
  32. _videoMediaType: string,
  33. /**
  34. * Whether video is currently muted or not.
  35. */
  36. _videoMuted: boolean,
  37. /**
  38. * The redux {@code dispatch} function.
  39. */
  40. dispatch: Function
  41. }
  42. /**
  43. * Component that renders a toolbar button for toggling video mute.
  44. *
  45. * @extends AbstractVideoMuteButton
  46. */
  47. class VideoMuteButton extends AbstractVideoMuteButton<Props, *> {
  48. accessibilityLabel = 'toolbar.accessibilityLabel.videomute';
  49. label = 'toolbar.videomute';
  50. tooltip = 'toolbar.videomute';
  51. /**
  52. * Initializes a new {@code VideoMuteButton} instance.
  53. *
  54. * @param {Props} props - The read-only React {@code Component} props with
  55. * which the new instance is to be initialized.
  56. */
  57. constructor(props: Props) {
  58. super(props);
  59. // Bind event handlers so they are only bound once per instance.
  60. this._onKeyboardShortcut = this._onKeyboardShortcut.bind(this);
  61. }
  62. /**
  63. * Registers the keyboard shortcut that toggles the video muting.
  64. *
  65. * @inheritdoc
  66. * @returns {void}
  67. */
  68. componentDidMount() {
  69. typeof APP === 'undefined'
  70. || APP.keyboardshortcut.registerShortcut(
  71. 'V',
  72. null,
  73. this._onKeyboardShortcut,
  74. 'keyboardShortcuts.videoMute');
  75. }
  76. /**
  77. * Unregisters the keyboard shortcut that toggles the video muting.
  78. *
  79. * @inheritdoc
  80. * @returns {void}
  81. */
  82. componentWillUnmount() {
  83. typeof APP === 'undefined'
  84. || APP.keyboardshortcut.unregisterShortcut('V');
  85. }
  86. /**
  87. * Indicates if video is currently muted ot nor.
  88. *
  89. * @override
  90. * @protected
  91. * @returns {boolean}
  92. */
  93. _isVideoMuted() {
  94. return this.props._videoMuted;
  95. }
  96. _onKeyboardShortcut: () => void;
  97. /**
  98. * Creates an analytics keyboard shortcut event and dispatches an action to
  99. * toggle the video muting.
  100. *
  101. * @private
  102. * @returns {void}
  103. */
  104. _onKeyboardShortcut() {
  105. sendAnalytics(
  106. createShortcutEvent(
  107. VIDEO_MUTE,
  108. ACTION_SHORTCUT_TRIGGERED,
  109. { enable: !this._isVideoMuted() }));
  110. super._handleClick();
  111. }
  112. /**
  113. * Changes the muted state.
  114. *
  115. * @override
  116. * @param {boolean} videoMuted - Whether video should be muted or not.
  117. * @protected
  118. * @returns {void}
  119. */
  120. _setVideoMuted(videoMuted: boolean) {
  121. sendAnalytics(createToolbarEvent(VIDEO_MUTE, { enable: videoMuted }));
  122. if (this.props._audioOnly) {
  123. this.props.dispatch(
  124. setAudioOnly(false, /* ensureTrack */ true));
  125. }
  126. const mediaType = this.props._videoMediaType;
  127. this.props.dispatch(
  128. setVideoMuted(
  129. videoMuted,
  130. mediaType,
  131. VIDEO_MUTISM_AUTHORITY.USER,
  132. /* ensureTrack */ true));
  133. // FIXME: The old conference logic still relies on this event being
  134. // emitted.
  135. typeof APP === 'undefined'
  136. || APP.UI.emitEvent(UIEvents.VIDEO_MUTED, videoMuted, true);
  137. }
  138. }
  139. /**
  140. * Maps (parts of) the redux state to the associated props for the
  141. * {@code VideoMuteButton} component.
  142. *
  143. * @param {Object} state - The Redux state.
  144. * @private
  145. * @returns {{
  146. * _audioOnly: boolean,
  147. * _videoMuted: boolean
  148. * }}
  149. */
  150. function _mapStateToProps(state): Object {
  151. const { enabled: audioOnly } = state['features/base/audio-only'];
  152. const tracks = state['features/base/tracks'];
  153. return {
  154. _audioOnly: Boolean(audioOnly),
  155. _videoMediaType: getLocalVideoType(tracks),
  156. _videoMuted: isLocalVideoTrackMuted(tracks)
  157. };
  158. }
  159. export default translate(connect(_mapStateToProps)(VideoMuteButton));