feat(alwaysontop): refactor toolbox
Use the new abstractions, which already take care of the rendering part.
This commit is contained in:
parent
b634f6b200
commit
01cb4ac7c8
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
import ToolboxAlwaysOnTop from './ToolboxAlwaysOnTop';
|
import Toolbar from './Toolbar';
|
||||||
|
|
||||||
const { api } = window.alwaysOnTop;
|
const { api } = window.alwaysOnTop;
|
||||||
|
|
||||||
|
@ -15,13 +15,9 @@ const TOOLBAR_TIMEOUT = 4000;
|
||||||
* The type of the React {@code Component} state of {@link FeedbackButton}.
|
* The type of the React {@code Component} state of {@link FeedbackButton}.
|
||||||
*/
|
*/
|
||||||
type State = {
|
type State = {
|
||||||
audioAvailable: boolean,
|
|
||||||
audioMuted: boolean,
|
|
||||||
avatarURL: string,
|
avatarURL: string,
|
||||||
displayName: string,
|
displayName: string,
|
||||||
isVideoDisplayed: boolean,
|
isVideoDisplayed: boolean,
|
||||||
videoAvailable: boolean,
|
|
||||||
videoMuted: boolean,
|
|
||||||
visible: boolean
|
visible: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,19 +41,12 @@ export default class AlwaysOnTop extends Component<*, State> {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
visible: true,
|
visible: true,
|
||||||
audioMuted: false,
|
|
||||||
videoMuted: false,
|
|
||||||
audioAvailable: false,
|
|
||||||
videoAvailable: false,
|
|
||||||
displayName: '',
|
displayName: '',
|
||||||
isVideoDisplayed: true,
|
isVideoDisplayed: true,
|
||||||
avatarURL: ''
|
avatarURL: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bind event handlers so they are only bound once per instance.
|
// Bind event handlers so they are only bound once per instance.
|
||||||
this._audioAvailabilityListener
|
|
||||||
= this._audioAvailabilityListener.bind(this);
|
|
||||||
this._audioMutedListener = this._audioMutedListener.bind(this);
|
|
||||||
this._avatarChangedListener = this._avatarChangedListener.bind(this);
|
this._avatarChangedListener = this._avatarChangedListener.bind(this);
|
||||||
this._largeVideoChangedListener
|
this._largeVideoChangedListener
|
||||||
= this._largeVideoChangedListener.bind(this);
|
= this._largeVideoChangedListener.bind(this);
|
||||||
|
@ -66,33 +55,6 @@ export default class AlwaysOnTop extends Component<*, State> {
|
||||||
this._mouseMove = this._mouseMove.bind(this);
|
this._mouseMove = this._mouseMove.bind(this);
|
||||||
this._onMouseOut = this._onMouseOut.bind(this);
|
this._onMouseOut = this._onMouseOut.bind(this);
|
||||||
this._onMouseOver = this._onMouseOver.bind(this);
|
this._onMouseOver = this._onMouseOver.bind(this);
|
||||||
this._videoAvailabilityListener
|
|
||||||
= this._videoAvailabilityListener.bind(this);
|
|
||||||
this._videoMutedListener = this._videoMutedListener.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_audioAvailabilityListener: ({ available: boolean }) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles audio available api events.
|
|
||||||
*
|
|
||||||
* @param {{ available: boolean }} status - The new available status.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_audioAvailabilityListener({ available }) {
|
|
||||||
this.setState({ audioAvailable: available });
|
|
||||||
}
|
|
||||||
|
|
||||||
_audioMutedListener: ({ muted: boolean }) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles audio muted api events.
|
|
||||||
*
|
|
||||||
* @param {{ muted: boolean }} status - The new muted status.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_audioMutedListener({ muted }) {
|
|
||||||
this.setState({ audioMuted: muted });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_avatarChangedListener: () => void;
|
_avatarChangedListener: () => void;
|
||||||
|
@ -200,8 +162,6 @@ export default class AlwaysOnTop extends Component<*, State> {
|
||||||
this._hovered = true;
|
this._hovered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_videoAvailabilityListener: ({ available: boolean }) => void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders display name and avatar for the on stage participant.
|
* Renders display name and avatar for the on stage participant.
|
||||||
*
|
*
|
||||||
|
@ -234,28 +194,6 @@ export default class AlwaysOnTop extends Component<*, State> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles audio available api events.
|
|
||||||
*
|
|
||||||
* @param {{ available: boolean }} status - The new available status.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_videoAvailabilityListener({ available }) {
|
|
||||||
this.setState({ videoAvailable: available });
|
|
||||||
}
|
|
||||||
|
|
||||||
_videoMutedListener: ({ muted: boolean }) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles video muted api events.
|
|
||||||
*
|
|
||||||
* @param {{ muted: boolean }} status - The new muted status.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_videoMutedListener({ muted }) {
|
|
||||||
this.setState({ videoMuted: muted });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets mouse move listener and initial toolbar timeout.
|
* Sets mouse move listener and initial toolbar timeout.
|
||||||
*
|
*
|
||||||
|
@ -263,37 +201,12 @@ export default class AlwaysOnTop extends Component<*, State> {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
api.on('audioMuteStatusChanged', this._audioMutedListener);
|
|
||||||
api.on('videoMuteStatusChanged', this._videoMutedListener);
|
|
||||||
api.on('audioAvailabilityChanged', this._audioAvailabilityListener);
|
|
||||||
api.on('videoAvailabilityChanged', this._videoAvailabilityListener);
|
|
||||||
api.on('largeVideoChanged', this._largeVideoChangedListener);
|
api.on('largeVideoChanged', this._largeVideoChangedListener);
|
||||||
api.on('displayNameChange', this._displayNameChangedListener);
|
api.on('displayNameChange', this._displayNameChangedListener);
|
||||||
api.on('avatarChanged', this._avatarChangedListener);
|
api.on('avatarChanged', this._avatarChangedListener);
|
||||||
|
|
||||||
this._largeVideoChangedListener();
|
this._largeVideoChangedListener();
|
||||||
|
|
||||||
Promise.all([
|
|
||||||
api.isAudioMuted(),
|
|
||||||
api.isVideoMuted(),
|
|
||||||
api.isAudioAvailable(),
|
|
||||||
api.isVideoAvailable()
|
|
||||||
])
|
|
||||||
.then(([
|
|
||||||
audioMuted = false,
|
|
||||||
videoMuted = false,
|
|
||||||
audioAvailable = false,
|
|
||||||
videoAvailable = false
|
|
||||||
]) =>
|
|
||||||
this.setState({
|
|
||||||
audioMuted,
|
|
||||||
videoMuted,
|
|
||||||
audioAvailable,
|
|
||||||
videoAvailable
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.catch(console.error);
|
|
||||||
|
|
||||||
window.addEventListener('mousemove', this._mouseMove);
|
window.addEventListener('mousemove', this._mouseMove);
|
||||||
|
|
||||||
this._hideToolbarAfterTimeout();
|
this._hideToolbarAfterTimeout();
|
||||||
|
@ -306,14 +219,6 @@ export default class AlwaysOnTop extends Component<*, State> {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
api.removeListener('audioMuteStatusChanged',
|
|
||||||
this._audioMutedListener);
|
|
||||||
api.removeListener('videoMuteStatusChanged',
|
|
||||||
this._videoMutedListener);
|
|
||||||
api.removeListener('audioAvailabilityChanged',
|
|
||||||
this._audioAvailabilityListener);
|
|
||||||
api.removeListener('videoAvailabilityChanged',
|
|
||||||
this._videoAvailabilityListener);
|
|
||||||
api.removeListener('largeVideoChanged',
|
api.removeListener('largeVideoChanged',
|
||||||
this._largeVideoChangedListener);
|
this._largeVideoChangedListener);
|
||||||
api.removeListener('displayNameChange',
|
api.removeListener('displayNameChange',
|
||||||
|
@ -343,14 +248,10 @@ export default class AlwaysOnTop extends Component<*, State> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div id = 'alwaysOnTop'>
|
<div id = 'alwaysOnTop'>
|
||||||
<ToolboxAlwaysOnTop
|
<Toolbar
|
||||||
audioAvailable = { this.state.audioAvailable }
|
|
||||||
audioMuted = { this.state.audioMuted }
|
|
||||||
className = { this.state.visible ? 'fadeIn' : 'fadeOut' }
|
className = { this.state.visible ? 'fadeIn' : 'fadeOut' }
|
||||||
onMouseOut = { this._onMouseOut }
|
onMouseOut = { this._onMouseOut }
|
||||||
onMouseOver = { this._onMouseOver }
|
onMouseOver = { this._onMouseOver } />
|
||||||
videoAvailable = { this.state.videoAvailable }
|
|
||||||
videoMuted = { this.state.videoMuted } />
|
|
||||||
{
|
{
|
||||||
this._renderVideoNotAvailableScreen()
|
this._renderVideoNotAvailableScreen()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
// XXX: AlwaysOnTop imports the button directly in order to avoid bringing in
|
||||||
|
// other components that use lib-jitsi-meet, which always on top does not
|
||||||
|
// import.
|
||||||
|
import AbstractAudioMuteButton
|
||||||
|
from '../toolbox/components/buttons/AbstractAudioMuteButton';
|
||||||
|
import type { Props } from '../toolbox/components/buttons/AbstractButton';
|
||||||
|
|
||||||
|
const { api } = window.alwaysOnTop;
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether audio is available is not.
|
||||||
|
*/
|
||||||
|
audioAvailable: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether audio is muted or not.
|
||||||
|
*/
|
||||||
|
audioMuted: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stateless hangup button for the Always-on-Top windows.
|
||||||
|
*/
|
||||||
|
export default class AudioMuteButton
|
||||||
|
extends AbstractAudioMuteButton<Props, State> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a new {@code AudioMuteButton} instance.
|
||||||
|
*
|
||||||
|
* @param {Props} props - The React {@code Component} props to initialize
|
||||||
|
* the new {@code AudioMuteButton} instance with.
|
||||||
|
*/
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
audioAvailable: false,
|
||||||
|
audioMuted: true
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bind event handlers so they are only bound once per instance.
|
||||||
|
this._audioAvailabilityListener
|
||||||
|
= this._audioAvailabilityListener.bind(this);
|
||||||
|
this._audioMutedListener = this._audioMutedListener.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets mouse move listener and initial toolbar timeout.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
api.on('audioAvailabilityChanged', this._audioAvailabilityListener);
|
||||||
|
api.on('audioMuteStatusChanged', this._audioMutedListener);
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
api.isAudioAvailable(),
|
||||||
|
api.isAudioMuted()
|
||||||
|
])
|
||||||
|
.then(values => {
|
||||||
|
const [ audioAvailable, audioMuted ] = values;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
audioAvailable,
|
||||||
|
audioMuted
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all listeners.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
componentWillUnmount() {
|
||||||
|
api.removeListener('audioAvailabilityChanged',
|
||||||
|
this._audioAvailabilityListener);
|
||||||
|
api.removeListener('audioMuteStatusChanged',
|
||||||
|
this._audioMutedListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this button is disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return !this.state.audioAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if audio is currently muted ot nor.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isAudioMuted() {
|
||||||
|
return this.state.audioMuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the muted state.
|
||||||
|
*
|
||||||
|
* @param {boolean} audioMuted - Whether audio should be muted or not.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_setAudioMuted(audioMuted: boolean) { // eslint-disable-line no-unused-vars
|
||||||
|
this.state.audioAvailable && api.executeCommand('toggleAudio');
|
||||||
|
}
|
||||||
|
|
||||||
|
_audioAvailabilityListener: ({ available: boolean }) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles audio available api events.
|
||||||
|
*
|
||||||
|
* @param {{ available: boolean }} status - The new available status.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_audioAvailabilityListener({ available }) {
|
||||||
|
this.setState({ audioAvailable: available });
|
||||||
|
}
|
||||||
|
|
||||||
|
_audioMutedListener: ({ muted: boolean }) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles audio muted api events.
|
||||||
|
*
|
||||||
|
* @param {{ muted: boolean }} status - The new muted status.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_audioMutedListener({ muted }) {
|
||||||
|
this.setState({ audioMuted: muted });
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
// XXX: AlwaysOnTop imports the button directly in order to avoid bringing in
|
||||||
|
// other components that use lib-jitsi-meet, which always on top does not
|
||||||
|
// import.
|
||||||
|
import AbstractHangupButton
|
||||||
|
from '../toolbox/components/buttons/AbstractHangupButton';
|
||||||
|
import type { Props } from '../toolbox/components/buttons/AbstractButton';
|
||||||
|
|
||||||
|
const { api } = window.alwaysOnTop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stateless hangup button for the Always-on-Top windows.
|
||||||
|
*/
|
||||||
|
export default class HangupButton extends AbstractHangupButton<Props, *> {
|
||||||
|
/**
|
||||||
|
* Helper function to perform the actual hangup action.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_doHangup() {
|
||||||
|
api.executeCommand('hangup');
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this button is disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import AudioMuteButton from './AudioMuteButton';
|
||||||
|
import HangupButton from './HangupButton';
|
||||||
|
import VideoMuteButton from './VideoMuteButton';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the React {@code Component} props of {@link Toolbar}.
|
||||||
|
*/
|
||||||
|
type Props = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional CSS class names to add to the root of the toolbar.
|
||||||
|
*/
|
||||||
|
className: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback invoked when no longer moused over the toolbar.
|
||||||
|
*/
|
||||||
|
onMouseOut: Function,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback invoked when the mouse has moved over the toolbar.
|
||||||
|
*/
|
||||||
|
onMouseOver: Function
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the toolbar in the Always On Top window.
|
||||||
|
*
|
||||||
|
* @extends Component
|
||||||
|
*/
|
||||||
|
export default class Toolbar extends Component<Props> {
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
className = '',
|
||||||
|
onMouseOut,
|
||||||
|
onMouseOver
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className = { `always-on-top-toolbox ${className}` }
|
||||||
|
onMouseOut = { onMouseOut }
|
||||||
|
onMouseOver = { onMouseOver }>
|
||||||
|
<AudioMuteButton />
|
||||||
|
<HangupButton />
|
||||||
|
<VideoMuteButton />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,159 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
|
|
||||||
// FIXME: AlwaysOnTop imports the button directly in order to avoid bringing in
|
|
||||||
// other components that use lib-jitsi-meet, which always on top does not
|
|
||||||
// import.
|
|
||||||
import ToolbarButton from '../toolbox/components/ToolbarButton';
|
|
||||||
|
|
||||||
const { api } = window.alwaysOnTop;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the React {@code Component} props of {@link ToolboxAlwaysOnTop}.
|
|
||||||
*/
|
|
||||||
type Props = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not microphone access is available.
|
|
||||||
*/
|
|
||||||
audioAvailable: boolean,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the user is currently audio muted.
|
|
||||||
*/
|
|
||||||
audioMuted: boolean,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Additional CSS class names to add to the root of the toolbar.
|
|
||||||
*/
|
|
||||||
className: string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback invoked when no longer moused over the toolbar.
|
|
||||||
*/
|
|
||||||
onMouseOut: Function,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback invoked when the mouse has moved over the toolbar.
|
|
||||||
*/
|
|
||||||
onMouseOver: Function,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not camera access is available.
|
|
||||||
*/
|
|
||||||
videoAvailable: boolean,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the user is currently video muted.
|
|
||||||
*/
|
|
||||||
videoMuted: boolean
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the toolbar in the Always On Top window.
|
|
||||||
*
|
|
||||||
* @extends Component
|
|
||||||
*/
|
|
||||||
export default class ToolboxAlwaysOnTop extends Component<Props> {
|
|
||||||
/**
|
|
||||||
* Initializes a new {@code ToolboxAlwaysOnTop} instance.
|
|
||||||
*
|
|
||||||
* @param {Props} props - The read-only properties with which the new
|
|
||||||
* instance is to be initialized.
|
|
||||||
*/
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
// Bind event handlers so they are only bound once per instance.
|
|
||||||
this._onToolbarHangup = this._onToolbarHangup.bind(this);
|
|
||||||
this._onToolbarToggleAudio = this._onToolbarToggleAudio.bind(this);
|
|
||||||
this._onToolbarToggleVideo = this._onToolbarToggleVideo.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
audioAvailable,
|
|
||||||
audioMuted,
|
|
||||||
className = '',
|
|
||||||
onMouseOut,
|
|
||||||
onMouseOver,
|
|
||||||
videoAvailable,
|
|
||||||
videoMuted
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const videoMuteIcon = `${videoMuted || !videoAvailable
|
|
||||||
? 'icon-camera-disabled toggled' : 'icon-camera'} ${
|
|
||||||
videoAvailable ? '' : 'disabled'}`;
|
|
||||||
const audioMuteIcon = `${audioMuted || !audioAvailable
|
|
||||||
? 'icon-mic-disabled toggled' : 'icon-microphone'} ${
|
|
||||||
audioAvailable ? '' : 'disabled'}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className = { `always-on-top-toolbox ${className}` }
|
|
||||||
onMouseOut = { onMouseOut }
|
|
||||||
onMouseOver = { onMouseOver }>
|
|
||||||
<ToolbarButton
|
|
||||||
accessibilityLabel = 'Audio mute'
|
|
||||||
iconName = { audioMuteIcon }
|
|
||||||
onClick = { this._onToolbarToggleAudio } />
|
|
||||||
<ToolbarButton
|
|
||||||
accessibilityLabel = 'Hangup'
|
|
||||||
iconName = 'icon-hangup'
|
|
||||||
onClick = { this._onToolbarHangup } />
|
|
||||||
<ToolbarButton
|
|
||||||
accessibilityLabel = 'Video mute'
|
|
||||||
iconName = { videoMuteIcon }
|
|
||||||
onClick = { this._onToolbarToggleVideo } />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onToolbarHangup: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ends the conference call and closes the always on top window.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_onToolbarHangup() {
|
|
||||||
api.executeCommand('hangup');
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
_onToolbarToggleAudio: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles audio mute if audio is avaiable.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_onToolbarToggleAudio() {
|
|
||||||
if (this.props.audioAvailable) {
|
|
||||||
api.executeCommand('toggleAudio');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onToolbarToggleVideo: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles video mute if video is avaiable.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_onToolbarToggleVideo() {
|
|
||||||
if (this.props.videoAvailable) {
|
|
||||||
api.executeCommand('toggleVideo');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
// XXX: AlwaysOnTop imports the button directly in order to avoid bringing in
|
||||||
|
// other components that use lib-jitsi-meet, which always on top does not
|
||||||
|
// import.
|
||||||
|
import AbstractVideoMuteButton
|
||||||
|
from '../toolbox/components/buttons/AbstractVideoMuteButton';
|
||||||
|
import type { Props } from '../toolbox/components/buttons/AbstractButton';
|
||||||
|
|
||||||
|
const { api } = window.alwaysOnTop;
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether video is available is not.
|
||||||
|
*/
|
||||||
|
videoAvailable: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether video is muted or not.
|
||||||
|
*/
|
||||||
|
videoMuted: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stateless hangup button for the Always-on-Top windows.
|
||||||
|
*/
|
||||||
|
export default class VideoMuteButton
|
||||||
|
extends AbstractVideoMuteButton<Props, State> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a new {@code VideoMuteButton} instance.
|
||||||
|
*
|
||||||
|
* @param {Props} props - The React {@code Component} props to initialize
|
||||||
|
* the new {@code VideoMuteButton} instance with.
|
||||||
|
*/
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
videoAvailable: false,
|
||||||
|
videoMuted: true
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bind event handlers so they are only bound once per instance.
|
||||||
|
this._videoAvailabilityListener
|
||||||
|
= this._videoAvailabilityListener.bind(this);
|
||||||
|
this._videoMutedListener = this._videoMutedListener.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets mouse move listener and initial toolbar timeout.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
api.on('videoAvailabilityChanged', this._videoAvailabilityListener);
|
||||||
|
api.on('videoMuteStatusChanged', this._videoMutedListener);
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
api.isVideoAvailable(),
|
||||||
|
api.isVideoMuted()
|
||||||
|
])
|
||||||
|
.then(values => {
|
||||||
|
const [ videoAvailable, videoMuted ] = values;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
videoAvailable,
|
||||||
|
videoMuted
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all listeners.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
componentWillUnmount() {
|
||||||
|
api.removeListener('videoAvailabilityChanged',
|
||||||
|
this._videoAvailabilityListener);
|
||||||
|
api.removeListener('videoMuteStatusChanged',
|
||||||
|
this._videoMutedListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this button is disabled or not.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isDisabled() {
|
||||||
|
return !this.state.videoAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if video is currently muted ot nor.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_isVideoMuted() {
|
||||||
|
return this.state.videoMuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the muted state.
|
||||||
|
*
|
||||||
|
* @param {boolean} videoMuted - Whether video should be muted or not.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_setVideoMuted(videoMuted: boolean) { // eslint-disable-line no-unused-vars
|
||||||
|
this.state.videoAvailable && api.executeCommand('toggleVideo');
|
||||||
|
}
|
||||||
|
|
||||||
|
_videoAvailabilityListener: ({ available: boolean }) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles video available api events.
|
||||||
|
*
|
||||||
|
* @param {{ available: boolean }} status - The new available status.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_videoAvailabilityListener({ available }) {
|
||||||
|
this.setState({ videoAvailable: available });
|
||||||
|
}
|
||||||
|
|
||||||
|
_videoMutedListener: ({ muted: boolean }) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles video muted api events.
|
||||||
|
*
|
||||||
|
* @param {{ muted: boolean }} status - The new muted status.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_videoMutedListener({ muted }) {
|
||||||
|
this.setState({ videoMuted: muted });
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue