jiti-meet/react/features/always-on-top/AlwaysOnTop.js

271 lines
7.0 KiB
JavaScript
Raw Normal View History

// @flow
2017-08-04 08:15:11 +00:00
import React, { Component } from 'react';
2019-07-03 15:39:39 +00:00
// We need to reference these files directly to avoid loading things that are not available
// in this environment (e.g. JitsiMeetJS or interfaceConfig)
import StatelessAvatar from '../base/avatar/components/web/StatelessAvatar';
import { getAvatarColor, getInitials } from '../base/avatar/functions';
2019-07-03 15:39:39 +00:00
import Toolbar from './Toolbar';
2017-08-04 08:15:11 +00:00
const { api } = window.alwaysOnTop;
2017-11-13 15:54:04 +00:00
/**
2018-05-03 17:36:29 +00:00
* The timeout in ms for hiding the toolbar.
*/
const TOOLBAR_TIMEOUT = 4000;
/**
2018-05-03 17:36:29 +00:00
* The type of the React {@code Component} state of {@link AlwaysOnTop}.
2017-11-13 15:54:04 +00:00
*/
type State = {
avatarURL: string,
displayName: string,
2019-07-03 15:39:39 +00:00
formattedDisplayName: string,
isVideoDisplayed: boolean,
userID: string,
visible: boolean
};
2017-11-13 15:54:04 +00:00
2017-08-04 08:15:11 +00:00
/**
* Represents the always on top page.
*
* @class AlwaysOnTop
* @extends Component
*/
export default class AlwaysOnTop extends Component<*, State> {
_hovered: boolean;
2017-08-04 08:15:11 +00:00
/**
2018-05-11 02:10:26 +00:00
* Initializes a new {@code AlwaysOnTop} instance.
2017-08-04 08:15:11 +00:00
*
* @param {*} props - The read-only properties with which the new instance
* is to be initialized.
2017-08-04 08:15:11 +00:00
*/
constructor(props: *) {
2017-08-04 08:15:11 +00:00
super(props);
this.state = {
2018-05-03 17:36:29 +00:00
avatarURL: '',
displayName: '',
2019-07-03 15:39:39 +00:00
formattedDisplayName: '',
isVideoDisplayed: true,
userID: '',
2018-05-03 17:36:29 +00:00
visible: true
2017-08-04 08:15:11 +00:00
};
// Bind event handlers so they are only bound once per instance.
this._avatarChangedListener = this._avatarChangedListener.bind(this);
this._displayNameChangedListener
= this._displayNameChangedListener.bind(this);
2018-05-03 17:36:29 +00:00
this._largeVideoChangedListener
= this._largeVideoChangedListener.bind(this);
2017-08-04 08:15:11 +00:00
this._mouseMove = this._mouseMove.bind(this);
this._onMouseOut = this._onMouseOut.bind(this);
this._onMouseOver = this._onMouseOver.bind(this);
2017-08-04 08:15:11 +00:00
}
_avatarChangedListener: () => void;
/**
* Handles avatar changed api events.
*
* @returns {void}
*/
_avatarChangedListener({ avatarURL, id }) {
2018-05-03 17:36:29 +00:00
if (api._getOnStageParticipant() === id
&& avatarURL !== this.state.avatarURL) {
this.setState({ avatarURL });
}
}
_displayNameChangedListener: () => void;
/**
* Handles display name changed api events.
*
* @returns {void}
*/
2019-07-03 15:39:39 +00:00
_displayNameChangedListener({ displayname, formattedDisplayName, id }) {
2018-05-03 17:36:29 +00:00
if (api._getOnStageParticipant() === id
2019-07-03 15:39:39 +00:00
&& (formattedDisplayName !== this.state.formattedDisplayName
|| displayname !== this.state.displayName)) {
// I think the API has a typo using lowercase n for the displayname
this.setState({
displayName: displayname,
formattedDisplayName
});
}
}
2017-08-04 08:15:11 +00:00
/**
* Hides the toolbar after a timeout.
*
* @returns {void}
*/
_hideToolbarAfterTimeout() {
2018-05-03 17:36:29 +00:00
setTimeout(
() => {
if (this._hovered) {
this._hideToolbarAfterTimeout();
} else {
this.setState({ visible: false });
}
},
TOOLBAR_TIMEOUT);
2017-08-04 08:15:11 +00:00
}
_largeVideoChangedListener: () => void;
/**
* Handles large video changed api events.
*
* @returns {void}
*/
_largeVideoChangedListener() {
const userID = api._getOnStageParticipant();
const avatarURL = api.getAvatarURL(userID);
2019-07-03 15:39:39 +00:00
const displayName = api.getDisplayName(userID);
const formattedDisplayName = api._getFormattedDisplayName(userID);
const isVideoDisplayed = Boolean(api._getLargeVideo());
this.setState({
avatarURL,
displayName,
2019-07-03 15:39:39 +00:00
formattedDisplayName,
isVideoDisplayed,
userID
});
}
_mouseMove: () => void;
2017-08-04 08:15:11 +00:00
/**
* Handles mouse move events.
*
* @returns {void}
*/
_mouseMove() {
2018-05-03 17:36:29 +00:00
this.state.visible || this.setState({ visible: true });
2017-08-04 08:15:11 +00:00
}
_onMouseOut: () => void;
2017-08-04 08:15:11 +00:00
/**
* Toolbar mouse out handler.
2017-08-04 08:15:11 +00:00
*
* @returns {void}
*/
_onMouseOut() {
this._hovered = false;
2017-08-04 08:15:11 +00:00
}
_onMouseOver: () => void;
2017-08-04 08:15:11 +00:00
/**
* Toolbar mouse over handler.
2017-08-04 08:15:11 +00:00
*
* @returns {void}
*/
_onMouseOver() {
this._hovered = true;
2017-08-04 08:15:11 +00:00
}
/**
* Renders display name and avatar for the on stage participant.
*
* @returns {ReactElement}
*/
_renderVideoNotAvailableScreen() {
const { avatarURL, displayName, formattedDisplayName, isVideoDisplayed, userID } = this.state;
if (isVideoDisplayed) {
return null;
}
return (
<div id = 'videoNotAvailableScreen'>
2019-07-03 15:39:39 +00:00
<div id = 'avatarContainer'>
<StatelessAvatar
color = { getAvatarColor(userID) }
2019-07-03 15:39:39 +00:00
id = 'avatar'
initials = { getInitials(displayName) }
url = { avatarURL } />)
</div>
<div
className = 'displayname'
id = 'displayname'>
2019-07-03 15:39:39 +00:00
{ formattedDisplayName }
</div>
</div>
);
}
2017-08-04 08:15:11 +00:00
/**
* Sets mouse move listener and initial toolbar timeout.
*
* @inheritdoc
* @returns {void}
*/
componentDidMount() {
api.on('avatarChanged', this._avatarChangedListener);
2018-05-03 17:36:29 +00:00
api.on('displayNameChange', this._displayNameChangedListener);
api.on('largeVideoChanged', this._largeVideoChangedListener);
this._largeVideoChangedListener();
2017-08-04 08:15:11 +00:00
window.addEventListener('mousemove', this._mouseMove);
this._hideToolbarAfterTimeout();
}
/**
* Sets a timeout to hide the toolbar when the toolbar is shown.
*
* @inheritdoc
* @returns {void}
*/
componentDidUpdate(prevProps: *, prevState: State) {
if (!prevState.visible && this.state.visible) {
this._hideToolbarAfterTimeout();
}
}
2017-08-04 08:15:11 +00:00
/**
* Removes all listeners.
*
* @inheritdoc
* @returns {void}
*/
componentWillUnmount() {
api.removeListener('avatarChanged', this._avatarChangedListener);
2018-05-03 17:36:29 +00:00
api.removeListener(
'displayNameChange',
this._displayNameChangedListener);
api.removeListener(
'largeVideoChanged',
this._largeVideoChangedListener);
2017-08-04 08:15:11 +00:00
window.removeEventListener('mousemove', this._mouseMove);
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
return (
<div id = 'alwaysOnTop'>
<Toolbar
className = { this.state.visible ? 'fadeIn' : 'fadeOut' }
onMouseOut = { this._onMouseOut }
onMouseOver = { this._onMouseOver } />
2018-05-03 17:36:29 +00:00
{ this._renderVideoNotAvailableScreen() }
</div>
2017-08-04 08:15:11 +00:00
);
}
}