// @flow import React, { Component } from 'react'; import StatelessToolbar from '../toolbox/components/StatelessToolbar'; import StatelessToolbarButton from '../toolbox/components/StatelessToolbarButton'; const { api } = window.alwaysOnTop; /** * Map with toolbar button descriptors. */ const TOOLBAR_BUTTONS = { /** * The descriptor of the camera toolbar button. */ camera: { classNames: [ 'button', 'icon-camera' ], enabled: true, id: 'toolbar_button_camera', onClick() { api.executeCommand('toggleVideo'); } }, /** * The descriptor of the toolbar button which hangs up the call/conference. */ hangup: { classNames: [ 'button', 'icon-hangup', 'button_hangup' ], enabled: true, id: 'toolbar_button_hangup', onClick() { api.executeCommand('hangup'); window.close(); } }, /** * The descriptor of the microphone toolbar button. */ microphone: { classNames: [ 'button', 'icon-microphone' ], enabled: true, id: 'toolbar_button_mute', onClick() { api.executeCommand('toggleAudio'); } } }; /** * The timeout in ms for hidding the toolbar. */ const TOOLBAR_TIMEOUT = 4000; /** * The type of the React {@code Component} state of {@link FeedbackButton}. */ type State = { audioAvailable: boolean, audioMuted: boolean, avatarURL: string, displayName: string, isVideoDisplayed: boolean, videoAvailable: boolean, videoMuted: boolean, visible: boolean }; /** * Represents the always on top page. * * @class AlwaysOnTop * @extends Component */ export default class AlwaysOnTop extends Component<*, State> { _hovered: boolean; /** * Initializes new AlwaysOnTop instance. * * @param {*} props - The read-only properties with which the new instance * is to be initialized. */ constructor(props: *) { super(props); this.state = { visible: true, audioMuted: false, videoMuted: false, audioAvailable: false, videoAvailable: false, displayName: '', isVideoDisplayed: true, avatarURL: '' }; // 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._largeVideoChangedListener = this._largeVideoChangedListener.bind(this); this._displayNameChangedListener = this._displayNameChangedListener.bind(this); this._mouseMove = this._mouseMove.bind(this); this._onMouseOut = this._onMouseOut.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; /** * Handles avatar changed api events. * * @returns {void} */ _avatarChangedListener({ avatarURL, id }) { if (api._getOnStageParticipant() !== id) { return; } if (avatarURL !== this.state.avatarURL) { this.setState({ avatarURL }); } } _displayNameChangedListener: () => void; /** * Handles display name changed api events. * * @returns {void} */ _displayNameChangedListener({ formattedDisplayName, id }) { if (api._getOnStageParticipant() !== id) { return; } if (formattedDisplayName !== this.state.displayName) { this.setState({ displayName: formattedDisplayName }); } } /** * Hides the toolbar after a timeout. * * @returns {void} */ _hideToolbarAfterTimeout() { setTimeout(() => { if (this._hovered) { this._hideToolbarAfterTimeout(); return; } this.setState({ visible: false }); }, TOOLBAR_TIMEOUT); } _largeVideoChangedListener: () => void; /** * Handles large video changed api events. * * @returns {void} */ _largeVideoChangedListener() { const userID = api._getOnStageParticipant(); const displayName = api._getFormattedDisplayName(userID); const avatarURL = api.getAvatarURL(userID); const isVideoDisplayed = Boolean(api._getLargeVideo()); this.setState({ avatarURL, displayName, isVideoDisplayed }); } _mouseMove: () => void; /** * Handles mouse move events. * * @returns {void} */ _mouseMove() { if (!this.state.visible) { this.setState({ visible: true }); } } _onMouseOut: () => void; /** * Toolbar mouse out handler. * * @returns {void} */ _onMouseOut() { this._hovered = false; } _onMouseOver: () => void; /** * Toolbar mouse over handler. * * @returns {void} */ _onMouseOver() { this._hovered = true; } _videoAvailabilityListener: ({ available: boolean }) => void; /** * Renders display name and avatar for the on stage participant. * * @returns {ReactElement} */ _renderVideoNotAvailableScreen() { const { avatarURL, displayName, isVideoDisplayed } = this.state; if (isVideoDisplayed) { return null; } return (