123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- // @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 (
- <div id = 'videoNotAvailableScreen'>
- {
- avatarURL
- ? <div id = 'avatarContainer'>
- <img
- id = 'avatar'
- src = { avatarURL } />
- </div>
- : null
- }
- <div
- className = 'displayname'
- id = 'displayname'>
- { displayName }
- </div>
- </div>
- );
- }
-
- /**
- * 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.
- *
- * @inheritdoc
- * @returns {void}
- */
- 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('displayNameChange', this._displayNameChangedListener);
- api.on('avatarChanged', this._avatarChangedListener);
-
- 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);
-
- this._hideToolbarAfterTimeout();
- }
-
- /**
- * Removes all listeners.
- *
- * @inheritdoc
- * @returns {void}
- */
- componentWillUnmount() {
- api.removeListener('audioMuteStatusChanged',
- this._audioMutedListener);
- api.removeListener('videoMuteStatusChanged',
- this._videoMutedListener);
- api.removeListener('audioAvailabilityChanged',
- this._audioAvailabilityListener);
- api.removeListener('videoAvailabilityChanged',
- this._videoAvailabilityListener);
- api.removeListener('largeVideoChanged',
- this._largeVideoChangedListener);
- api.removeListener('displayNameChange',
- this._displayNameChangedListener);
- api.removeListener('avatarChanged', this._avatarChangedListener);
- window.removeEventListener('mousemove', this._mouseMove);
- }
-
- /**
- * Sets a timeout to hide the toolbar when the toolbar is shown.
- *
- * @inheritdoc
- * @returns {void}
- */
- componentWillUpdate(nextProps: *, nextState: State) {
- if (!this.state.visible && nextState.visible) {
- this._hideToolbarAfterTimeout();
- }
- }
-
- /**
- * Implements React's {@link Component#render()}.
- *
- * @inheritdoc
- * @returns {ReactElement}
- */
- render() {
- const className
- = `toolbar_primary always-on-top ${
- this.state.visible ? 'fadeIn' : 'fadeOut'}`;
-
- return (
- <div id = 'alwaysOnTop'>
- <StatelessToolbar
- className = { className }
- onMouseOut = { this._onMouseOut }
- onMouseOver = { this._onMouseOver }>
- {
- Object.entries(TOOLBAR_BUTTONS).map(
- ([ key, button ]) => {
- // XXX The following silences a couple of flow
- // errors:
- if (button === null
- || typeof button !== 'object') {
- return null;
- }
-
- const { onClick } = button;
- let enabled = false;
- let toggled = false;
-
- switch (key) {
- case 'microphone':
- enabled = this.state.audioAvailable;
- toggled = enabled
- ? this.state.audioMuted : true;
- break;
- case 'camera':
- enabled = this.state.videoAvailable;
- toggled = enabled
- ? this.state.videoMuted : true;
- break;
- default: // hangup button
- toggled = false;
- enabled = true;
- }
-
- const updatedButton = {
- ...button,
- enabled,
- toggled
- };
-
- return (
- <StatelessToolbarButton
- button = { updatedButton }
- key = { key }
- onClick = { onClick } />
- );
- }
- )
- }
- </StatelessToolbar>
- {
- this._renderVideoNotAvailableScreen()
- }
- </div>
- );
- }
- }
|