Merge pull request #1756 from jitsi/remote_control_mm

Remote control
This commit is contained in:
virtuacoplenny 2017-07-10 15:20:55 -07:00 committed by GitHub
commit fcda36a8e0
8 changed files with 136 additions and 49 deletions

View File

@ -87,8 +87,8 @@ import {VIDEO_CONTAINER_TYPE} from "./modules/UI/videolayout/VideoContainer";
* lib-jitsi-meet to detect and invoke
*/
window.JitsiMeetScreenObtainer = {
openDesktopPicker(onSourceChoose) {
APP.store.dispatch(showDesktopPicker(onSourceChoose));
openDesktopPicker(options, onSourceChoose) {
APP.store.dispatch(showDesktopPicker(options, onSourceChoose));
}
};
@ -341,6 +341,7 @@ function createLocalTracks(options, checkForPermissionPrompt) {
.createLocalTracks({
// copy array to avoid mutations inside library
devices: options.devices.slice(0),
desktopSharingSources: options.desktopSharingSources,
resolution: config.resolution,
cameraDeviceId: typeof options.cameraDeviceId === 'undefined' ||
options.cameraDeviceId === null
@ -1108,9 +1109,14 @@ export default {
/**
* Toggles between screensharing and camera video.
* @param {boolean} [shareScreen]
* @param {Object} [options] - Screen sharing options that will be passed to
* createLocalTracks.
* @param {Array<string>} [options.desktopSharingSources] - Array with the
* sources that have to be displayed in the desktop picker window ('screen',
* 'window', etc.).
* @return {Promise.<T>}
*/
toggleScreenSharing(shareScreen = !this.isSharingScreen) {
toggleScreenSharing(shareScreen = !this.isSharingScreen, options = {}) {
if (this.videoSwitchInProgress) {
return Promise.reject('Switch in progress.');
}
@ -1130,6 +1136,7 @@ export default {
if (shareScreen) {
return createLocalTracks({
desktopSharingSources: options.desktopSharingSources,
devices: ['desktop'],
desktopSharingExtensionExternalInstallation: {
interval: 500,
@ -1176,24 +1183,25 @@ export default {
JitsiMeetJS.analytics.sendEvent(
'conference.sharingDesktop.start');
logger.log('sharing local desktop');
}).catch((err) => {
}).catch(err => {
// close external installation dialog to show the error.
if(externalInstallation)
$.prompt.close();
this.videoSwitchInProgress = false;
this.toggleScreenSharing(false);
if (err.name === TrackErrors.CHROME_EXTENSION_USER_CANCELED) {
return;
return Promise.reject(err);
}
this.toggleScreenSharing(false);
logger.error('failed to share local desktop', err);
if (err.name === TrackErrors.FIREFOX_EXTENSION_NEEDED) {
APP.UI.showExtensionRequiredDialog(
config.desktopSharingFirefoxExtensionURL
);
return;
return Promise.reject(err);
}
// Handling:
@ -2129,5 +2137,17 @@ export default {
*/
getDesktopSharingSourceId() {
return localVideo.sourceId;
},
/**
* Returns the desktop sharing source type or undefined if the desktop
* sharing is not active at the moment.
*
* @returns {'screen'|'window'|undefined} - The source type. If the track is
* not desktop track or the source type is not available, undefined will be
* returned.
*/
getDesktopSharingSourceType() {
return localVideo.sourceType;
}
};

View File

@ -341,6 +341,7 @@
"remoteControlDeniedMessage": "__user__ rejected your remote control request!",
"remoteControlAllowedMessage": "__user__ accepted your remote control request!",
"remoteControlErrorMessage": "An error occurred while trying to request remote control permissions from __user__!",
"startRemoteControlErrorMessage": "An error occurred while trying to start the remote control session!",
"remoteControlStopMessage": "The remote control session ended!",
"close": "Close",
"shareYourScreen": "Share your screen",

View File

@ -214,10 +214,15 @@ export default class Receiver extends RemoteControlParticipant {
let promise;
if (APP.conference.isSharingScreen) {
if (APP.conference.isSharingScreen
&& APP.conference.getDesktopSharingSourceType() === 'screen') {
promise = this._sendStartRequest();
} else {
promise = APP.conference.toggleScreenSharing()
promise = APP.conference.toggleScreenSharing(
true,
{
desktopSharingSources: [ 'screen' ]
})
.then(() => this._sendStartRequest());
}
@ -228,14 +233,20 @@ export default class Receiver extends RemoteControlParticipant {
action: PERMISSIONS_ACTIONS.grant
})
)
.catch(() => {
.catch(error => {
logger.error(error);
this.sendRemoteControlEndpointMessage(userId, {
type: EVENTS.permissions,
action: PERMISSIONS_ACTIONS.error
});
// FIXME: show err msg
this._stop();
APP.UI.messageHandler.openMessageDialog(
'dialog.remoteControlTitle',
'dialog.startRemoteControlErrorMessage'
);
this._stop(true);
});
}

View File

@ -62,12 +62,14 @@ export function resetDesktopSources() {
/**
* Signals to open a dialog with the DesktopPicker component.
*
* @param {Object} options - Desktop sharing settings.
* @param {Function} onSourceChoose - The callback to invoke when
* a DesktopCapturerSource has been chosen.
* @returns {Object}
*/
export function showDesktopPicker(onSourceChoose) {
export function showDesktopPicker(options, onSourceChoose) {
return openDialog(DesktopPicker, {
options,
onSourceChoose
});
}

View File

@ -1,5 +1,3 @@
/* global config */
import Tabs from '@atlaskit/tabs';
import React, { Component } from 'react';
import { connect } from 'react-redux';
@ -33,12 +31,7 @@ const TAB_CONFIGURATIONS = [
type: 'window'
}
];
const CONFIGURED_TYPES = config.desktopSharingChromeSources || [];
const VALID_TYPES = TAB_CONFIGURATIONS.map(c => c.type);
const TABS_TO_POPULATE
= TAB_CONFIGURATIONS.filter(
c => CONFIGURED_TYPES.includes(c.type) && VALID_TYPES.includes(c.type));
const TYPES_TO_FETCH = TABS_TO_POPULATE.map(c => c.type);
/**
* React component for DesktopPicker.
@ -63,6 +56,11 @@ class DesktopPicker extends Component {
*/
onSourceChoose: React.PropTypes.func,
/**
* An object with options related to desktop sharing.
*/
options: React.PropTypes.object,
/**
* An object with arrays of DesktopCapturerSources. The key should be
* the source type.
@ -85,7 +83,9 @@ class DesktopPicker extends Component {
super(props);
this.state = {
selectedSourceId: ''
selectedSource: {},
tabsToPopulate: [],
typesToFetch: []
};
this._poller = null;
@ -102,6 +102,10 @@ class DesktopPicker extends Component {
* @inheritdoc
*/
componentWillMount() {
const options = this.props.options || {};
this._onSourceTypesConfigChanged(
options.desktopSharingSources);
this._updateSources();
this._startPolling();
}
@ -116,12 +120,20 @@ class DesktopPicker extends Component {
* @returns {void}
*/
componentWillReceiveProps(nextProps) {
if (!this.state.selectedSourceId
if (!this.state.selectedSource.id
&& nextProps.sources.screen.length) {
this.setState({
selectedSourceId: nextProps.sources.screen[0].id
selectedSource: {
id: nextProps.sources.screen[0].id,
type: 'screen'
}
});
}
const options = this.props.options || {};
this._onSourceTypesConfigChanged(
options.desktopSharingSources);
}
/**
@ -155,14 +167,16 @@ class DesktopPicker extends Component {
/**
* Dispatches an action to hide the DesktopPicker and invokes the passed in
* callback with a selectedSourceId, if any.
* callback with a selectedSource, if any.
*
* @param {string} id - The id of the DesktopCapturerSource to pass into the
* onSourceChoose callback.
* @param {string} type - The type of the DesktopCapturerSource to pass into
* the onSourceChoose callback.
* @returns {void}
*/
_onCloseModal(id = '') {
this.props.onSourceChoose(id);
_onCloseModal(id = '', type) {
this.props.onSourceChoose(id, type);
this.props.dispatch(hideDialog());
}
@ -170,10 +184,16 @@ class DesktopPicker extends Component {
* Sets the currently selected DesktopCapturerSource.
*
* @param {string} id - The id of DesktopCapturerSource.
* @param {string} type - The type of DesktopCapturerSource.
* @returns {void}
*/
_onPreviewClick(id) {
this.setState({ selectedSourceId: id });
_onPreviewClick(id, type) {
this.setState({
selectedSource: {
id,
type
}
});
}
/**
@ -183,7 +203,9 @@ class DesktopPicker extends Component {
* @returns {void}
*/
_onSubmit() {
this._onCloseModal(this.state.selectedSourceId);
const { id, type } = this.state.selectedSource;
this._onCloseModal(id, type);
}
/**
@ -193,22 +215,23 @@ class DesktopPicker extends Component {
* @returns {ReactElement}
*/
_renderTabs() {
const { selectedSourceId } = this.state;
const { selectedSource } = this.state;
const { sources, t } = this.props;
const tabs
= TABS_TO_POPULATE.map(({ defaultSelected, label, type }) => {
return {
content: <DesktopPickerPane
key = { type }
onClick = { this._onPreviewClick }
onDoubleClick = { this._onCloseModal }
selectedSourceId = { selectedSourceId }
sources = { sources[type] || [] }
type = { type } />,
defaultSelected,
label: t(label)
};
});
= this.state.tabsToPopulate.map(
({ defaultSelected, label, type }) => {
return {
content: <DesktopPickerPane
key = { type }
onClick = { this._onPreviewClick }
onDoubleClick = { this._onCloseModal }
selectedSourceId = { selectedSource.id }
sources = { sources[type] || [] }
type = { type } />,
defaultSelected,
label: t(label)
};
});
return <Tabs tabs = { tabs } />;
}
@ -235,6 +258,25 @@ class DesktopPicker extends Component {
this._poller = null;
}
/**
* Handles changing of allowed desktop sharing source types.
*
* @param {Array<string>} desktopSharingSourceTypes - The types that will be
* fetched and displayed.
* @returns {void}
*/
_onSourceTypesConfigChanged(desktopSharingSourceTypes = []) {
const tabsToPopulate = TAB_CONFIGURATIONS.filter(
c => desktopSharingSourceTypes.includes(c.type)
&& VALID_TYPES.includes(c.type)
);
this.setState({
tabsToPopulate,
typesToFetch: tabsToPopulate.map(c => c.type)
});
}
/**
* Dispatches an action to get currently available DesktopCapturerSources.
*
@ -243,7 +285,7 @@ class DesktopPicker extends Component {
*/
_updateSources() {
this.props.dispatch(obtainDesktopSources(
TYPES_TO_FETCH,
this.state.typesToFetch,
{
THUMBNAIL_SIZE
}

View File

@ -66,7 +66,8 @@ class DesktopPickerPane extends Component {
onClick = { onClick }
onDoubleClick = { onDoubleClick }
selected = { source.id === selectedSourceId }
source = { source } />);
source = { source }
type = { type } />);
return (
<div className = { classNames }>

View File

@ -34,7 +34,12 @@ class DesktopSourcePreview extends Component {
/**
* The DesktopCapturerSource to display.
*/
source: React.PropTypes.object
source: React.PropTypes.object,
/**
* The source type of the DesktopCapturerSources to display.
*/
type: React.PropTypes.string
};
/**
@ -83,7 +88,9 @@ class DesktopSourcePreview extends Component {
* @returns {void}
*/
_onClick() {
this.props.onClick(this.props.source.id);
const { source, type } = this.props;
this.props.onClick(source.id, type);
}
/**
@ -92,7 +99,9 @@ class DesktopSourcePreview extends Component {
* @returns {void}
*/
_onDoubleClick() {
this.props.onDoubleClick(this.props.source.id);
const { source, type } = this.props;
this.props.onDoubleClick(source.id, type);
}
}

View File

@ -94,7 +94,8 @@ class RemoteControlAuthorizationDialog extends Component {
_getAdditionalMessage() {
// FIXME: Once we have this information in redux we should
// start getting it from there.
if (APP.conference.isSharingScreen) {
if (APP.conference.isSharingScreen
&& APP.conference.getDesktopSharingSourceType() === 'screen') {
return null;
}