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.

AudioMuteButton.js 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // @flow
  2. import React, { Component } from 'react';
  3. // We need to reference these files directly to avoid loading things that are not available
  4. // in this environment (e.g. JitsiMeetJS or interfaceConfig)
  5. import { IconMicrophoneEmpty, IconMicrophoneEmptySlash } from '../base/icons';
  6. import type { Props } from '../base/toolbox/components/AbstractButton';
  7. import ToolbarButton from './ToolbarButton';
  8. const { api } = window.alwaysOnTop;
  9. /**
  10. * The type of the React {@code Component} state of {@link AudioMuteButton}.
  11. */
  12. type State = {
  13. /**
  14. * Whether audio is available is not.
  15. */
  16. audioAvailable: boolean,
  17. /**
  18. * Whether audio is muted or not.
  19. */
  20. audioMuted: boolean
  21. };
  22. /**
  23. * Stateless "mute/unmute audio" button for the Always-on-Top windows.
  24. */
  25. export default class AudioMuteButton extends Component<Props, State> {
  26. icon = IconMicrophoneEmpty;
  27. toggledIcon = IconMicrophoneEmptySlash;
  28. accessibilityLabel = 'Audio mute';
  29. /**
  30. * Initializes a new {@code AudioMuteButton} instance.
  31. *
  32. * @param {Props} props - The React {@code Component} props to initialize
  33. * the new {@code AudioMuteButton} instance with.
  34. */
  35. constructor(props: Props) {
  36. super(props);
  37. this.state = {
  38. audioAvailable: false,
  39. audioMuted: true
  40. };
  41. // Bind event handlers so they are only bound once per instance.
  42. this._audioAvailabilityListener
  43. = this._audioAvailabilityListener.bind(this);
  44. this._audioMutedListener = this._audioMutedListener.bind(this);
  45. this._onClick = this._onClick.bind(this);
  46. }
  47. /**
  48. * Sets mouse move listener and initial toolbar timeout.
  49. *
  50. * @inheritdoc
  51. * @returns {void}
  52. */
  53. componentDidMount() {
  54. api.on('audioAvailabilityChanged', this._audioAvailabilityListener);
  55. api.on('audioMuteStatusChanged', this._audioMutedListener);
  56. Promise.all([
  57. api.isAudioAvailable(),
  58. api.isAudioMuted(),
  59. api.isAudioDisabled?.() || Promise.resolve(false)
  60. ])
  61. .then(([ audioAvailable, audioMuted, audioDisabled ]) =>
  62. this.setState({
  63. audioAvailable: audioAvailable && !audioDisabled,
  64. audioMuted
  65. }))
  66. .catch(console.error);
  67. }
  68. /**
  69. * Removes all listeners.
  70. *
  71. * @inheritdoc
  72. * @returns {void}
  73. */
  74. componentWillUnmount() {
  75. api.removeListener(
  76. 'audioAvailabilityChanged',
  77. this._audioAvailabilityListener);
  78. api.removeListener(
  79. 'audioMuteStatusChanged',
  80. this._audioMutedListener);
  81. }
  82. _audioAvailabilityListener: ({ available: boolean }) => void;
  83. /**
  84. * Handles audio available api events.
  85. *
  86. * @param {{ available: boolean }} status - The new available status.
  87. * @returns {void}
  88. */
  89. _audioAvailabilityListener({ available }) {
  90. this.setState({ audioAvailable: available });
  91. }
  92. _audioMutedListener: ({ muted: boolean }) => void;
  93. /**
  94. * Handles audio muted api events.
  95. *
  96. * @param {{ muted: boolean }} status - The new muted status.
  97. * @returns {void}
  98. */
  99. _audioMutedListener({ muted }) {
  100. this.setState({ audioMuted: muted });
  101. }
  102. /**
  103. * Indicates if audio is currently muted or not.
  104. *
  105. * @override
  106. * @protected
  107. * @returns {boolean}
  108. */
  109. _isAudioMuted() {
  110. return this.state.audioMuted;
  111. }
  112. /**
  113. * Indicates whether this button is disabled or not.
  114. *
  115. * @override
  116. * @protected
  117. * @returns {boolean}
  118. */
  119. _isDisabled() {
  120. return !this.state.audioAvailable;
  121. }
  122. /**
  123. * Changes the muted state.
  124. *
  125. * @override
  126. * @param {boolean} audioMuted - Whether audio should be muted or not.
  127. * @protected
  128. * @returns {void}
  129. */
  130. _setAudioMuted(audioMuted: boolean) { // eslint-disable-line no-unused-vars
  131. this.state.audioAvailable && api.executeCommand('toggleAudio');
  132. }
  133. _onClick: () => {};
  134. /**
  135. * Handles clicking / pressing the button, and toggles the audio mute state
  136. * accordingly.
  137. *
  138. * @returns {void}
  139. */
  140. _onClick() {
  141. this._setAudioMuted(!this._isAudioMuted());
  142. }
  143. /**
  144. * Implements React's {@link Component#render()}.
  145. *
  146. * @inheritdoc
  147. * @returns {ReactElement}
  148. */
  149. render() {
  150. const toggled = this._isAudioMuted();
  151. return (<ToolbarButton
  152. accessibilityLabel = { this.accessibilityLabel }
  153. disabled = { this._isDisabled() }
  154. icon = { toggled ? this.toggledIcon : this.icon }
  155. onClick = { this._onClick }
  156. toggled = { toggled } />);
  157. }
  158. }