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.tsx 4.5KB

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