| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 | import React, { Component } from 'react';
import { appNavigate } from '../../app';
import { isRoomValid } from '../../base/conference';
import { VideoTrack } from '../../base/media';
import { getLocalVideoTrack } from '../../base/tracks';
import { generateRoomWithoutSeparator } from '../../base/util';
/**
 * Base (abstract) class for container component rendering the welcome page.
 *
 * @abstract
 */
export class AbstractWelcomePage extends Component {
    /**
     * AbstractWelcomePage component's property types.
     *
     * @static
     */
    static propTypes = {
        dispatch: React.PropTypes.func,
        localVideoTrack: React.PropTypes.object,
        room: React.PropTypes.string
    }
    /**
     * Initializes a new AbstractWelcomePage instance, including the initial
     * state of the room name input.
     *
     * @param {Object} props - Component properties.
     */
    constructor(props) {
        super(props);
        /**
         * Save room name into component's local state.
         *
         * @type {Object}
         * @property {number|null} animateTimeoutId - Identificator for
         * letter animation timeout.
         * @property {string} generatedRoomname - Automatically generated
         * room name.
         * @property {string} room - Room name.
         * @property {string} roomPlaceholder - Room placeholder
         * that's used as a placeholder for input.
         * @property {nubmer|null} updateTimeoutId - Identificator for
         * updating generated room name.
         */
        this.state = {
            animateTimeoutId: null,
            generatedRoomname: '',
            room: '',
            roomPlaceholder: '',
            updateTimeoutId: null
        };
        // Bind event handlers so they are only bound once for every instance.
        this._animateRoomnameChanging
            = this._animateRoomnameChanging.bind(this);
        this._onJoin = this._onJoin.bind(this);
        this._onRoomChange = this._onRoomChange.bind(this);
        this._updateRoomname = this._updateRoomname.bind(this);
    }
    /**
     * This method is executed when component receives new properties.
     *
     * @inheritdoc
     * @param {Object} nextProps - New props component will receive.
     */
    componentWillReceiveProps(nextProps) {
        this.setState({ room: nextProps.room });
    }
    /**
     * This method is executed when method will be unmounted from DOM.
     *
     * @inheritdoc
     */
    componentWillUnmount() {
        this._clearTimeouts();
    }
    /**
     * Animates the changing of the room name.
     *
     * @param {string} word - The part of room name that should be added to
     * placeholder.
     * @private
     * @returns {void}
     */
    _animateRoomnameChanging(word) {
        let animateTimeoutId = null;
        const roomPlaceholder = this.state.roomPlaceholder + word.substr(0, 1);
        if (word.length > 1) {
            animateTimeoutId
                = setTimeout(
                        () => {
                            this._animateRoomnameChanging(
                                    word.substring(1, word.length));
                        },
                        70);
        }
        this.setState({
            animateTimeoutId,
            roomPlaceholder
        });
    }
    /**
     * Method that clears timeouts for animations and updates of room name.
     *
     * @private
     * @returns {void}
     */
    _clearTimeouts() {
        clearTimeout(this.state.animateTimeoutId);
        clearTimeout(this.state.updateTimeoutId);
    }
    /**
     * Determines whether the 'Join' button is (to be) disabled i.e. there's no
     * valid room name typed into the respective text input field.
     *
     * @protected
     * @returns {boolean} If the 'Join' button is (to be) disabled, true;
     * otherwise, false.
     */
    _isJoinDisabled() {
        return !isRoomValid(this.state.room);
    }
    /**
     * Handles joining. Either by clicking on 'Join' button
     * or by pressing 'Enter' in room name input field.
     *
     * @protected
     * @returns {void}
     */
    _onJoin() {
        const room = this.state.room || this.state.generatedRoomname;
        if (room) {
            this.props.dispatch(appNavigate(room));
        }
    }
    /**
     * Handles 'change' event for the room name text input field.
     *
     * @param {string} value - The text typed into the respective text input
     * field.
     * @protected
     * @returns {void}
     */
    _onRoomChange(value) {
        this.setState({ room: value });
    }
    /**
     * Renders a local video if any.
     *
     * @protected
     * @returns {(ReactElement|null)}
     */
    _renderLocalVideo() {
        return (
            <VideoTrack videoTrack = { this.props.localVideoTrack } />
        );
    }
    /**
     * Triggers the generation of a new room name and initiates an animation of
     * its changing.
     *
     * @protected
     * @returns {void}
     */
    _updateRoomname() {
        const generatedRoomname = generateRoomWithoutSeparator();
        const roomPlaceholder = '';
        const updateTimeoutId = setTimeout(this._updateRoomname, 10000);
        this._clearTimeouts();
        this.setState(
            {
                generatedRoomname,
                roomPlaceholder,
                updateTimeoutId
            },
            () => this._animateRoomnameChanging(generatedRoomname));
    }
}
/**
 * Selects local video track from tracks in state, local participant and room
 * and maps them to component props. It seems it's not possible to 'connect'
 * base component and then extend from it. So we export this function in order
 * to be used in child classes for 'connect'.
 *
 * @param {Object} state - Redux state.
 * @returns {{
 *      localVideoTrack: (Track|undefined),
 *      room: string
 * }}
 */
export function mapStateToProps(state) {
    const conference = state['features/base/conference'];
    const tracks = state['features/base/tracks'];
    return {
        localVideoTrack: getLocalVideoTrack(tracks),
        room: conference.room
    };
}
 |