Coding style

This commit is contained in:
Lyubo Marinov 2018-05-03 12:36:29 -05:00
parent 9ca7ca9515
commit a42496ba53
10 changed files with 138 additions and 138 deletions

View File

@ -7,12 +7,12 @@ import Toolbar from './Toolbar';
const { api } = window.alwaysOnTop; const { api } = window.alwaysOnTop;
/** /**
* The timeout in ms for hidding the toolbar. * The timeout in ms for hiding the toolbar.
*/ */
const TOOLBAR_TIMEOUT = 4000; 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 AlwaysOnTop}.
*/ */
type State = { type State = {
avatarURL: string, avatarURL: string,
@ -40,18 +40,18 @@ export default class AlwaysOnTop extends Component<*, State> {
super(props); super(props);
this.state = { this.state = {
visible: true, avatarURL: '',
displayName: '', displayName: '',
isVideoDisplayed: true, isVideoDisplayed: true,
avatarURL: '' visible: true
}; };
// Bind event handlers so they are only bound once per instance. // Bind event handlers so they are only bound once per instance.
this._avatarChangedListener = this._avatarChangedListener.bind(this); this._avatarChangedListener = this._avatarChangedListener.bind(this);
this._largeVideoChangedListener
= this._largeVideoChangedListener.bind(this);
this._displayNameChangedListener this._displayNameChangedListener
= this._displayNameChangedListener.bind(this); = this._displayNameChangedListener.bind(this);
this._largeVideoChangedListener
= this._largeVideoChangedListener.bind(this);
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);
@ -65,11 +65,8 @@ export default class AlwaysOnTop extends Component<*, State> {
* @returns {void} * @returns {void}
*/ */
_avatarChangedListener({ avatarURL, id }) { _avatarChangedListener({ avatarURL, id }) {
if (api._getOnStageParticipant() !== id) { if (api._getOnStageParticipant() === id
return; && avatarURL !== this.state.avatarURL) {
}
if (avatarURL !== this.state.avatarURL) {
this.setState({ avatarURL }); this.setState({ avatarURL });
} }
} }
@ -82,11 +79,8 @@ export default class AlwaysOnTop extends Component<*, State> {
* @returns {void} * @returns {void}
*/ */
_displayNameChangedListener({ formattedDisplayName, id }) { _displayNameChangedListener({ formattedDisplayName, id }) {
if (api._getOnStageParticipant() !== id) { if (api._getOnStageParticipant() === id
return; && formattedDisplayName !== this.state.displayName) {
}
if (formattedDisplayName !== this.state.displayName) {
this.setState({ displayName: formattedDisplayName }); this.setState({ displayName: formattedDisplayName });
} }
} }
@ -97,14 +91,15 @@ export default class AlwaysOnTop extends Component<*, State> {
* @returns {void} * @returns {void}
*/ */
_hideToolbarAfterTimeout() { _hideToolbarAfterTimeout() {
setTimeout(() => { setTimeout(
if (this._hovered) { () => {
this._hideToolbarAfterTimeout(); if (this._hovered) {
this._hideToolbarAfterTimeout();
return; } else {
} this.setState({ visible: false });
this.setState({ visible: false }); }
}, TOOLBAR_TIMEOUT); },
TOOLBAR_TIMEOUT);
} }
_largeVideoChangedListener: () => void; _largeVideoChangedListener: () => void;
@ -116,8 +111,8 @@ export default class AlwaysOnTop extends Component<*, State> {
*/ */
_largeVideoChangedListener() { _largeVideoChangedListener() {
const userID = api._getOnStageParticipant(); const userID = api._getOnStageParticipant();
const displayName = api._getFormattedDisplayName(userID);
const avatarURL = api.getAvatarURL(userID); const avatarURL = api.getAvatarURL(userID);
const displayName = api._getFormattedDisplayName(userID);
const isVideoDisplayed = Boolean(api._getLargeVideo()); const isVideoDisplayed = Boolean(api._getLargeVideo());
this.setState({ this.setState({
@ -135,9 +130,7 @@ export default class AlwaysOnTop extends Component<*, State> {
* @returns {void} * @returns {void}
*/ */
_mouseMove() { _mouseMove() {
if (!this.state.visible) { this.state.visible || this.setState({ visible: true });
this.setState({ visible: true });
}
} }
_onMouseOut: () => void; _onMouseOut: () => void;
@ -201,9 +194,9 @@ export default class AlwaysOnTop extends Component<*, State> {
* @returns {void} * @returns {void}
*/ */
componentDidMount() { componentDidMount() {
api.on('largeVideoChanged', this._largeVideoChangedListener);
api.on('displayNameChange', this._displayNameChangedListener);
api.on('avatarChanged', this._avatarChangedListener); api.on('avatarChanged', this._avatarChangedListener);
api.on('displayNameChange', this._displayNameChangedListener);
api.on('largeVideoChanged', this._largeVideoChangedListener);
this._largeVideoChangedListener(); this._largeVideoChangedListener();
@ -219,11 +212,14 @@ export default class AlwaysOnTop extends Component<*, State> {
* @returns {void} * @returns {void}
*/ */
componentWillUnmount() { componentWillUnmount() {
api.removeListener('largeVideoChanged',
this._largeVideoChangedListener);
api.removeListener('displayNameChange',
this._displayNameChangedListener);
api.removeListener('avatarChanged', this._avatarChangedListener); api.removeListener('avatarChanged', this._avatarChangedListener);
api.removeListener(
'displayNameChange',
this._displayNameChangedListener);
api.removeListener(
'largeVideoChanged',
this._largeVideoChangedListener);
window.removeEventListener('mousemove', this._mouseMove); window.removeEventListener('mousemove', this._mouseMove);
} }
@ -252,9 +248,7 @@ export default class AlwaysOnTop extends Component<*, State> {
className = { this.state.visible ? 'fadeIn' : 'fadeOut' } className = { this.state.visible ? 'fadeIn' : 'fadeOut' }
onMouseOut = { this._onMouseOut } onMouseOut = { this._onMouseOut }
onMouseOver = { this._onMouseOver } /> onMouseOver = { this._onMouseOver } />
{ { this._renderVideoNotAvailableScreen() }
this._renderVideoNotAvailableScreen()
}
</div> </div>
); );
} }

View File

@ -1,14 +1,16 @@
// @flow // @flow
// XXX: AlwaysOnTop imports the button directly in order to avoid bringing in // XXX Import the button directly in order to avoid bringing in other components
// other components that use lib-jitsi-meet, which always on top does not // that use lib-jitsi-meet, which always-on-top does not import.
// import.
import AbstractAudioMuteButton import AbstractAudioMuteButton
from '../toolbox/components/buttons/AbstractAudioMuteButton'; from '../toolbox/components/buttons/AbstractAudioMuteButton';
import type { Props } from '../toolbox/components/buttons/AbstractButton'; import type { Props } from '../toolbox/components/buttons/AbstractButton';
const { api } = window.alwaysOnTop; const { api } = window.alwaysOnTop;
/**
* The type of the React {@code Component} state of {@link AudioMuteButton}.
*/
type State = { type State = {
/** /**
@ -23,7 +25,7 @@ type State = {
}; };
/** /**
* Stateless hangup button for the Always-on-Top windows. * Stateless "mute/unmute audio" button for the Always-on-Top windows.
*/ */
export default class AudioMuteButton export default class AudioMuteButton
extends AbstractAudioMuteButton<Props, State> { extends AbstractAudioMuteButton<Props, State> {
@ -62,14 +64,11 @@ export default class AudioMuteButton
api.isAudioAvailable(), api.isAudioAvailable(),
api.isAudioMuted() api.isAudioMuted()
]) ])
.then(values => { .then(([ audioAvailable, audioMuted ]) =>
const [ audioAvailable, audioMuted ] = values;
this.setState({ this.setState({
audioAvailable, audioAvailable,
audioMuted audioMuted
}); }))
})
.catch(console.error); .catch(console.error);
} }
@ -80,45 +79,14 @@ export default class AudioMuteButton
* @returns {void} * @returns {void}
*/ */
componentWillUnmount() { componentWillUnmount() {
api.removeListener('audioAvailabilityChanged', api.removeListener(
'audioAvailabilityChanged',
this._audioAvailabilityListener); this._audioAvailabilityListener);
api.removeListener('audioMuteStatusChanged', api.removeListener(
'audioMuteStatusChanged',
this._audioMutedListener); 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; _audioAvailabilityListener: ({ available: boolean }) => void;
/** /**
@ -142,4 +110,37 @@ export default class AudioMuteButton
_audioMutedListener({ muted }) { _audioMutedListener({ muted }) {
this.setState({ audioMuted: muted }); this.setState({ audioMuted: muted });
} }
/**
* Indicates if audio is currently muted ot nor.
*
* @override
* @private
* @returns {boolean}
*/
_isAudioMuted() {
return this.state.audioMuted;
}
/**
* Indicates whether this button is disabled or not.
*
* @override
* @private
* @returns {boolean}
*/
_isDisabled() {
return !this.state.audioAvailable;
}
/**
* 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');
}
} }

View File

@ -1,8 +1,7 @@
// @flow // @flow
// XXX: AlwaysOnTop imports the button directly in order to avoid bringing in // XXX Import the button directly in order to avoid bringing in other components
// other components that use lib-jitsi-meet, which always on top does not // that use lib-jitsi-meet, which always-on-top does not import.
// import.
import AbstractHangupButton import AbstractHangupButton
from '../toolbox/components/buttons/AbstractHangupButton'; from '../toolbox/components/buttons/AbstractHangupButton';
import type { Props } from '../toolbox/components/buttons/AbstractButton'; import type { Props } from '../toolbox/components/buttons/AbstractButton';

View File

@ -24,7 +24,7 @@ type Props = {
/** /**
* Callback invoked when the mouse has moved over the toolbar. * Callback invoked when the mouse has moved over the toolbar.
*/ */
onMouseOver: Function onMouseOver: Function
}; };
/** /**

View File

@ -1,14 +1,16 @@
// @flow // @flow
// XXX: AlwaysOnTop imports the button directly in order to avoid bringing in // XXX Import the button directly in order to avoid bringing in other components
// other components that use lib-jitsi-meet, which always on top does not // that use lib-jitsi-meet, which always-on-top does not import.
// import.
import AbstractVideoMuteButton import AbstractVideoMuteButton
from '../toolbox/components/buttons/AbstractVideoMuteButton'; from '../toolbox/components/buttons/AbstractVideoMuteButton';
import type { Props } from '../toolbox/components/buttons/AbstractButton'; import type { Props } from '../toolbox/components/buttons/AbstractButton';
const { api } = window.alwaysOnTop; const { api } = window.alwaysOnTop;
/**
* The type of the React {@code Component} state of {@link VideoMuteButton}.
*/
type State = { type State = {
/** /**
@ -23,7 +25,7 @@ type State = {
}; };
/** /**
* Stateless hangup button for the Always-on-Top windows. * Stateless "mute/unmute video" button for the Always-on-Top windows.
*/ */
export default class VideoMuteButton export default class VideoMuteButton
extends AbstractVideoMuteButton<Props, State> { extends AbstractVideoMuteButton<Props, State> {
@ -62,14 +64,11 @@ export default class VideoMuteButton
api.isVideoAvailable(), api.isVideoAvailable(),
api.isVideoMuted() api.isVideoMuted()
]) ])
.then(values => { .then(([ videoAvailable, videoMuted ]) =>
const [ videoAvailable, videoMuted ] = values;
this.setState({ this.setState({
videoAvailable, videoAvailable,
videoMuted videoMuted
}); }))
})
.catch(console.error); .catch(console.error);
} }
@ -80,9 +79,11 @@ export default class VideoMuteButton
* @returns {void} * @returns {void}
*/ */
componentWillUnmount() { componentWillUnmount() {
api.removeListener('videoAvailabilityChanged', api.removeListener(
'videoAvailabilityChanged',
this._videoAvailabilityListener); this._videoAvailabilityListener);
api.removeListener('videoMuteStatusChanged', api.removeListener(
'videoMuteStatusChanged',
this._videoMutedListener); this._videoMutedListener);
} }

View File

@ -1,14 +1,14 @@
// @flow
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import AlwaysOnTop from './AlwaysOnTop'; import AlwaysOnTop from './AlwaysOnTop';
// Render the main/root Component. // Render the main/root Component.
ReactDOM.render( // $FlowExpectedError
<AlwaysOnTop />, ReactDOM.render(<AlwaysOnTop />, document.getElementById('react'));
document.getElementById('react')
);
window.addEventListener('beforeunload', () => { window.addEventListener(
ReactDOM.unmountComponentAtNode(document.getElementById('react')); 'beforeunload',
}); () => ReactDOM.unmountComponentAtNode(document.getElementById('react')));

View File

@ -139,10 +139,13 @@ class Conference extends Component<Props> {
render() { render() {
const { const {
VIDEO_QUALITY_LABEL_DISABLED, VIDEO_QUALITY_LABEL_DISABLED,
filmStripOnly
// XXX The character casing of the name filmStripOnly utilized by
// interfaceConfig is obsolete but legacy support is required.
filmStripOnly: filmstripOnly
} = interfaceConfig; } = interfaceConfig;
const hideVideoQualityLabel const hideVideoQualityLabel
= filmStripOnly = filmstripOnly
|| VIDEO_QUALITY_LABEL_DISABLED || VIDEO_QUALITY_LABEL_DISABLED
|| this.props._iAmRecorder; || this.props._iAmRecorder;
@ -153,11 +156,11 @@ class Conference extends Component<Props> {
<div id = 'videospace'> <div id = 'videospace'>
<LargeVideo <LargeVideo
hideVideoQualityLabel = { hideVideoQualityLabel } /> hideVideoQualityLabel = { hideVideoQualityLabel } />
<Filmstrip filmstripOnly = { filmStripOnly } /> <Filmstrip filmstripOnly = { filmstripOnly } />
</div> </div>
{ !filmStripOnly && <Toolbox /> } { filmstripOnly || <Toolbox /> }
{ !filmStripOnly && <SidePanel /> } { filmstripOnly || <SidePanel /> }
<DialogContainer /> <DialogContainer />
<NotificationsContainer /> <NotificationsContainer />

View File

@ -9,7 +9,6 @@ import { dockToolbox } from '../../toolbox';
import { setFilmstripHovered } from '../actions'; import { setFilmstripHovered } from '../actions';
import { shouldRemoteVideosBeVisible } from '../functions'; import { shouldRemoteVideosBeVisible } from '../functions';
import Toolbar from './Toolbar'; import Toolbar from './Toolbar';
declare var interfaceConfig: Object; declare var interfaceConfig: Object;
@ -36,9 +35,9 @@ class Filmstrip extends Component<*> {
*/ */
static propTypes = { static propTypes = {
/** /**
* Whether or not the conference is in filmstripOnly mode. * Whether the UI/UX is filmstrip-only.
*/ */
_filmStripOnly: PropTypes.bool, _filmstripOnly: PropTypes.bool,
/** /**
* Whether or not remote videos are currently being hovered over. * Whether or not remote videos are currently being hovered over.
@ -58,7 +57,7 @@ class Filmstrip extends Component<*> {
_toolboxVisible: PropTypes.bool, _toolboxVisible: PropTypes.bool,
/** /**
* Updates the redux store with filmstrip hover changes. * The redux {@code dispatch} function.
*/ */
dispatch: PropTypes.func dispatch: PropTypes.func
}; };
@ -85,8 +84,8 @@ class Filmstrip extends Component<*> {
this._isHovered = false; this._isHovered = false;
// Bind event handlers so they are only bound once for every instance. // Bind event handlers so they are only bound once for every instance.
this._onMouseOver = this._onMouseOver.bind(this);
this._onMouseOut = this._onMouseOut.bind(this); this._onMouseOut = this._onMouseOut.bind(this);
this._onMouseOver = this._onMouseOver.bind(this);
} }
/** /**
@ -97,27 +96,27 @@ class Filmstrip extends Component<*> {
*/ */
render() { render() {
const { const {
_filmStripOnly, _filmstripOnly,
_remoteVideosVisible, _remoteVideosVisible,
_toolboxVisible _toolboxVisible
} = this.props; } = this.props;
/** // Note: Appending of {@code RemoteVideo} views is handled through
* Note: Appending of {@code RemoteVideo} views is handled through // VideoLayout. The views do not get blown away on render() because
* VideoLayout. The views do not get blown away on render() because // ReactDOMComponent is only aware of the given JSX and not new appended
* ReactDOMComponent is only aware of the given JSX and not new appended // DOM. As such, when updateDOMProperties gets called, only attributes
* DOM. As such, when updateDOMProperties gets called, only attributes // will get updated without replacing the DOM. If the known DOM gets
* will get updated without replacing the DOM. If the known DOM gets // modified, then the views will get blown away.
* modified, then the views will get blown away.
*/
const reduceHeight const reduceHeight
= _toolboxVisible && interfaceConfig.TOOLBAR_BUTTONS.length; = _toolboxVisible && interfaceConfig.TOOLBAR_BUTTONS.length;
const filmstripClassNames = `filmstrip ${_remoteVideosVisible const classNames
? '' : 'hide-videos'} ${reduceHeight ? 'reduce-height' : ''}`; = `filmstrip ${
_remoteVideosVisible ? '' : 'hide-videos'} ${
reduceHeight ? 'reduce-height' : ''}`;
return ( return (
<div className = { filmstripClassNames }> <div className = { classNames }>
{ _filmStripOnly && <Toolbar /> } { _filmstripOnly && <Toolbar /> }
<div <div
className = 'filmstrip__videos' className = 'filmstrip__videos'
id = 'remoteVideos'> id = 'remoteVideos'>
@ -131,9 +130,9 @@ class Filmstrip extends Component<*> {
<div <div
className = 'filmstrip__videos' className = 'filmstrip__videos'
id = 'filmstripRemoteVideos'> id = 'filmstripRemoteVideos'>
{/** {/*
* This extra video container is needed for scrolling * XXX This extra video container is needed for
* thumbnails in Firefox; otherwise, the flex * scrolling thumbnails in Firefox; otherwise, the flex
* thumbnails resize instead of causing overflow. * thumbnails resize instead of causing overflow.
*/} */}
<div <div
@ -192,7 +191,7 @@ class Filmstrip extends Component<*> {
* @param {Object} state - The Redux state. * @param {Object} state - The Redux state.
* @private * @private
* @returns {{ * @returns {{
* _filmStripOnly: boolean, * _filmstripOnly: boolean,
* _hovered: boolean, * _hovered: boolean,
* _remoteVideosVisible: boolean, * _remoteVideosVisible: boolean,
* _toolboxVisible: boolean * _toolboxVisible: boolean
@ -202,7 +201,7 @@ function _mapStateToProps(state) {
const { hovered } = state['features/filmstrip']; const { hovered } = state['features/filmstrip'];
return { return {
_filmStripOnly: Boolean(interfaceConfig.filmStripOnly), _filmstripOnly: Boolean(interfaceConfig.filmStripOnly),
_hovered: hovered, _hovered: hovered,
_remoteVideosVisible: shouldRemoteVideosBeVisible(state), _remoteVideosVisible: shouldRemoteVideosBeVisible(state),
_toolboxVisible: state['features/toolbox'].visible _toolboxVisible: state['features/toolbox'].visible

View File

@ -12,16 +12,19 @@ import {
declare var interfaceConfig: Object; declare var interfaceConfig: Object;
/**
* The type of the React {@code Component} props of {@link Toolbar}.
*/
type Props = { type Props = {
/** /**
* Set of buttons which should be visible in this toolbar. * The set of buttons which should be visible in this {@code Toolbar}.
*/ */
_visibleButtons: Set<string> _visibleButtons: Set<string>
}; };
/** /**
* Implements the conference toolbar on React/Web for filmstrip only mode. * Implements the conference toolbar on React/Web for filmstrip-only mode.
* *
* @extends Component * @extends Component
*/ */

View File

@ -17,7 +17,7 @@ type Props = AbstractButtonProps & {
/** /**
* Whether we are in filmstrip only mode or not. * Whether we are in filmstrip only mode or not.
*/ */
_filmStripOnly: boolean, _filmstripOnly: boolean,
/** /**
* Array containing the enabled settings sections. * Array containing the enabled settings sections.
@ -46,10 +46,10 @@ class SettingsButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { _filmStripOnly, _sections, dispatch } = this.props; const { _filmstripOnly, _sections, dispatch } = this.props;
sendAnalytics(createToolbarEvent('settings')); sendAnalytics(createToolbarEvent('settings'));
if (_filmStripOnly if (_filmstripOnly
|| (_sections.length === 1 && _sections.includes('devices'))) { || (_sections.length === 1 && _sections.includes('devices'))) {
dispatch(openDeviceSelectionDialog()); dispatch(openDeviceSelectionDialog());
} else { } else {
@ -76,7 +76,7 @@ class SettingsButton extends AbstractButton<Props, *> {
* @param {Object} state - The Redux state. * @param {Object} state - The Redux state.
* @private * @private
* @returns {{ * @returns {{
* _filmStripOnly: boolean * _filmstripOnly: boolean
* }} * }}
*/ */
function _mapStateToProps(state): Object { // eslint-disable-line no-unused-vars function _mapStateToProps(state): Object { // eslint-disable-line no-unused-vars
@ -84,7 +84,7 @@ function _mapStateToProps(state): Object { // eslint-disable-line no-unused-vars
// interfaceConfig is part of redux we will. // interfaceConfig is part of redux we will.
return { return {
_filmStripOnly: Boolean(interfaceConfig.filmStripOnly), _filmstripOnly: Boolean(interfaceConfig.filmStripOnly),
_sections: interfaceConfig.SETTINGS_SECTIONS || [] _sections: interfaceConfig.SETTINGS_SECTIONS || []
}; };
} }