您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

VideoMuteButton.tsx 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import { ClassNameMap, withStyles } from '@mui/styles';
  2. import React, { ReactElement } from 'react';
  3. import { connect } from 'react-redux';
  4. import { ACTION_SHORTCUT_TRIGGERED, VIDEO_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 AbstractVideoMuteButton, {
  14. IProps as AbstractVideoMuteButtonProps,
  15. mapStateToProps as abstractMapStateToProps
  16. } from '../AbstractVideoMuteButton';
  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 VideoMuteButton}.
  28. */
  29. export interface IProps extends AbstractVideoMuteButtonProps {
  30. /**
  31. * The gumPending state from redux.
  32. */
  33. _gumPending: IGUMPendingState;
  34. /**
  35. * The @mui/styles classes.
  36. */
  37. classes: ClassNameMap<string>;
  38. }
  39. /**
  40. * Component that renders a toolbar button for toggling video mute.
  41. *
  42. * @augments AbstractVideoMuteButton
  43. */
  44. class VideoMuteButton extends AbstractVideoMuteButton<IProps> {
  45. /**
  46. * Initializes a new {@code VideoMuteButton} 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 video muting.
  59. *
  60. * @inheritdoc
  61. * @returns {void}
  62. */
  63. componentDidMount() {
  64. this.props.dispatch(registerShortcut({
  65. character: 'V',
  66. helpDescription: 'keyboardShortcuts.videoMute',
  67. handler: this._onKeyboardShortcut
  68. }));
  69. }
  70. /**
  71. * Unregisters the keyboard shortcut that toggles the video muting.
  72. *
  73. * @inheritdoc
  74. * @returns {void}
  75. */
  76. componentWillUnmount() {
  77. this.props.dispatch(unregisterShortcut('V'));
  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.videomuteGUMPending';
  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.videomuteGUMPending';
  109. }
  110. /**
  111. * Indicates if video is currently muted or not.
  112. *
  113. * @override
  114. * @protected
  115. * @returns {boolean}
  116. */
  117. _isVideoMuted() {
  118. if (this.props._gumPending === IGUMPendingState.PENDING_UNMUTE) {
  119. return false;
  120. }
  121. return super._isVideoMuted();
  122. }
  123. /**
  124. * Returns a spinner if there is pending GUM.
  125. *
  126. * @returns {ReactElement | null}
  127. */
  128. _getElementAfter(): ReactElement | null {
  129. const { _gumPending, classes } = this.props;
  130. return _gumPending === IGUMPendingState.NONE ? null
  131. : (
  132. <div className = { classes.pendingContainer }>
  133. <Spinner
  134. color = { SPINNER_COLOR }
  135. size = 'small' />
  136. </div>
  137. );
  138. }
  139. /**
  140. * Creates an analytics keyboard shortcut event and dispatches an action to
  141. * toggle the video muting.
  142. *
  143. * @private
  144. * @returns {void}
  145. */
  146. _onKeyboardShortcut() {
  147. // Ignore keyboard shortcuts if the video button is disabled.
  148. if (this._isDisabled()) {
  149. return;
  150. }
  151. sendAnalytics(
  152. createShortcutEvent(
  153. VIDEO_MUTE,
  154. ACTION_SHORTCUT_TRIGGERED,
  155. { enable: !this._isVideoMuted() }));
  156. AbstractButton.prototype._onClick.call(this);
  157. }
  158. }
  159. /**
  160. * Maps (parts of) the redux state to the associated props for the
  161. * {@code VideoMuteButton} component.
  162. *
  163. * @param {Object} state - The Redux state.
  164. * @private
  165. * @returns {{
  166. * _videoMuted: boolean
  167. * }}
  168. */
  169. function _mapStateToProps(state: IReduxState) {
  170. const { gumPending } = state['features/base/media'].video;
  171. return {
  172. ...abstractMapStateToProps(state),
  173. _gumPending: gumPending
  174. };
  175. }
  176. export default withStyles(styles)(translate(connect(_mapStateToProps)(VideoMuteButton)));