| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 | /* @flow */
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Audio } from '../../media';
import { Avatar } from '../../participants';
import { Container, Text } from '../../react';
import UIEvents from '../../../../../service/UI/UIEvents';
import styles from './styles';
declare var $: Object;
declare var APP: Object;
declare var interfaceConfig: Object;
/**
 * Implements a React {@link Component} which depicts the establishment of a
 * call with a specific remote callee.
 *
 * @extends Component
 */
class CallOverlay extends Component<*, *> {
    /**
     * The (reference to the) {@link Audio} which plays/renders the audio
     * depicting the ringing phase of the call establishment represented by this
     * {@code CallOverlay}.
     */
    _audio: ?Audio
    _onLargeVideoAvatarVisible: Function
    _playAudioInterval: ?number
    _ringingTimeout: ?number
    _setAudio: Function
    state: {
        /**
         * The CSS class (name), if any, to add to this {@code CallOverlay}.
         *
         * @type {string}
         */
        className: ?string,
        /**
         * The indicator which determines whether this {@code CallOverlay}
         * should play/render audio to indicate the ringing phase of the
         * call establishment between the local participant and the
         * associated remote callee.
         *
         * @type {boolean}
         */
        renderAudio: boolean,
        /**
         * The indicator which determines whether this {@code CallOverlay}
         * is depicting the ringing phase of the call establishment between
         * the local participant and the associated remote callee or the
         * phase afterwards when the callee has not answered the call for a
         * period of time and, consequently, is considered unavailable.
         *
         * @type {boolean}
         */
        ringing: boolean
    }
    /**
     * {@code CallOverlay} component's property types.
     *
     * @static
     */
    static propTypes = {
        _callee: PropTypes.object
    };
    /**
     * Initializes a new {@code CallOverlay} instance.
     *
     * @param {Object} props - The read-only React {@link Component} props with
     * which the new instance is to be initialized.
     */
    constructor(props) {
        super(props);
        this.state = {
            className: undefined,
            renderAudio:
                typeof interfaceConfig !== 'object'
                    || !interfaceConfig.DISABLE_RINGING,
            ringing: true
        };
        this._onLargeVideoAvatarVisible
            = this._onLargeVideoAvatarVisible.bind(this);
        this._setAudio = this._setAudio.bind(this);
        if (typeof APP === 'object') {
            APP.UI.addListener(
                UIEvents.LARGE_VIDEO_AVATAR_VISIBLE,
                this._onLargeVideoAvatarVisible);
        }
    }
    /**
     * Sets up timeouts such as the timeout to end the ringing phase of the call
     * establishment depicted by this {@code CallOverlay}.
     *
     * @inheritdoc
     */
    componentDidMount() {
        // Set up the timeout to end the ringing phase of the call establishment
        // depicted by this CallOverlay.
        if (this.state.ringing && !this._ringingTimeout) {
            this._ringingTimeout
                = setTimeout(
                    () => {
                        this._pauseAudio();
                        this._ringingTimeout = undefined;
                        this.setState({
                            ringing: false
                        });
                    },
                    30000);
        }
        this._playAudio();
    }
    /**
     * Cleans up before this {@code Calleverlay} is unmounted and destroyed.
     *
     * @inheritdoc
     */
    componentWillUnmount() {
        this._pauseAudio();
        if (this._ringingTimeout) {
            clearTimeout(this._ringingTimeout);
            this._ringingTimeout = undefined;
        }
        if (typeof APP === 'object') {
            APP.UI.removeListener(
                UIEvents.LARGE_VIDEO_AVATAR_VISIBLE,
                this._onLargeVideoAvatarVisible);
        }
    }
    /**
     * Implements React's {@link Component#render()}.
     *
     * @inheritdoc
     * @returns {ReactElement}
     */
    render() {
        const { className, ringing } = this.state;
        const { avatarUrl, avatar, name } = this.props._callee;
        return (
            <Container
                { ...this._style('ringing', className) }
                id = 'ringOverlay'>
                <Container
                    { ...this._style('ringing__content') }>
                    <Text { ...this._style('ringing__text') }>
                        { ringing ? 'Calling...' : '' }
                    </Text>
                    <Avatar
                        { ...this._style('ringing__avatar') }
                        uri = { avatarUrl || avatar } />
                    <Container
                        { ...this._style('ringing__caller-info') }>
                        <Text
                            { ...this._style('ringing__text') }>
                            { name }
                            { ringing ? '' : ' isn\'t available' }
                        </Text>
                    </Container>
                </Container>
                { this._renderAudio() }
            </Container>
        );
    }
    /**
     * Notifies this {@code CallOverlay} that the visibility of the
     * participant's avatar in the large video has changed.
     *
     * @param {boolean} largeVideoAvatarVisible - If the avatar in the large
     * video (i.e. of the participant on the stage) is visible, then
     * {@code true}; otherwise, {@code false}.
     * @private
     * @returns {void}
     */
    _onLargeVideoAvatarVisible(largeVideoAvatarVisible: boolean) {
        this.setState({
            className: largeVideoAvatarVisible ? 'solidBG' : undefined
        });
    }
    /**
     * Stops the playback of the audio which represents the ringing phase of the
     * call establishment depicted by this {@code CallOverlay}.
     *
     * @private
     * @returns {void}
     */
    _pauseAudio() {
        const audio = this._audio;
        if (audio) {
            audio.pause();
        }
        if (this._playAudioInterval) {
            clearInterval(this._playAudioInterval);
            this._playAudioInterval = undefined;
        }
    }
    /**
     * Starts the playback of the audio which represents the ringing phase of
     * the call establishment depicted by this {@code CallOverlay}.
     *
     * @private
     * @returns {void}
     */
    _playAudio() {
        if (this._audio) {
            this._audio.play();
            if (!this._playAudioInterval) {
                this._playAudioInterval
                    = setInterval(() => this._playAudio(), 5000);
            }
        }
    }
    /**
     * Renders an audio element to represent the ringing phase of the call
     * establishment represented by this {@code CallOverlay}.
     *
     * @private
     * @returns {ReactElement}
     */
    _renderAudio() {
        if (this.state.renderAudio && this.state.ringing) {
            return (
                <Audio
                    ref = { this._setAudio }
                    src = './sounds/ring.ogg' />
            );
        }
        return null;
    }
    /**
     * Sets the (reference to the) {@link Audio} which renders the ringing phase
     * of the call establishment represented by this {@code CallOverlay}.
     *
     * @param {Audio} audio - The (reference to the) {@code Audio} which
     * plays/renders the audio depicting the ringing phase of the call
     * establishment represented by this {@code CallOverlay}.
     * @private
     * @returns {void}
     */
    _setAudio(audio) {
        this._audio = audio;
    }
    /**
     * Attempts to convert specified CSS class names into React
     * {@link Component} props {@code style} or {@code className}.
     *
     * @param {Array<string>} classNames - The CSS class names to convert
     * into React {@code Component} props {@code style} or {@code className}.
     * @returns {{
     *     className: string,
     *     style: Object
     * }}
     */
    _style(...classNames: Array<?string>) {
        let className = '';
        let style;
        for (const aClassName of classNames) {
            if (aClassName) {
                // Attemp to convert aClassName into style.
                if (styles && aClassName in styles) {
                    // React Native will accept an Array as the value of the
                    // style prop. However, I do not know about React.
                    style = {
                        ...style,
                        ...styles[aClassName]
                    };
                } else {
                    // Otherwise, leave it as className.
                    className += aClassName;
                }
            }
        }
        // Choose which of the className and/or style props has a value and,
        // consequently, must be returned.
        const props = {};
        if (className) {
            props.className = className;
        }
        if (style) {
            props.style = style;
        }
        return props;
    }
}
/**
 * Maps (parts of) the redux state to {@code CallOverlay}'s props.
 *
 * @param {Object} state - The redux state.
 * @private
 * @returns {{
 *     _callee: Object
 * }}
 */
function _mapStateToProps(state) {
    return {
        /**
         *
         * @private
         * @type {Object}
         */
        _callee: state['features/base/jwt'].callee
    };
}
export default connect(_mapStateToProps)(CallOverlay);
 |