| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 | import AKInlineDialog from '@atlaskit/inline-dialog';
import React, { Component } from 'react';
import { JitsiParticipantConnectionStatus } from '../../base/lib-jitsi-meet';
import { ConnectionStatsTable } from '../../connection-stats';
import statsEmitter from '../statsEmitter';
declare var $: Object;
declare var interfaceConfig: Object;
// Converts the percent for connection quality into a string recognized for CSS.
const QUALITY_TO_WIDTH = [
    // Full (5 bars)
    {
        percent: 80,
        width: '100%'
    },
    // 4 bars
    {
        percent: 60,
        width: '80%'
    },
    // 3 bars
    {
        percent: 40,
        width: '55%'
    },
    // 2 bars
    {
        percent: 20,
        width: '40%'
    },
    // 1 bar
    {
        percent: 0,
        width: '20%'
    }
    // Note: we never show 0 bars.
];
/**
 * Implements a React {@link Component} which displays the current connection
 * quality percentage and has a popover to show more detailed connection stats.
 *
 * @extends {Component}
 */
class ConnectionIndicator extends Component {
    /**
     * {@code ConnectionIndicator} component's property types.
     *
     * @static
     */
    static propTypes = {
        /**
         * The current condition of the user's connection, matching one of the
         * enumerated values in the library.
         *
         * @type {JitsiParticipantConnectionStatus}
         */
        connectionStatus: React.PropTypes.string,
        /**
         * Whether or not clicking the indicator should display a popover for
         * more details.
         */
        enableStatsDisplay: React.PropTypes.bool,
        /**
         * Whether or not the displays stats are for local video.
         */
        isLocalVideo: React.PropTypes.bool,
        /**
         * Relative to the icon from where the popover for more connection
         * details should display.
         */
        statsPopoverPosition: React.PropTypes.string,
        /**
         * Invoked to obtain translated strings.
         */
        t: React.PropTypes.func,
        /**
         * The user ID associated with the displayed connection indication and
         * stats.
         */
        userID: React.PropTypes.string
    };
    /**
     * Initializes a new {@code ConnectionIndicator} instance.
     *
     * @param {Object} props - The read-only properties with which the new
     * instance is to be initialized.
     */
    constructor(props) {
        super(props);
        this.state = {
            /**
             * Whether or not the popover content should display additional
             * statistics.
             *
             * @type {boolean}
             */
            showMoreStats: false,
            /**
             * Cache of the stats received from subscribing to stats emitting.
             * The keys should be the name of the stat. With each stat update,
             * updates stats are mixed in with cached stats and a new stats
             * object is set in state.
             */
            stats: {}
        };
        // Bind event handlers so they are only bound once for every instance.
        this._onStatsUpdated = this._onStatsUpdated.bind(this);
        this._onStatsClose = this._onStatsClose.bind(this);
        this._onStatsToggle = this._onStatsToggle.bind(this);
        this._onStatsUpdated = this._onStatsUpdated.bind(this);
        this._onToggleShowMore = this._onToggleShowMore.bind(this);
    }
    /**
     * Starts listening for stat updates.
     *
     * @inheritdoc
     * returns {void}
     */
    componentDidMount() {
        statsEmitter.subscribeToClientStats(
            this.props.userID, this._onStatsUpdated);
    }
    /**
     * Updates which user's stats are being listened to.
     *
     * @inheritdoc
     * returns {void}
     */
    componentDidUpdate(prevProps) {
        if (prevProps.userID !== this.props.userID) {
            statsEmitter.unsubscribeToClientStats(
                prevProps.userID, this._onStatsUpdated);
            statsEmitter.subscribeToClientStats(
                this.props.userID, this._onStatsUpdated);
        }
    }
    /**
     * Sets the state to hide the Statistics Table popover.
     *
     * @private
     * @returns {void}
     */
    componentWillUnmount() {
        statsEmitter.unsubscribeToClientStats(
            this.props.userID, this._onStatsUpdated);
    }
    /**
     * Implements React's {@link Component#render()}.
     *
     * @inheritdoc
     * @returns {ReactElement}
     */
    render() {
        return (
            <div className = 'indicator-container'>
                <AKInlineDialog
                    content = { this._renderStatisticsTable() }
                    isOpen = { this.state.showStats }
                    onClose = { this._onStatsClose }
                    position = { this.props.statsPopoverPosition }>
                    <div
                        className = 'popover-trigger'
                        onClick = { this._onStatsToggle }>
                        <div className = 'connection-indicator indicator'>
                            <div className = 'connection indicatoricon'>
                                { this._renderIcon() }
                            </div>
                        </div>
                    </div>
                </AKInlineDialog>
            </div>
        );
    }
    /**
     * Sets the state not to show the Statistics Table popover.
     *
     * @private
     * @returns {void}
     */
    _onStatsClose() {
        this.setState({ showStats: false });
    }
    /**
     * Sets the state to show or hide the Statistics Table popover.
     *
     * @private
     * @returns {void}
     */
    _onStatsToggle() {
        if (this.props.enableStatsDisplay) {
            this.setState({ showStats: !this.state.showStats });
        }
    }
    /**
     * Callback invoked when new connection stats associated with the passed in
     * user ID are available. Will update the component's display of current
     * statistics.
     *
     * @param {Object} stats - Connection stats from the library.
     * @private
     * @returns {void}
     */
    _onStatsUpdated(stats = {}) {
        const { connectionQuality } = stats;
        const newPercentageState = typeof connectionQuality === 'undefined'
            ? {} : { percent: connectionQuality };
        const newStats = Object.assign(
            {},
            this.state.stats,
            stats,
            newPercentageState);
        this.setState({
            stats: newStats
        });
    }
    /**
     * Callback to invoke when the show more link in the popover content is
     * clicked. Sets the state which will determine if the popover should show
     * additional statistics about the connection.
     *
     * @returns {void}
     */
    _onToggleShowMore() {
        this.setState({ showMoreStats: !this.state.showMoreStats });
    }
    /**
     * Creates a ReactElement for displaying an icon that represents the current
     * connection quality.
     *
     * @returns {ReactElement}
     */
    _renderIcon() {
        switch (this.props.connectionStatus) {
        case JitsiParticipantConnectionStatus.INTERRUPTED:
            return (
                <span className = 'connection_lost'>
                    <i className = 'icon-connection-lost' />
                </span>
            );
        case JitsiParticipantConnectionStatus.INACTIVE:
            return (
                <span className = 'connection_ninja'>
                    <i className = 'icon-ninja' />
                </span>
            );
        default: {
            const { percent } = this.state.stats;
            const width = QUALITY_TO_WIDTH.find(x => percent >= x.percent);
            const iconWidth = width && width.width
                ? { width: width && width.width } : {};
            return [
                <span
                    className = 'connection_empty'
                    key = 'icon-empty'>
                    <i className = 'icon-connection' />
                </span>,
                <span
                    className = 'connection_full'
                    key = 'icon-full'
                    style = { iconWidth }>
                    <i className = 'icon-connection' />
                </span>
            ];
        }
        }
    }
    /**
     * Creates a {@code ConnectionStatisticsTable} instance.
     *
     * @returns {ReactElement}
     */
    _renderStatisticsTable() {
        const {
            bandwidth,
            bitrate,
            framerate,
            packetLoss,
            resolution,
            transport
        } = this.state.stats;
        return (
            <ConnectionStatsTable
                bandwidth = { bandwidth }
                bitrate = { bitrate }
                framerate = { framerate }
                isLocalVideo = { this.props.isLocalVideo }
                onShowMore = { this._onToggleShowMore }
                packetLoss = { packetLoss }
                resolution = { resolution }
                shouldShowMore = { this.state.showMoreStats }
                transport = { transport } />
        );
    }
}
export default ConnectionIndicator;
 |