Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

AudioMuteButton.tsx 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import React, { ReactElement } from 'react';
  2. import { connect } from 'react-redux';
  3. import { withStyles } from 'tss-react/mui';
  4. import { ACTION_SHORTCUT_TRIGGERED, AUDIO_MUTE, createShortcutEvent } from '../../../analytics/AnalyticsEvents';
  5. import { sendAnalytics } from '../../../analytics/functions';
  6. import { IReduxState } from '../../../app/types';
  7. import { translate } from '../../../base/i18n/functions';
  8. import { IGUMPendingState } from '../../../base/media/types';
  9. import AbstractButton from '../../../base/toolbox/components/AbstractButton';
  10. import Spinner from '../../../base/ui/components/web/Spinner';
  11. import { registerShortcut, unregisterShortcut } from '../../../keyboard-shortcuts/actions';
  12. import { SPINNER_COLOR } from '../../constants';
  13. import AbstractAudioMuteButton, {
  14. IProps as AbstractAudioMuteButtonProps,
  15. mapStateToProps as abstractMapStateToProps
  16. } from '../AbstractAudioMuteButton';
  17. const styles = () => {
  18. return {
  19. pendingContainer: {
  20. position: 'absolute' as const,
  21. bottom: '3px',
  22. right: '3px'
  23. }
  24. };
  25. };
  26. /**
  27. * The type of the React {@code Component} props of {@link AudioMuteButton}.
  28. */
  29. interface IProps extends AbstractAudioMuteButtonProps {
  30. /**
  31. * The gumPending state from redux.
  32. */
  33. _gumPending: IGUMPendingState;
  34. /**
  35. * An object containing the CSS classes.
  36. */
  37. classes?: Partial<Record<keyof ReturnType<typeof styles>, string>>;
  38. }
  39. /**
  40. * Component that renders a toolbar button for toggling audio mute.
  41. *
  42. * @augments AbstractAudioMuteButton
  43. */
  44. class AudioMuteButton extends AbstractAudioMuteButton<IProps> {
  45. /**
  46. * Initializes a new {@code AudioMuteButton} instance.
  47. *
  48. * @param {IProps} props - The read-only React {@code Component} props with
  49. * which the new instance is to be initialized.
  50. */
  51. constructor(props: IProps) {
  52. super(props);
  53. // Bind event handlers so they are only bound once per instance.
  54. this._onKeyboardShortcut = this._onKeyboardShortcut.bind(this);
  55. this._getTooltip = this._getLabel;
  56. }
  57. /**
  58. * Registers the keyboard shortcut that toggles the audio muting.
  59. *
  60. * @inheritdoc
  61. * @returns {void}
  62. */
  63. componentDidMount() {
  64. this.props.dispatch(registerShortcut({
  65. character: 'M',
  66. helpDescription: 'keyboardShortcuts.mute',
  67. handler: this._onKeyboardShortcut
  68. }));
  69. }
  70. /**
  71. * Unregisters the keyboard shortcut that toggles the audio muting.
  72. *
  73. * @inheritdoc
  74. * @returns {void}
  75. */
  76. componentWillUnmount() {
  77. this.props.dispatch(unregisterShortcut('M'));
  78. }
  79. /**
  80. * Gets the current accessibility label, taking the toggled and GUM pending state into account. If no toggled label
  81. * is provided, the regular accessibility label will also be used in the toggled state.
  82. *
  83. * The accessibility label is not visible in the UI, it is meant to be used by assistive technologies, mainly screen
  84. * readers.
  85. *
  86. * @private
  87. * @returns {string}
  88. */
  89. _getAccessibilityLabel() {
  90. const { _gumPending } = this.props;
  91. if (_gumPending === IGUMPendingState.NONE) {
  92. return super._getAccessibilityLabel();
  93. }
  94. return 'toolbar.accessibilityLabel.muteGUMPending';
  95. }
  96. /**
  97. * Gets the current label, taking the toggled and GUM pending state into account. If no
  98. * toggled label is provided, the regular label will also be used in the toggled state.
  99. *
  100. * @private
  101. * @returns {string}
  102. */
  103. _getLabel() {
  104. const { _gumPending } = this.props;
  105. if (_gumPending === IGUMPendingState.NONE) {
  106. return super._getLabel();
  107. }
  108. return 'toolbar.muteGUMPending';
  109. }
  110. /**
  111. * Indicates if audio is currently muted or not.
  112. *
  113. * @override
  114. * @protected
  115. * @returns {boolean}
  116. */
  117. _isAudioMuted() {
  118. if (this.props._gumPending === IGUMPendingState.PENDING_UNMUTE) {
  119. return false;
  120. }
  121. return super._isAudioMuted();
  122. }
  123. /**
  124. * Creates an analytics keyboard shortcut event and dispatches an action to
  125. * toggle the audio muting.
  126. *
  127. * @private
  128. * @returns {void}
  129. */
  130. _onKeyboardShortcut() {
  131. // Ignore keyboard shortcuts if the audio button is disabled.
  132. if (this._isDisabled()) {
  133. return;
  134. }
  135. sendAnalytics(
  136. createShortcutEvent(
  137. AUDIO_MUTE,
  138. ACTION_SHORTCUT_TRIGGERED,
  139. { enable: !this._isAudioMuted() }));
  140. AbstractButton.prototype._onClick.call(this);
  141. }
  142. /**
  143. * Returns a spinner if there is pending GUM.
  144. *
  145. * @returns {ReactElement | null}
  146. */
  147. _getElementAfter(): ReactElement | null {
  148. const { _gumPending } = this.props;
  149. const classes = withStyles.getClasses(this.props);
  150. return _gumPending === IGUMPendingState.NONE ? null
  151. : (
  152. <div className = { classes.pendingContainer }>
  153. <Spinner
  154. color = { SPINNER_COLOR }
  155. size = 'small' />
  156. </div>
  157. );
  158. }
  159. }
  160. /**
  161. * Maps (parts of) the redux state to the associated props for the
  162. * {@code AudioMuteButton} component.
  163. *
  164. * @param {Object} state - The Redux state.
  165. * @private
  166. * @returns {{
  167. * _audioMuted: boolean,
  168. * _disabled: boolean
  169. * }}
  170. */
  171. function _mapStateToProps(state: IReduxState) {
  172. const { gumPending } = state['features/base/media'].audio;
  173. return {
  174. ...abstractMapStateToProps(state),
  175. _gumPending: gumPending
  176. };
  177. }
  178. export default withStyles(translate(connect(_mapStateToProps)(AudioMuteButton)), styles);