| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 | /* global JitsiMeetJS */
import { AtlasKitThemeProvider } from '@atlaskit/theme';
import React from 'react';
import ReactDOM from 'react-dom';
import { I18nextProvider } from 'react-i18next';
import {
    PostMessageTransportBackend,
    Transport
} from '../../../../../modules/transport';
import {
    getAvailableDevices,
    getCurrentDevices,
    isDeviceChangeAvailable,
    isDeviceListAvailable,
    isMultipleAudioInputSupported,
    setAudioInputDevice,
    setAudioOutputDevice,
    setVideoInputDevice
} from '../../../../../modules/API/external/functions';
import parseURLParams from '../../../base/config/parseURLParams';
import DialogWithTabs from '../../../base/dialog/components/web/DialogWithTabs';
import DeviceSelection from '../../../device-selection/components/DeviceSelection';
/**
 * Implements a class that renders the React components for the device selection
 * popup page and handles the communication between the components and Jitsi
 * Meet.
 */
export default class DeviceSelectionPopup {
    /**
     * Initializes a new DeviceSelectionPopup instance.
     *
     * @param {Object} i18next - The i18next instance used for translation.
     */
    constructor(i18next) {
        this.close = this.close.bind(this);
        this._i18next = i18next;
        this._onSubmit = this._onSubmit.bind(this);
        const { scope } = parseURLParams(window.location);
        this._transport = new Transport({
            backend: new PostMessageTransportBackend({
                postisOptions: {
                    scope,
                    window: window.opener
                }
            })
        });
        this._transport.on('event', event => {
            if (event.name === 'deviceListChanged') {
                this._updateAvailableDevices();
                return true;
            }
            return false;
        });
        this._dialogProps = {
            availableDevices: {},
            selectedAudioInputId: '',
            selectedAudioOutputId: '',
            selectedVideoInputId: '',
            disableAudioInputChange: true,
            disableBlanketClickDismiss: true,
            disableDeviceChange: true,
            hideAudioInputPreview: !JitsiMeetJS.isCollectingLocalStats(),
            hideAudioOutputSelect: true
        };
        this._initState();
    }
    /**
     * Sends event to Jitsi Meet to close the popup dialog.
     *
     * @returns {void}
     */
    close() {
        this._transport.sendEvent({
            type: 'devices-dialog',
            name: 'close'
        });
    }
    /**
     * Changes the properties of the react component and re-renders it.
     *
     * @param {Object} newProps - The new properties that will be assigned to
     * the current ones.
     * @returns {void}
     */
    _changeDialogProps(newProps) {
        this._dialogProps = {
            ...this._dialogProps,
            ...newProps
        };
        this._render();
    }
    /**
     * Returns Promise that resolves with result an list of available devices.
     *
     * @returns {Promise}
     */
    _getAvailableDevices() {
        return getAvailableDevices(this._transport);
    }
    /**
     * Returns Promise that resolves with current selected devices.
     *
     * @returns {Promise}
     */
    _getCurrentDevices() {
        return getCurrentDevices(this._transport).then(currentDevices => {
            const {
                audioInput = {},
                audioOutput = {},
                videoInput = {}
            } = currentDevices;
            return {
                audioInput: audioInput.deviceId,
                audioOutput: audioOutput.deviceId,
                videoInput: videoInput.deviceId
            };
        });
    }
    /**
     * Initializes the state.
     *
     * @returns {void}
     */
    _initState() {
        return Promise.all([
            this._getAvailableDevices(),
            this._isDeviceListAvailable(),
            this._isDeviceChangeAvailable(),
            this._isDeviceChangeAvailable('output'),
            this._getCurrentDevices(),
            this._isMultipleAudioInputSupported()
        ]).then(([
            availableDevices,
            listAvailable,
            changeAvailable,
            changeOutputAvailable,
            currentDevices,
            multiAudioInputSupported
        ]) => {
            this._changeDialogProps({
                availableDevices,
                selectedAudioInputId: currentDevices.audioInput,
                selectedAudioOutputId: currentDevices.audioOutput,
                selectedVideoInputId: currentDevices.videoInput,
                disableAudioInputChange: !multiAudioInputSupported,
                disableDeviceChange: !listAvailable || !changeAvailable,
                hideAudioOutputSelect: !changeOutputAvailable
            });
        });
    }
    /**
     * Returns Promise that resolves with true if the device change is available
     * and with false if not.
     *
     * @param {string} [deviceType] - Values - 'output', 'input' or undefined.
     * Default - 'input'.
     * @returns {Promise}
     */
    _isDeviceChangeAvailable(deviceType) {
        return isDeviceChangeAvailable(this._transport, deviceType).catch(() => false);
    }
    /**
     * Returns Promise that resolves with true if the device list is available
     * and with false if not.
     *
     * @returns {Promise}
     */
    _isDeviceListAvailable() {
        return isDeviceListAvailable(this._transport).catch(() => false);
    }
    /**
     * Returns Promise that resolves with true if multiple audio input is supported
     * and with false if not.
     *
     * @returns {Promise}
     */
    _isMultipleAudioInputSupported() {
        return isMultipleAudioInputSupported(this._transport).catch(() => false);
    }
    /**
     * Callback invoked to save changes to selected devices and close the
     * dialog.
     *
     * @param {Object} newSettings - The chosen device IDs.
     * @private
     * @returns {void}
     */
    _onSubmit(newSettings) {
        const promises = [];
        if (newSettings.selectedVideoInputId
                !== this._dialogProps.selectedVideoInputId) {
            promises.push(
                this._setVideoInputDevice(newSettings.selectedVideoInputId));
        }
        if (newSettings.selectedAudioInputId
                !== this._dialogProps.selectedAudioInputId) {
            promises.push(
                this._setAudioInputDevice(newSettings.selectedAudioInputId));
        }
        if (newSettings.selectedAudioOutputId
                !== this._dialogProps.selectedAudioOutputId) {
            promises.push(
                this._setAudioOutputDevice(newSettings.selectedAudioOutputId));
        }
        Promise.all(promises).then(this.close, this.close);
    }
    /**
     * Renders the React components for the popup page.
     *
     * @returns {void}
     */
    _render() {
        const onSubmit = this.close;
        ReactDOM.render(
            <I18nextProvider i18n = { this._i18next }>
                <AtlasKitThemeProvider mode = 'dark'>
                    <DialogWithTabs
                        closeDialog = { this.close }
                        cssClassName = 'settings-dialog'
                        onSubmit = { onSubmit }
                        tabs = { [ {
                            component: DeviceSelection,
                            label: 'settings.devices',
                            props: this._dialogProps,
                            submit: this._onSubmit
                        } ] }
                        titleKey = 'settings.title' />
                </AtlasKitThemeProvider>
            </I18nextProvider>,
            document.getElementById('react'));
    }
    /**
     * Sets the audio input device to the one with the id that is passed.
     *
     * @param {string} id - The id of the new device.
     * @returns {Promise}
     */
    _setAudioInputDevice(id) {
        return setAudioInputDevice(this._transport, undefined, id);
    }
    /**
     * Sets the audio output device to the one with the id that is passed.
     *
     * @param {string} id - The id of the new device.
     * @returns {Promise}
     */
    _setAudioOutputDevice(id) {
        return setAudioOutputDevice(this._transport, undefined, id);
    }
    /**
     * Sets the video input device to the one with the id that is passed.
     *
     * @param {string} id - The id of the new device.
     * @returns {Promise}
     */
    _setVideoInputDevice(id) {
        return setVideoInputDevice(this._transport, undefined, id);
    }
    /**
     * Updates the available devices.
     *
     * @returns {void}
     */
    _updateAvailableDevices() {
        this._getAvailableDevices().then(devices =>
            this._changeDialogProps({ availableDevices: devices })
        );
    }
}
 |