Remote control - display the authorization dialog in meet (#1541)
* fix(react/participant): store display name in redux * feat(remotecontrol): Add option to display the authorization dialog in meet * feat(remotecontrol): Enable ESLint and Flow
This commit is contained in:
parent
d694e8df86
commit
d91340166d
|
@ -35,6 +35,11 @@ module.system=haste
|
|||
|
||||
experimental.strict_type_args=true
|
||||
|
||||
; FIXME: munge_underscores should be false but right now there are some errors
|
||||
; if we change the value to false
|
||||
; Treats class properties with underscore as private. Disabled because currently
|
||||
; for us "_" can mean protected too.
|
||||
; munge_underscores=false
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
|
||||
|
@ -46,6 +51,7 @@ suppress_type=$FixMe
|
|||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-8]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-8]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowDisableNextLine
|
||||
|
||||
unsafe.enable_getters_and_setters=true
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ node_modules/
|
|||
# supersedes JSHint.
|
||||
flow-typed/
|
||||
modules/API/
|
||||
modules/remotecontrol/RemoteControlParticipant.js
|
||||
modules/remotecontrol/
|
||||
modules/transport/
|
||||
react/
|
||||
|
||||
|
|
|
@ -1442,6 +1442,10 @@ export default {
|
|||
room.on(ConferenceEvents.DISPLAY_NAME_CHANGED, (id, displayName) => {
|
||||
const formattedDisplayName
|
||||
= displayName.substr(0, MAX_DISPLAY_NAME_LENGTH);
|
||||
APP.store.dispatch(participantUpdated({
|
||||
id,
|
||||
name: formattedDisplayName
|
||||
}));
|
||||
APP.API.notifyDisplayNameChanged(id, formattedDisplayName);
|
||||
APP.UI.changeDisplayName(id, formattedDisplayName);
|
||||
});
|
||||
|
@ -2053,6 +2057,12 @@ export default {
|
|||
return;
|
||||
}
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
id: this.getMyUserId(),
|
||||
local: true,
|
||||
name: formattedNickname
|
||||
}));
|
||||
|
||||
APP.settings.setDisplayName(formattedNickname);
|
||||
if (room) {
|
||||
room.setDisplayName(formattedNickname);
|
||||
|
|
|
@ -212,6 +212,7 @@
|
|||
},
|
||||
"dialog": {
|
||||
"add": "Add",
|
||||
"allow": "Allow",
|
||||
"kickMessage": "Ouch! You have been kicked out of the meet!",
|
||||
"popupError": "Your browser is blocking popup windows from this site. Please enable popups in your browser's security settings and try again.",
|
||||
"passwordErrorTitle": "Password Error",
|
||||
|
@ -330,7 +331,9 @@
|
|||
"muteParticipantTitle": "Mute this participant?",
|
||||
"muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
|
||||
"muteParticipantButton": "Mute",
|
||||
"remoteControlTitle": "Remote Control",
|
||||
"remoteControlTitle": "Remote desktop control",
|
||||
"remoteControlRequestMessage": "Will you allow __user__ to remotely control your desktop?",
|
||||
"remoteControlShareScreenWarning": "Note that if you press \"Allow\" you will share your screen!",
|
||||
"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__!",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
'extends': '../../react/.eslintrc.js'
|
||||
};
|
|
@ -1,20 +1,29 @@
|
|||
/* global $, JitsiMeetJS, APP */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
import * as KeyCodes from "../keycode/keycode";
|
||||
/* @flow */
|
||||
|
||||
import { getLogger } from 'jitsi-meet-logger';
|
||||
|
||||
import * as KeyCodes from '../keycode/keycode';
|
||||
import {
|
||||
EVENT_TYPES,
|
||||
PERMISSIONS_ACTIONS,
|
||||
REMOTE_CONTROL_EVENT_NAME
|
||||
} from "../../service/remotecontrol/Constants";
|
||||
import RemoteControlParticipant from "./RemoteControlParticipant";
|
||||
import UIEvents from "../../service/UI/UIEvents";
|
||||
} from '../../service/remotecontrol/Constants';
|
||||
import UIEvents from '../../service/UI/UIEvents';
|
||||
|
||||
import RemoteControlParticipant from './RemoteControlParticipant';
|
||||
|
||||
declare var $: Function;
|
||||
declare var APP: Object;
|
||||
declare var JitsiMeetJS: Object;
|
||||
|
||||
const ConferenceEvents = JitsiMeetJS.events.conference;
|
||||
const logger = getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Extract the keyboard key from the keyboard event.
|
||||
* @param event {KeyboardEvent} the event.
|
||||
* @returns {KEYS} the key that is pressed or undefined.
|
||||
*
|
||||
* @param {KeyboardEvent} event - The event.
|
||||
* @returns {KEYS} The key that is pressed or undefined.
|
||||
*/
|
||||
function getKey(event) {
|
||||
return KeyCodes.keyboardEventToKey(event);
|
||||
|
@ -22,26 +31,28 @@ function getKey(event) {
|
|||
|
||||
/**
|
||||
* Extract the modifiers from the keyboard event.
|
||||
* @param event {KeyboardEvent} the event.
|
||||
* @returns {Array} with possible values: "shift", "control", "alt", "command".
|
||||
*
|
||||
* @param {KeyboardEvent} event - The event.
|
||||
* @returns {Array} With possible values: "shift", "control", "alt", "command".
|
||||
*/
|
||||
function getModifiers(event) {
|
||||
let modifiers = [];
|
||||
if(event.shiftKey) {
|
||||
modifiers.push("shift");
|
||||
const modifiers = [];
|
||||
|
||||
if (event.shiftKey) {
|
||||
modifiers.push('shift');
|
||||
}
|
||||
|
||||
if(event.ctrlKey) {
|
||||
modifiers.push("control");
|
||||
if (event.ctrlKey) {
|
||||
modifiers.push('control');
|
||||
}
|
||||
|
||||
|
||||
if(event.altKey) {
|
||||
modifiers.push("alt");
|
||||
if (event.altKey) {
|
||||
modifiers.push('alt');
|
||||
}
|
||||
|
||||
if(event.metaKey) {
|
||||
modifiers.push("command");
|
||||
if (event.metaKey) {
|
||||
modifiers.push('command');
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
|
@ -53,14 +64,22 @@ function getModifiers(event) {
|
|||
* party of the remote control session.
|
||||
*/
|
||||
export default class Controller extends RemoteControlParticipant {
|
||||
_area: ?Object;
|
||||
_controlledParticipant: string | null;
|
||||
_isCollectingEvents: boolean;
|
||||
_largeVideoChangedListener: Function;
|
||||
_requestedParticipant: string | null;
|
||||
_stopListener: Function;
|
||||
_userLeftListener: Function;
|
||||
|
||||
/**
|
||||
* Creates new instance.
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this.isCollectingEvents = false;
|
||||
this.controlledParticipant = null;
|
||||
this.requestedParticipant = null;
|
||||
this._isCollectingEvents = false;
|
||||
this._controlledParticipant = null;
|
||||
this._requestedParticipant = null;
|
||||
this._stopListener = this._handleRemoteControlStoppedEvent.bind(this);
|
||||
this._userLeftListener = this._onUserLeft.bind(this);
|
||||
this._largeVideoChangedListener
|
||||
|
@ -69,24 +88,28 @@ export default class Controller extends RemoteControlParticipant {
|
|||
|
||||
/**
|
||||
* Requests permissions from the remote control receiver side.
|
||||
* @param {string} userId the user id of the participant that will be
|
||||
*
|
||||
* @param {string} userId - The user id of the participant that will be
|
||||
* requested.
|
||||
* @param {JQuerySelector} eventCaptureArea the area that is going to be
|
||||
* @param {JQuerySelector} eventCaptureArea - The area that is going to be
|
||||
* used mouse and keyboard event capture.
|
||||
* @returns {Promise<boolean>} - resolve values:
|
||||
* true - accept
|
||||
* false - deny
|
||||
* null - the participant has left.
|
||||
* @returns {Promise<boolean>} Resolve values - true(accept), false(deny),
|
||||
* null(the participant has left).
|
||||
*/
|
||||
requestPermissions(userId, eventCaptureArea) {
|
||||
if(!this.enabled) {
|
||||
return Promise.reject(new Error("Remote control is disabled!"));
|
||||
requestPermissions(userId: string, eventCaptureArea: Object) {
|
||||
if (!this.enabled) {
|
||||
return Promise.reject(new Error('Remote control is disabled!'));
|
||||
}
|
||||
this.area = eventCaptureArea;// $("#largeVideoWrapper")
|
||||
logger.log("Requsting remote control permissions from: " + userId);
|
||||
|
||||
this._area = eventCaptureArea;// $("#largeVideoWrapper")
|
||||
logger.log(`Requsting remote control permissions from: ${userId}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let onUserLeft, permissionsReplyListener;
|
||||
|
||||
const clearRequest = () => {
|
||||
this.requestedParticipant = null;
|
||||
this._requestedParticipant = null;
|
||||
APP.conference.removeConferenceListener(
|
||||
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||
permissionsReplyListener);
|
||||
|
@ -94,31 +117,35 @@ export default class Controller extends RemoteControlParticipant {
|
|||
ConferenceEvents.USER_LEFT,
|
||||
onUserLeft);
|
||||
};
|
||||
const permissionsReplyListener = (participant, event) => {
|
||||
|
||||
permissionsReplyListener = (participant, event) => {
|
||||
let result = null;
|
||||
|
||||
try {
|
||||
result = this._handleReply(participant, event);
|
||||
} catch (e) {
|
||||
clearRequest();
|
||||
reject(e);
|
||||
}
|
||||
if(result !== null) {
|
||||
if (result !== null) {
|
||||
clearRequest();
|
||||
resolve(result);
|
||||
}
|
||||
};
|
||||
const onUserLeft = (id) => {
|
||||
if(id === this.requestedParticipant) {
|
||||
onUserLeft = id => {
|
||||
if (id === this._requestedParticipant) {
|
||||
clearRequest();
|
||||
resolve(null);
|
||||
}
|
||||
};
|
||||
|
||||
APP.conference.addConferenceListener(
|
||||
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||
permissionsReplyListener);
|
||||
APP.conference.addConferenceListener(ConferenceEvents.USER_LEFT,
|
||||
onUserLeft);
|
||||
this.requestedParticipant = userId;
|
||||
this._sendRemoteControlEvent(userId, {
|
||||
this._requestedParticipant = userId;
|
||||
this.sendRemoteControlEvent(userId, {
|
||||
type: EVENT_TYPES.permissions,
|
||||
action: PERMISSIONS_ACTIONS.request
|
||||
}, e => {
|
||||
|
@ -130,54 +157,58 @@ export default class Controller extends RemoteControlParticipant {
|
|||
|
||||
/**
|
||||
* Handles the reply of the permissions request.
|
||||
* @param {JitsiParticipant} participant the participant that has sent the
|
||||
* reply
|
||||
* @param {RemoteControlEvent} event the remote control event.
|
||||
*
|
||||
* @param {JitsiParticipant} participant - The participant that has sent the
|
||||
* reply.
|
||||
* @param {RemoteControlEvent} event - The remote control event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleReply(participant, event) {
|
||||
_handleReply(participant: Object, event: Object) {
|
||||
const userId = participant.getId();
|
||||
if(this.enabled
|
||||
|
||||
if (this.enabled
|
||||
&& event.name === REMOTE_CONTROL_EVENT_NAME
|
||||
&& event.type === EVENT_TYPES.permissions
|
||||
&& userId === this.requestedParticipant) {
|
||||
if(event.action !== PERMISSIONS_ACTIONS.grant) {
|
||||
this.area = null;
|
||||
&& userId === this._requestedParticipant) {
|
||||
if (event.action !== PERMISSIONS_ACTIONS.grant) {
|
||||
this._area = undefined;
|
||||
}
|
||||
switch(event.action) {
|
||||
case PERMISSIONS_ACTIONS.grant: {
|
||||
this.controlledParticipant = userId;
|
||||
logger.log("Remote control permissions granted to: "
|
||||
+ userId);
|
||||
this._start();
|
||||
return true;
|
||||
}
|
||||
case PERMISSIONS_ACTIONS.deny:
|
||||
return false;
|
||||
case PERMISSIONS_ACTIONS.error:
|
||||
throw new Error("Error occurred on receiver side");
|
||||
default:
|
||||
throw new Error("Unknown reply received!");
|
||||
switch (event.action) {
|
||||
case PERMISSIONS_ACTIONS.grant: {
|
||||
this._controlledParticipant = userId;
|
||||
logger.log('Remote control permissions granted to:', userId);
|
||||
this._start();
|
||||
|
||||
return true;
|
||||
}
|
||||
case PERMISSIONS_ACTIONS.deny:
|
||||
return false;
|
||||
case PERMISSIONS_ACTIONS.error:
|
||||
throw new Error('Error occurred on receiver side');
|
||||
default:
|
||||
throw new Error('Unknown reply received!');
|
||||
}
|
||||
} else {
|
||||
//different message type or another user -> ignoring the message
|
||||
// different message type or another user -> ignoring the message
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles remote control stopped.
|
||||
* @param {JitsiParticipant} participant the participant that has sent the
|
||||
* event
|
||||
* @param {Object} event EndpointMessage event from the data channels.
|
||||
* @property {string} type property. The function process only events with
|
||||
* name REMOTE_CONTROL_EVENT_NAME
|
||||
* @property {RemoteControlEvent} event - the remote control event.
|
||||
*
|
||||
* @param {JitsiParticipant} participant - The participant that has sent the
|
||||
* event.
|
||||
* @param {Object} event - EndpointMessage event from the data channels.
|
||||
* @property {string} type - The function process only events with
|
||||
* name REMOTE_CONTROL_EVENT_NAME.
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleRemoteControlStoppedEvent(participant, event) {
|
||||
if(this.enabled
|
||||
_handleRemoteControlStoppedEvent(participant: Object, event: Object) {
|
||||
if (this.enabled
|
||||
&& event.name === REMOTE_CONTROL_EVENT_NAME
|
||||
&& event.type === EVENT_TYPES.stop
|
||||
&& participant.getId() === this.controlledParticipant) {
|
||||
&& participant.getId() === this._controlledParticipant) {
|
||||
this._stop();
|
||||
}
|
||||
}
|
||||
|
@ -185,9 +216,11 @@ export default class Controller extends RemoteControlParticipant {
|
|||
/**
|
||||
* Starts processing the mouse and keyboard events. Sets conference
|
||||
* listeners. Disables keyboard events.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_start() {
|
||||
logger.log("Starting remote control controller.");
|
||||
logger.log('Starting remote control controller.');
|
||||
APP.UI.addListener(UIEvents.LARGE_VIDEO_ID_CHANGED,
|
||||
this._largeVideoChangedListener);
|
||||
APP.conference.addConferenceListener(
|
||||
|
@ -200,35 +233,53 @@ export default class Controller extends RemoteControlParticipant {
|
|||
|
||||
/**
|
||||
* Disables the keyboatd shortcuts. Starts collecting remote control
|
||||
* events.
|
||||
* events. It can be used to resume an active remote control session wchich
|
||||
* was paused with this.pause().
|
||||
*
|
||||
* It can be used to resume an active remote control session wchich was
|
||||
* paused with this.pause().
|
||||
* @returns {void}
|
||||
*/
|
||||
resume() {
|
||||
if(!this.enabled || this.isCollectingEvents) {
|
||||
if (!this.enabled || this._isCollectingEvents || !this._area) {
|
||||
return;
|
||||
}
|
||||
logger.log("Resuming remote control controller.");
|
||||
this.isCollectingEvents = true;
|
||||
logger.log('Resuming remote control controller.');
|
||||
this._isCollectingEvents = true;
|
||||
APP.keyboardshortcut.enable(false);
|
||||
this.area.mousemove(event => {
|
||||
const position = this.area.position();
|
||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
this._area.mousemove(event => {
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
const position = this._area.position();
|
||||
|
||||
this.sendRemoteControlEvent(this._controlledParticipant, {
|
||||
type: EVENT_TYPES.mousemove,
|
||||
x: (event.pageX - position.left)/this.area.width(),
|
||||
y: (event.pageY - position.top)/this.area.height()
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null
|
||||
x: (event.pageX - position.left) / this._area.width(),
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null
|
||||
y: (event.pageY - position.top) / this._area.height()
|
||||
});
|
||||
});
|
||||
this.area.mousedown(this._onMouseClickHandler.bind(this,
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
this._area.mousedown(this._onMouseClickHandler.bind(this,
|
||||
EVENT_TYPES.mousedown));
|
||||
this.area.mouseup(this._onMouseClickHandler.bind(this,
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
this._area.mouseup(this._onMouseClickHandler.bind(this,
|
||||
EVENT_TYPES.mouseup));
|
||||
this.area.dblclick(
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
this._area.dblclick(
|
||||
this._onMouseClickHandler.bind(this, EVENT_TYPES.mousedblclick));
|
||||
this.area.contextmenu(() => false);
|
||||
this.area[0].onmousewheel = event => {
|
||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
this._area.contextmenu(() => false);
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
this._area[0].onmousewheel = event => {
|
||||
this.sendRemoteControlEvent(this._controlledParticipant, {
|
||||
type: EVENT_TYPES.mousescroll,
|
||||
x: event.deltaX,
|
||||
y: event.deltaY
|
||||
|
@ -243,12 +294,14 @@ export default class Controller extends RemoteControlParticipant {
|
|||
* Stops processing the mouse and keyboard events. Removes added listeners.
|
||||
* Enables the keyboard shortcuts. Displays dialog to notify the user that
|
||||
* remote control session has ended.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_stop() {
|
||||
if(!this.controlledParticipant) {
|
||||
if (!this._controlledParticipant) {
|
||||
return;
|
||||
}
|
||||
logger.log("Stopping remote control controller.");
|
||||
logger.log('Stopping remote control controller.');
|
||||
APP.UI.removeListener(UIEvents.LARGE_VIDEO_ID_CHANGED,
|
||||
this._largeVideoChangedListener);
|
||||
APP.conference.removeConferenceListener(
|
||||
|
@ -256,29 +309,28 @@ export default class Controller extends RemoteControlParticipant {
|
|||
this._stopListener);
|
||||
APP.conference.removeConferenceListener(ConferenceEvents.USER_LEFT,
|
||||
this._userLeftListener);
|
||||
this.controlledParticipant = null;
|
||||
this._controlledParticipant = null;
|
||||
this.pause();
|
||||
this.area = null;
|
||||
this._area = undefined;
|
||||
APP.UI.messageHandler.openMessageDialog(
|
||||
"dialog.remoteControlTitle",
|
||||
"dialog.remoteControlStopMessage"
|
||||
'dialog.remoteControlTitle',
|
||||
'dialog.remoteControlStopMessage'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes this._stop() mehtod:
|
||||
* Stops processing the mouse and keyboard events. Removes added listeners.
|
||||
* Enables the keyboard shortcuts. Displays dialog to notify the user that
|
||||
* remote control session has ended.
|
||||
* Executes this._stop() mehtod which stops processing the mouse and
|
||||
* keyboard events, removes added listeners, enables the keyboard shortcuts,
|
||||
* displays dialog to notify the user that remote control session has ended.
|
||||
* In addition sends stop message to the controlled participant.
|
||||
*
|
||||
* In addition:
|
||||
* Sends stop message to the controlled participant.
|
||||
* @returns {void}
|
||||
*/
|
||||
stop() {
|
||||
if(!this.controlledParticipant) {
|
||||
if (!this._controlledParticipant) {
|
||||
return;
|
||||
}
|
||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
||||
this.sendRemoteControlEvent(this._controlledParticipant, {
|
||||
type: EVENT_TYPES.stop
|
||||
});
|
||||
this._stop();
|
||||
|
@ -288,88 +340,112 @@ export default class Controller extends RemoteControlParticipant {
|
|||
* Pauses the collecting of events and enables the keyboard shortcus. But
|
||||
* it doesn't removes any other listeners. Basically the remote control
|
||||
* session will be still active after this.pause(), but no events from the
|
||||
* controller side will be captured and sent.
|
||||
* controller side will be captured and sent. You can resume the collecting
|
||||
* of the events with this.resume().
|
||||
*
|
||||
* You can resume the collecting of the events with this.resume().
|
||||
* @returns {void}
|
||||
*/
|
||||
pause() {
|
||||
if(!this.controlledParticipant) {
|
||||
if (!this._controlledParticipant) {
|
||||
return;
|
||||
}
|
||||
logger.log("Pausing remote control controller.");
|
||||
this.isCollectingEvents = false;
|
||||
logger.log('Pausing remote control controller.');
|
||||
this._isCollectingEvents = false;
|
||||
APP.keyboardshortcut.enable(true);
|
||||
this.area.off( "mousemove" );
|
||||
this.area.off( "mousedown" );
|
||||
this.area.off( "mouseup" );
|
||||
this.area.off( "contextmenu" );
|
||||
this.area.off( "dblclick" );
|
||||
$(window).off( "keydown");
|
||||
$(window).off( "keyup");
|
||||
this.area[0].onmousewheel = undefined;
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
this._area.off('mousemove');
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
this._area.off('mousedown');
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
this._area.off('mouseup');
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
this._area.off('contextmenu');
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
this._area.off('dblclick');
|
||||
|
||||
$(window).off('keydown');
|
||||
$(window).off('keyup');
|
||||
|
||||
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||
this._area[0].onmousewheel = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for mouse click events.
|
||||
* @param {String} type the type of event ("mousedown"/"mouseup")
|
||||
* @param {Event} event the mouse event.
|
||||
*
|
||||
* @param {string} type - The type of event ("mousedown"/"mouseup").
|
||||
* @param {Event} event - The mouse event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onMouseClickHandler(type, event) {
|
||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
||||
type: type,
|
||||
_onMouseClickHandler(type: string, event: Object) {
|
||||
this.sendRemoteControlEvent(this._controlledParticipant, {
|
||||
type,
|
||||
button: event.which
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the remote control session is started.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isStarted() {
|
||||
return this.controlledParticipant !== null;
|
||||
return this._controlledParticipant !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the requested participant
|
||||
* @returns {string} this.requestedParticipant.
|
||||
* Returns the id of the requested participant.
|
||||
*
|
||||
* @returns {string} The id of the requested participant.
|
||||
* NOTE: This id should be the result of JitsiParticipant.getId() call.
|
||||
*/
|
||||
getRequestedParticipant() {
|
||||
return this.requestedParticipant;
|
||||
return this._requestedParticipant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for key press events.
|
||||
* @param {String} type the type of event ("keydown"/"keyup")
|
||||
* @param {Event} event the key event.
|
||||
*
|
||||
* @param {string} type - The type of event ("keydown"/"keyup").
|
||||
* @param {Event} event - The key event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onKeyPessHandler(type, event) {
|
||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
||||
type: type,
|
||||
_onKeyPessHandler(type: string, event: Object) {
|
||||
this.sendRemoteControlEvent(this._controlledParticipant, {
|
||||
type,
|
||||
key: getKey(event),
|
||||
modifiers: getModifiers(event),
|
||||
modifiers: getModifiers(event)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the stop method if the other side have left.
|
||||
* @param {string} id - the user id for the participant that have left
|
||||
*
|
||||
* @param {string} id - The user id for the participant that have left.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onUserLeft(id) {
|
||||
if(this.controlledParticipant === id) {
|
||||
_onUserLeft(id: string) {
|
||||
if (this._controlledParticipant === id) {
|
||||
this._stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes of the participant displayed on the large video.
|
||||
* @param {string} id - the user id for the participant that is displayed.
|
||||
*
|
||||
* @param {string} id - The user id for the participant that is displayed.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onLargeVideoIdChanged(id) {
|
||||
if (!this.controlledParticipant) {
|
||||
_onLargeVideoIdChanged(id: string) {
|
||||
if (!this._controlledParticipant) {
|
||||
return;
|
||||
}
|
||||
if(this.controlledParticipant == id) {
|
||||
if (this._controlledParticipant === id) {
|
||||
this.resume();
|
||||
} else {
|
||||
this.pause();
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
/* global APP, config, interfaceConfig, JitsiMeetJS */
|
||||
/* @flow */
|
||||
|
||||
import { getLogger } from 'jitsi-meet-logger';
|
||||
|
||||
import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
|
||||
import {
|
||||
openRemoteControlAuthorizationDialog
|
||||
} from '../../react/features/remote-control';
|
||||
import {
|
||||
DISCO_REMOTE_CONTROL_FEATURE,
|
||||
EVENT_TYPES,
|
||||
|
@ -11,8 +16,13 @@ import { getJitsiMeetTransport } from '../transport';
|
|||
|
||||
import RemoteControlParticipant from './RemoteControlParticipant';
|
||||
|
||||
declare var APP: Object;
|
||||
declare var config: Object;
|
||||
declare var interfaceConfig: Object;
|
||||
declare var JitsiMeetJS: Object;
|
||||
|
||||
const ConferenceEvents = JitsiMeetJS.events.conference;
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
const logger = getLogger(__filename);
|
||||
|
||||
/**
|
||||
* The transport instance used for communication with external apps.
|
||||
|
@ -28,17 +38,23 @@ const transport = getJitsiMeetTransport();
|
|||
* and executed.
|
||||
*/
|
||||
export default class Receiver extends RemoteControlParticipant {
|
||||
_controller: ?string;
|
||||
_enabled: boolean;
|
||||
_hangupListener: Function;
|
||||
_remoteControlEventsListener: Function;
|
||||
_userLeftListener: Function;
|
||||
|
||||
/**
|
||||
* Creates new instance.
|
||||
* @constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this.controller = null;
|
||||
this._controller = null;
|
||||
this._remoteControlEventsListener
|
||||
= this._onRemoteControlEvent.bind(this);
|
||||
this._userLeftListener = this._onUserLeft.bind(this);
|
||||
this._hangupListener = this._onHangup.bind(this);
|
||||
|
||||
// We expect here that even if we receive the supported event earlier
|
||||
// it will be cached and we'll receive it.
|
||||
transport.on('event', event => {
|
||||
|
@ -53,16 +69,19 @@ export default class Receiver extends RemoteControlParticipant {
|
|||
}
|
||||
|
||||
/**
|
||||
* Enables / Disables the remote control
|
||||
* @param {boolean} enabled the new state.
|
||||
* Enables / Disables the remote control.
|
||||
*
|
||||
* @param {boolean} enabled - The new state.
|
||||
* @returns {void}
|
||||
*/
|
||||
_enable(enabled) {
|
||||
if(this.enabled === enabled) {
|
||||
_enable(enabled: boolean) {
|
||||
if (this._enabled === enabled) {
|
||||
return;
|
||||
}
|
||||
this.enabled = enabled;
|
||||
if(enabled === true) {
|
||||
logger.log("Remote control receiver enabled.");
|
||||
this._enabled = enabled;
|
||||
if (enabled === true) {
|
||||
logger.log('Remote control receiver enabled.');
|
||||
|
||||
// Announce remote control support.
|
||||
APP.connection.addFeature(DISCO_REMOTE_CONTROL_FEATURE, true);
|
||||
APP.conference.addConferenceListener(
|
||||
|
@ -71,7 +90,7 @@ export default class Receiver extends RemoteControlParticipant {
|
|||
APP.conference.addListener(JitsiMeetConferenceEvents.BEFORE_HANGUP,
|
||||
this._hangupListener);
|
||||
} else {
|
||||
logger.log("Remote control receiver disabled.");
|
||||
logger.log('Remote control receiver disabled.');
|
||||
this._stop(true);
|
||||
APP.connection.removeFeature(DISCO_REMOTE_CONTROL_FEATURE);
|
||||
APP.conference.removeConferenceListener(
|
||||
|
@ -88,36 +107,43 @@ export default class Receiver extends RemoteControlParticipant {
|
|||
* events. Sends stop message to the wrapper application. Optionally
|
||||
* displays dialog for informing the user that remote control session
|
||||
* ended.
|
||||
* @param {boolean} dontShowDialog - if true the dialog won't be displayed.
|
||||
*
|
||||
* @param {boolean} [dontShowDialog] - If true the dialog won't be
|
||||
* displayed.
|
||||
* @returns {void}
|
||||
*/
|
||||
_stop(dontShowDialog = false) {
|
||||
if(!this.controller) {
|
||||
_stop(dontShowDialog: boolean = false) {
|
||||
if (!this._controller) {
|
||||
return;
|
||||
}
|
||||
logger.log("Remote control receiver stop.");
|
||||
this.controller = null;
|
||||
logger.log('Remote control receiver stop.');
|
||||
this._controller = null;
|
||||
APP.conference.removeConferenceListener(ConferenceEvents.USER_LEFT,
|
||||
this._userLeftListener);
|
||||
transport.sendEvent({
|
||||
name: REMOTE_CONTROL_EVENT_NAME,
|
||||
type: EVENT_TYPES.stop
|
||||
});
|
||||
if(!dontShowDialog) {
|
||||
if (this.remoteControlExternalAuth) {
|
||||
transport.sendEvent({
|
||||
name: REMOTE_CONTROL_EVENT_NAME,
|
||||
type: EVENT_TYPES.stop
|
||||
});
|
||||
}
|
||||
if (!dontShowDialog) {
|
||||
APP.UI.messageHandler.openMessageDialog(
|
||||
"dialog.remoteControlTitle",
|
||||
"dialog.remoteControlStopMessage"
|
||||
'dialog.remoteControlTitle',
|
||||
'dialog.remoteControlStopMessage'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls this._stop() and sends stop message to the controller participant
|
||||
* Calls this._stop() and sends stop message to the controller participant.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
stop() {
|
||||
if(!this.controller) {
|
||||
if (!this._controller) {
|
||||
return;
|
||||
}
|
||||
this._sendRemoteControlEvent(this.controller, {
|
||||
this.sendRemoteControlEvent(this._controller, {
|
||||
type: EVENT_TYPES.stop
|
||||
});
|
||||
this._stop();
|
||||
|
@ -127,91 +153,145 @@ export default class Receiver extends RemoteControlParticipant {
|
|||
* Listens for data channel EndpointMessage events. Handles only events of
|
||||
* type remote control. Sends "remote-control-event" events to the API
|
||||
* module.
|
||||
* @param {JitsiParticipant} participant the controller participant
|
||||
* @param {Object} event EndpointMessage event from the data channels.
|
||||
* @property {string} type property. The function process only events with
|
||||
* name REMOTE_CONTROL_EVENT_NAME
|
||||
* @property {RemoteControlEvent} event - the remote control event.
|
||||
*
|
||||
* @param {JitsiParticipant} participant - The controller participant.
|
||||
* @param {Object} event - EndpointMessage event from the data channels.
|
||||
* @param {string} event.name - The function process only events with
|
||||
* name REMOTE_CONTROL_EVENT_NAME.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRemoteControlEvent(participant, event) {
|
||||
_onRemoteControlEvent(participant: Object, event: Object) {
|
||||
if (event.name !== REMOTE_CONTROL_EVENT_NAME) {
|
||||
return;
|
||||
}
|
||||
|
||||
const remoteControlEvent = Object.assign({}, event);
|
||||
|
||||
if (this.enabled) {
|
||||
if (this.controller === null
|
||||
if (this._enabled) {
|
||||
if (this._controller === null
|
||||
&& event.type === EVENT_TYPES.permissions
|
||||
&& event.action === PERMISSIONS_ACTIONS.request) {
|
||||
const userId = participant.getId();
|
||||
|
||||
if (!config.remoteControlExternalAuth) {
|
||||
APP.store.dispatch(
|
||||
openRemoteControlAuthorizationDialog(userId));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Maybe use transport.sendRequest in this case???
|
||||
remoteControlEvent.userId = participant.getId();
|
||||
remoteControlEvent.userId = userId;
|
||||
remoteControlEvent.userJID = participant.getJid();
|
||||
remoteControlEvent.displayName = participant.getDisplayName()
|
||||
|| interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
||||
remoteControlEvent.screenSharing
|
||||
= APP.conference.isSharingScreen;
|
||||
} else if (this.controller !== participant.getId()) {
|
||||
} else if (this._controller !== participant.getId()) {
|
||||
return;
|
||||
} else if (event.type === EVENT_TYPES.stop) {
|
||||
this._stop();
|
||||
|
||||
return;
|
||||
}
|
||||
transport.sendEvent(remoteControlEvent);
|
||||
} else {
|
||||
logger.log("Remote control event is ignored because remote "
|
||||
+ "control is disabled", event);
|
||||
logger.log('Remote control event is ignored because remote '
|
||||
+ 'control is disabled', event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles remote control permission events.
|
||||
* @param {String} userId the user id of the participant related to the
|
||||
*
|
||||
* @param {string} userId - The user id of the participant related to the
|
||||
* event.
|
||||
* @param {PERMISSIONS_ACTIONS} action the action related to the event.
|
||||
* @param {PERMISSIONS_ACTIONS} action - The action related to the event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRemoteControlPermissionsEvent(userId, action) {
|
||||
if (action === PERMISSIONS_ACTIONS.grant) {
|
||||
APP.conference.addConferenceListener(ConferenceEvents.USER_LEFT,
|
||||
this._userLeftListener);
|
||||
this.controller = userId;
|
||||
logger.log("Remote control permissions granted to: " + userId);
|
||||
if(!APP.conference.isSharingScreen) {
|
||||
APP.conference.toggleScreenSharing();
|
||||
APP.conference.screenSharingPromise.then(() => {
|
||||
if(APP.conference.isSharingScreen) {
|
||||
this._sendRemoteControlEvent(userId, {
|
||||
type: EVENT_TYPES.permissions,
|
||||
action: action
|
||||
});
|
||||
} else {
|
||||
this._sendRemoteControlEvent(userId, {
|
||||
type: EVENT_TYPES.permissions,
|
||||
action: PERMISSIONS_ACTIONS.error
|
||||
});
|
||||
}
|
||||
}).catch(() => {
|
||||
this._sendRemoteControlEvent(userId, {
|
||||
type: EVENT_TYPES.permissions,
|
||||
action: PERMISSIONS_ACTIONS.error
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
_onRemoteControlPermissionsEvent(userId: string, action: string) {
|
||||
switch (action) {
|
||||
case PERMISSIONS_ACTIONS.grant:
|
||||
this.grant(userId);
|
||||
break;
|
||||
case PERMISSIONS_ACTIONS.deny:
|
||||
this.deny(userId);
|
||||
break;
|
||||
case PERMISSIONS_ACTIONS.error:
|
||||
this.sendRemoteControlEvent(userId, {
|
||||
type: EVENT_TYPES.permissions,
|
||||
action
|
||||
});
|
||||
break;
|
||||
default:
|
||||
|
||||
// Unknown action. Ignore.
|
||||
}
|
||||
this._sendRemoteControlEvent(userId, {
|
||||
}
|
||||
|
||||
/**
|
||||
* Denies remote control access for user associated with the passed user id.
|
||||
*
|
||||
* @param {string} userId - The id associated with the user who sent the
|
||||
* request for remote control authorization.
|
||||
* @returns {void}
|
||||
*/
|
||||
deny(userId: string) {
|
||||
this.sendRemoteControlEvent(userId, {
|
||||
type: EVENT_TYPES.permissions,
|
||||
action
|
||||
action: PERMISSIONS_ACTIONS.deny
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles remote control events from the external app. Currently only
|
||||
* events with type = EVENT_TYPES.supported or EVENT_TYPES.permissions
|
||||
* @param {RemoteControlEvent} event the remote control event.
|
||||
* Grants remote control access to user associated with the passed user id.
|
||||
*
|
||||
* @param {string} userId - The id associated with the user who sent the
|
||||
* request for remote control authorization.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRemoteControlAPIEvent(event) {
|
||||
switch(event.type) {
|
||||
grant(userId: string) {
|
||||
APP.conference.addConferenceListener(ConferenceEvents.USER_LEFT,
|
||||
this._userLeftListener);
|
||||
this._controller = userId;
|
||||
logger.log(`Remote control permissions granted to: ${userId}`);
|
||||
if (APP.conference.isSharingScreen) {
|
||||
this.sendRemoteControlEvent(userId, {
|
||||
type: EVENT_TYPES.permissions,
|
||||
action: PERMISSIONS_ACTIONS.grant
|
||||
});
|
||||
} else {
|
||||
APP.conference.toggleScreenSharing();
|
||||
APP.conference.screenSharingPromise.then(() => {
|
||||
if (APP.conference.isSharingScreen) {
|
||||
this.sendRemoteControlEvent(userId, {
|
||||
type: EVENT_TYPES.permissions,
|
||||
action: PERMISSIONS_ACTIONS.grant
|
||||
});
|
||||
} else {
|
||||
this.sendRemoteControlEvent(userId, {
|
||||
type: EVENT_TYPES.permissions,
|
||||
action: PERMISSIONS_ACTIONS.error
|
||||
});
|
||||
}
|
||||
}).catch(() => {
|
||||
this.sendRemoteControlEvent(userId, {
|
||||
type: EVENT_TYPES.permissions,
|
||||
action: PERMISSIONS_ACTIONS.error
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles remote control events from the external app. Currently only
|
||||
* events with type = EVENT_TYPES.supported or EVENT_TYPES.permissions.
|
||||
*
|
||||
* @param {RemoteControlEvent} event - The remote control event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRemoteControlAPIEvent(event: Object) {
|
||||
switch (event.type) {
|
||||
case EVENT_TYPES.permissions:
|
||||
this._onRemoteControlPermissionsEvent(event.userId, event.action);
|
||||
break;
|
||||
|
@ -224,11 +304,13 @@ export default class Receiver extends RemoteControlParticipant {
|
|||
/**
|
||||
* Handles events for support for executing remote control events into
|
||||
* the wrapper application.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRemoteControlSupported() {
|
||||
logger.log("Remote Control supported.");
|
||||
logger.log('Remote Control supported.');
|
||||
if (config.disableRemoteControl) {
|
||||
logger.log("Remote Control disabled.");
|
||||
logger.log('Remote Control disabled.');
|
||||
} else {
|
||||
this._enable(true);
|
||||
}
|
||||
|
@ -236,16 +318,20 @@ export default class Receiver extends RemoteControlParticipant {
|
|||
|
||||
/**
|
||||
* Calls the stop method if the other side have left.
|
||||
* @param {string} id - the user id for the participant that have left
|
||||
*
|
||||
* @param {string} id - The user id for the participant that have left.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onUserLeft(id) {
|
||||
if(this.controller === id) {
|
||||
_onUserLeft(id: string) {
|
||||
if (this._controller === id) {
|
||||
this._stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles hangup events. Disables the receiver.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onHangup() {
|
||||
this._enable(false);
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
/* global APP, config */
|
||||
/* @flow */
|
||||
|
||||
import { getLogger } from 'jitsi-meet-logger';
|
||||
|
||||
import { DISCO_REMOTE_CONTROL_FEATURE }
|
||||
from '../../service/remotecontrol/Constants';
|
||||
|
@ -6,44 +8,53 @@ import { DISCO_REMOTE_CONTROL_FEATURE }
|
|||
import Controller from './Controller';
|
||||
import Receiver from './Receiver';
|
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
const logger = getLogger(__filename);
|
||||
|
||||
declare var APP: Object;
|
||||
declare var config: Object;
|
||||
|
||||
/**
|
||||
* Implements the remote control functionality.
|
||||
*/
|
||||
class RemoteControl {
|
||||
_initialized: boolean;
|
||||
controller: Controller;
|
||||
receiver: Receiver;
|
||||
|
||||
/**
|
||||
* Constructs new instance. Creates controller and receiver properties.
|
||||
* @constructor
|
||||
*/
|
||||
constructor() {
|
||||
this.controller = new Controller();
|
||||
this.initialized = false;
|
||||
this._initialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the remote control - checks if the remote control should be
|
||||
* enabled or not.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
init() {
|
||||
if(config.disableRemoteControl
|
||||
|| this.initialized
|
||||
if (config.disableRemoteControl
|
||||
|| this._initialized
|
||||
|| !APP.conference.isDesktopSharingEnabled) {
|
||||
return;
|
||||
}
|
||||
logger.log("Initializing remote control.");
|
||||
this.initialized = true;
|
||||
logger.log('Initializing remote control.');
|
||||
this._initialized = true;
|
||||
this.controller.enable(true);
|
||||
this.receiver = new Receiver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the passed user supports remote control or not
|
||||
* @param {JitsiParticipant} user the user to be tested
|
||||
* @returns {Promise<boolean>} the promise will be resolved with true if
|
||||
* Checks whether the passed user supports remote control or not.
|
||||
*
|
||||
* @param {JitsiParticipant} user - The user to be tested.
|
||||
* @returns {Promise<boolean>} The promise will be resolved with true if
|
||||
* the user supports remote control and with false if not.
|
||||
*/
|
||||
checkUserRemoteControlSupport(user) {
|
||||
checkUserRemoteControlSupport(user: Object) {
|
||||
return user.getFeatures().then(
|
||||
features => features.has(DISCO_REMOTE_CONTROL_FEATURE),
|
||||
() => false);
|
||||
|
|
|
@ -1,50 +1,70 @@
|
|||
/* global APP */
|
||||
/* @flow */
|
||||
|
||||
import { getLogger } from 'jitsi-meet-logger';
|
||||
|
||||
import {
|
||||
REMOTE_CONTROL_EVENT_NAME
|
||||
} from "../../service/remotecontrol/Constants";
|
||||
} from '../../service/remotecontrol/Constants';
|
||||
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
const logger = getLogger(__filename);
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Implements common logic for Receiver class and Controller class.
|
||||
*/
|
||||
export default class RemoteControlParticipant {
|
||||
_enabled: boolean;
|
||||
|
||||
/**
|
||||
* Creates new instance.
|
||||
*/
|
||||
constructor() {
|
||||
this.enabled = false;
|
||||
this._enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables / Disables the remote control
|
||||
* @param {boolean} enabled the new state.
|
||||
* Enables / Disables the remote control.
|
||||
*
|
||||
* @param {boolean} enabled - The new state.
|
||||
* @returns {void}
|
||||
*/
|
||||
enable(enabled) {
|
||||
this.enabled = enabled;
|
||||
enable(enabled: boolean) {
|
||||
this._enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends remote control event to other participant trough data channel.
|
||||
* @param {RemoteControlEvent} event the remote control event.
|
||||
* @param {Function} onDataChannelFail handler for data channel failure.
|
||||
*
|
||||
* @param {string} to - The participant who will receive the event.
|
||||
* @param {RemoteControlEvent} event - The remote control event.
|
||||
* @param {Function} onDataChannelFail - Handler for data channel failure.
|
||||
* @returns {void}
|
||||
*/
|
||||
_sendRemoteControlEvent(to, event, onDataChannelFail = () => {}) {
|
||||
if(!this.enabled || !to) {
|
||||
sendRemoteControlEvent(
|
||||
to: ?string,
|
||||
event: Object,
|
||||
onDataChannelFail: ?Function) {
|
||||
if (!this._enabled || !to) {
|
||||
logger.warn(
|
||||
"Remote control: Skip sending remote control event. Params:",
|
||||
'Remote control: Skip sending remote control event. Params:',
|
||||
this.enable,
|
||||
to);
|
||||
|
||||
return;
|
||||
}
|
||||
try{
|
||||
try {
|
||||
APP.conference.sendEndpointMessage(to, {
|
||||
name: REMOTE_CONTROL_EVENT_NAME,
|
||||
...event
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
"Failed to send EndpointMessage via the datachannels",
|
||||
'Failed to send EndpointMessage via the datachannels',
|
||||
e);
|
||||
onDataChannelFail(e);
|
||||
if (typeof onDataChannelFail === 'function') {
|
||||
onDataChannelFail(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,8 @@ export class AbstractApp extends Component {
|
|||
localParticipant = {
|
||||
avatarID: APP.settings.getAvatarId(),
|
||||
avatarURL: APP.settings.getAvatarUrl(),
|
||||
email: APP.settings.getEmail()
|
||||
email: APP.settings.getEmail(),
|
||||
name: APP.settings.getDisplayName()
|
||||
};
|
||||
}
|
||||
dispatch(localParticipantJoined(localParticipant));
|
||||
|
|
|
@ -24,7 +24,7 @@ export default class AbstractDialog extends Component {
|
|||
cancelTitleKey: React.PropTypes.string,
|
||||
|
||||
/**
|
||||
* Used to show hide the dialog on cancel.
|
||||
* Used to show/hide the dialog on cancel.
|
||||
*/
|
||||
dispatch: React.PropTypes.func,
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { openDialog } from '../base/dialog';
|
||||
|
||||
import { RemoteControlAuthorizationDialog } from './components';
|
||||
|
||||
/**
|
||||
* Signals that the remote control authorization dialog should be displayed.
|
||||
*
|
||||
* @param {string} participantId - The id of the participant who is requesting
|
||||
* the authorization.
|
||||
* @returns {{
|
||||
* type: OPEN_DIALOG,
|
||||
* component: {RemoteControlAuthorizationDialog},
|
||||
* componentProps: {
|
||||
* participantId: {string}
|
||||
* }
|
||||
* }}
|
||||
* @public
|
||||
*/
|
||||
export function openRemoteControlAuthorizationDialog(participantId) {
|
||||
return openDialog(RemoteControlAuthorizationDialog, { participantId });
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import {
|
||||
Dialog,
|
||||
hideDialog
|
||||
} from '../../base/dialog';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { getParticipantById } from '../../base/participants';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Implements a dialog for remote control authorization.
|
||||
*/
|
||||
class RemoteControlAuthorizationDialog extends Component {
|
||||
/**
|
||||
* RemoteControlAuthorizationDialog component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* The display name of the participant who is requesting authorization
|
||||
* for remote desktop control session.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_displayName: React.PropTypes.string,
|
||||
|
||||
/**
|
||||
* Used to show/hide the dialog on cancel.
|
||||
*/
|
||||
dispatch: React.PropTypes.func,
|
||||
|
||||
/**
|
||||
* The ID of the participant who is requesting authorization for remote
|
||||
* desktop control session.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
participantId: React.PropTypes.string,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: React.PropTypes.func
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new RemoteControlAuthorizationDialog instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._onCancel = this._onCancel.bind(this);
|
||||
this._onSubmit = this._onSubmit.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Dialog
|
||||
okTitleKey = { 'dialog.allow' }
|
||||
onCancel = { this._onCancel }
|
||||
onSubmit = { this._onSubmit }
|
||||
titleKey = 'dialog.remoteControlTitle'
|
||||
width = 'small'>
|
||||
{
|
||||
this.props.t('dialog.remoteControlRequestMessage',
|
||||
{ user: this.props._displayName })
|
||||
}
|
||||
{
|
||||
this._getAdditionalMessage()
|
||||
}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders additional message text for the dialog.
|
||||
*
|
||||
* @private
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_getAdditionalMessage() {
|
||||
// FIXME: Once we have this information in redux we should
|
||||
// start getting it from there.
|
||||
if (APP.conference.isSharingScreen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<br />
|
||||
{ this.props.t('dialog.remoteControlShareScreenWarning') }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the remote control module about the denial of the remote control
|
||||
* request.
|
||||
*
|
||||
* @private
|
||||
* @returns {boolean} Returns true to close the dialog.
|
||||
*/
|
||||
_onCancel() {
|
||||
// FIXME: This should be action one day.
|
||||
APP.remoteControl.receiver.deny(this.props.participantId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the remote control module that the remote control request is
|
||||
* accepted.
|
||||
*
|
||||
* @private
|
||||
* @returns {boolean} Returns false to prevent closure because the dialog is
|
||||
* closed manually to be sure that if the desktop picker dialog can be
|
||||
* displayed (if this dialog is displayed when we try to display the desktop
|
||||
* picker window, the action will be ignored).
|
||||
*/
|
||||
_onSubmit() {
|
||||
this.props.dispatch(hideDialog());
|
||||
|
||||
// FIXME: This should be action one day.
|
||||
APP.remoteControl.receiver.grant(this.props.participantId);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the Redux state to the RemoteControlAuthorizationDialog's
|
||||
* props.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Object} ownProps - The React Component props passed to the associated
|
||||
* (instance of) RemoteControlAuthorizationDialog.
|
||||
* @private
|
||||
* @returns {{
|
||||
* _displayName: string
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
const { _displayName, participantId } = ownProps;
|
||||
const participant = getParticipantById(
|
||||
state['features/base/participants'], participantId);
|
||||
|
||||
return {
|
||||
_displayName: participant ? participant.name : _displayName
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(
|
||||
connect(_mapStateToProps)(RemoteControlAuthorizationDialog));
|
|
@ -0,0 +1,2 @@
|
|||
export { default as RemoteControlAuthorizationDialog }
|
||||
from './RemoteControlAuthorizationDialog';
|
|
@ -0,0 +1,2 @@
|
|||
export * from './actions';
|
||||
export * from './components';
|
Loading…
Reference in New Issue