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
|
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
|
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'
|
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\\)*\\$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\\)*\\$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\\)*\\$FlowFixedInNextDeploy
|
||||||
|
suppress_comment=\\(.\\|\n\\)*\\$FlowDisableNextLine
|
||||||
|
|
||||||
unsafe.enable_getters_and_setters=true
|
unsafe.enable_getters_and_setters=true
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ node_modules/
|
||||||
# supersedes JSHint.
|
# supersedes JSHint.
|
||||||
flow-typed/
|
flow-typed/
|
||||||
modules/API/
|
modules/API/
|
||||||
modules/remotecontrol/RemoteControlParticipant.js
|
modules/remotecontrol/
|
||||||
modules/transport/
|
modules/transport/
|
||||||
react/
|
react/
|
||||||
|
|
||||||
|
|
|
@ -1442,6 +1442,10 @@ export default {
|
||||||
room.on(ConferenceEvents.DISPLAY_NAME_CHANGED, (id, displayName) => {
|
room.on(ConferenceEvents.DISPLAY_NAME_CHANGED, (id, displayName) => {
|
||||||
const formattedDisplayName
|
const formattedDisplayName
|
||||||
= displayName.substr(0, MAX_DISPLAY_NAME_LENGTH);
|
= displayName.substr(0, MAX_DISPLAY_NAME_LENGTH);
|
||||||
|
APP.store.dispatch(participantUpdated({
|
||||||
|
id,
|
||||||
|
name: formattedDisplayName
|
||||||
|
}));
|
||||||
APP.API.notifyDisplayNameChanged(id, formattedDisplayName);
|
APP.API.notifyDisplayNameChanged(id, formattedDisplayName);
|
||||||
APP.UI.changeDisplayName(id, formattedDisplayName);
|
APP.UI.changeDisplayName(id, formattedDisplayName);
|
||||||
});
|
});
|
||||||
|
@ -2053,6 +2057,12 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
APP.store.dispatch(participantUpdated({
|
||||||
|
id: this.getMyUserId(),
|
||||||
|
local: true,
|
||||||
|
name: formattedNickname
|
||||||
|
}));
|
||||||
|
|
||||||
APP.settings.setDisplayName(formattedNickname);
|
APP.settings.setDisplayName(formattedNickname);
|
||||||
if (room) {
|
if (room) {
|
||||||
room.setDisplayName(formattedNickname);
|
room.setDisplayName(formattedNickname);
|
||||||
|
|
|
@ -212,6 +212,7 @@
|
||||||
},
|
},
|
||||||
"dialog": {
|
"dialog": {
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
|
"allow": "Allow",
|
||||||
"kickMessage": "Ouch! You have been kicked out of the meet!",
|
"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.",
|
"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",
|
"passwordErrorTitle": "Password Error",
|
||||||
|
@ -330,7 +331,9 @@
|
||||||
"muteParticipantTitle": "Mute this participant?",
|
"muteParticipantTitle": "Mute this participant?",
|
||||||
"muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
|
"muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
|
||||||
"muteParticipantButton": "Mute",
|
"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!",
|
"remoteControlDeniedMessage": "__user__ rejected your remote control request!",
|
||||||
"remoteControlAllowedMessage": "__user__ accepted your remote control request!",
|
"remoteControlAllowedMessage": "__user__ accepted your remote control request!",
|
||||||
"remoteControlErrorMessage": "An error occurred while trying to request remote control permissions from __user__!",
|
"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 */
|
/* @flow */
|
||||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
|
||||||
import * as KeyCodes from "../keycode/keycode";
|
import { getLogger } from 'jitsi-meet-logger';
|
||||||
|
|
||||||
|
import * as KeyCodes from '../keycode/keycode';
|
||||||
import {
|
import {
|
||||||
EVENT_TYPES,
|
EVENT_TYPES,
|
||||||
PERMISSIONS_ACTIONS,
|
PERMISSIONS_ACTIONS,
|
||||||
REMOTE_CONTROL_EVENT_NAME
|
REMOTE_CONTROL_EVENT_NAME
|
||||||
} from "../../service/remotecontrol/Constants";
|
} from '../../service/remotecontrol/Constants';
|
||||||
import RemoteControlParticipant from "./RemoteControlParticipant";
|
import UIEvents from '../../service/UI/UIEvents';
|
||||||
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 ConferenceEvents = JitsiMeetJS.events.conference;
|
||||||
|
const logger = getLogger(__filename);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the keyboard key from the keyboard event.
|
* 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) {
|
function getKey(event) {
|
||||||
return KeyCodes.keyboardEventToKey(event);
|
return KeyCodes.keyboardEventToKey(event);
|
||||||
|
@ -22,26 +31,28 @@ function getKey(event) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the modifiers from the keyboard 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) {
|
function getModifiers(event) {
|
||||||
let modifiers = [];
|
const modifiers = [];
|
||||||
if(event.shiftKey) {
|
|
||||||
modifiers.push("shift");
|
if (event.shiftKey) {
|
||||||
|
modifiers.push('shift');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event.ctrlKey) {
|
if (event.ctrlKey) {
|
||||||
modifiers.push("control");
|
modifiers.push('control');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(event.altKey) {
|
if (event.altKey) {
|
||||||
modifiers.push("alt");
|
modifiers.push('alt');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event.metaKey) {
|
if (event.metaKey) {
|
||||||
modifiers.push("command");
|
modifiers.push('command');
|
||||||
}
|
}
|
||||||
|
|
||||||
return modifiers;
|
return modifiers;
|
||||||
|
@ -53,14 +64,22 @@ function getModifiers(event) {
|
||||||
* party of the remote control session.
|
* party of the remote control session.
|
||||||
*/
|
*/
|
||||||
export default class Controller extends RemoteControlParticipant {
|
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.
|
* Creates new instance.
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.isCollectingEvents = false;
|
this._isCollectingEvents = false;
|
||||||
this.controlledParticipant = null;
|
this._controlledParticipant = null;
|
||||||
this.requestedParticipant = null;
|
this._requestedParticipant = null;
|
||||||
this._stopListener = this._handleRemoteControlStoppedEvent.bind(this);
|
this._stopListener = this._handleRemoteControlStoppedEvent.bind(this);
|
||||||
this._userLeftListener = this._onUserLeft.bind(this);
|
this._userLeftListener = this._onUserLeft.bind(this);
|
||||||
this._largeVideoChangedListener
|
this._largeVideoChangedListener
|
||||||
|
@ -69,24 +88,28 @@ export default class Controller extends RemoteControlParticipant {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests permissions from the remote control receiver side.
|
* 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.
|
* 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.
|
* used mouse and keyboard event capture.
|
||||||
* @returns {Promise<boolean>} - resolve values:
|
* @returns {Promise<boolean>} Resolve values - true(accept), false(deny),
|
||||||
* true - accept
|
* null(the participant has left).
|
||||||
* false - deny
|
|
||||||
* null - the participant has left.
|
|
||||||
*/
|
*/
|
||||||
requestPermissions(userId, eventCaptureArea) {
|
requestPermissions(userId: string, eventCaptureArea: Object) {
|
||||||
if(!this.enabled) {
|
if (!this.enabled) {
|
||||||
return Promise.reject(new Error("Remote control is disabled!"));
|
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) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
|
let onUserLeft, permissionsReplyListener;
|
||||||
|
|
||||||
const clearRequest = () => {
|
const clearRequest = () => {
|
||||||
this.requestedParticipant = null;
|
this._requestedParticipant = null;
|
||||||
APP.conference.removeConferenceListener(
|
APP.conference.removeConferenceListener(
|
||||||
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||||
permissionsReplyListener);
|
permissionsReplyListener);
|
||||||
|
@ -94,31 +117,35 @@ export default class Controller extends RemoteControlParticipant {
|
||||||
ConferenceEvents.USER_LEFT,
|
ConferenceEvents.USER_LEFT,
|
||||||
onUserLeft);
|
onUserLeft);
|
||||||
};
|
};
|
||||||
const permissionsReplyListener = (participant, event) => {
|
|
||||||
|
permissionsReplyListener = (participant, event) => {
|
||||||
let result = null;
|
let result = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = this._handleReply(participant, event);
|
result = this._handleReply(participant, event);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
clearRequest();
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
if(result !== null) {
|
if (result !== null) {
|
||||||
clearRequest();
|
clearRequest();
|
||||||
resolve(result);
|
resolve(result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onUserLeft = (id) => {
|
onUserLeft = id => {
|
||||||
if(id === this.requestedParticipant) {
|
if (id === this._requestedParticipant) {
|
||||||
clearRequest();
|
clearRequest();
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
APP.conference.addConferenceListener(
|
APP.conference.addConferenceListener(
|
||||||
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||||
permissionsReplyListener);
|
permissionsReplyListener);
|
||||||
APP.conference.addConferenceListener(ConferenceEvents.USER_LEFT,
|
APP.conference.addConferenceListener(ConferenceEvents.USER_LEFT,
|
||||||
onUserLeft);
|
onUserLeft);
|
||||||
this.requestedParticipant = userId;
|
this._requestedParticipant = userId;
|
||||||
this._sendRemoteControlEvent(userId, {
|
this.sendRemoteControlEvent(userId, {
|
||||||
type: EVENT_TYPES.permissions,
|
type: EVENT_TYPES.permissions,
|
||||||
action: PERMISSIONS_ACTIONS.request
|
action: PERMISSIONS_ACTIONS.request
|
||||||
}, e => {
|
}, e => {
|
||||||
|
@ -130,54 +157,58 @@ export default class Controller extends RemoteControlParticipant {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the reply of the permissions request.
|
* Handles the reply of the permissions request.
|
||||||
* @param {JitsiParticipant} participant the participant that has sent the
|
*
|
||||||
* reply
|
* @param {JitsiParticipant} participant - The participant that has sent the
|
||||||
* @param {RemoteControlEvent} event the remote control event.
|
* reply.
|
||||||
|
* @param {RemoteControlEvent} event - The remote control event.
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_handleReply(participant, event) {
|
_handleReply(participant: Object, event: Object) {
|
||||||
const userId = participant.getId();
|
const userId = participant.getId();
|
||||||
if(this.enabled
|
|
||||||
|
if (this.enabled
|
||||||
&& event.name === REMOTE_CONTROL_EVENT_NAME
|
&& event.name === REMOTE_CONTROL_EVENT_NAME
|
||||||
&& event.type === EVENT_TYPES.permissions
|
&& event.type === EVENT_TYPES.permissions
|
||||||
&& userId === this.requestedParticipant) {
|
&& userId === this._requestedParticipant) {
|
||||||
if(event.action !== PERMISSIONS_ACTIONS.grant) {
|
if (event.action !== PERMISSIONS_ACTIONS.grant) {
|
||||||
this.area = null;
|
this._area = undefined;
|
||||||
}
|
}
|
||||||
switch(event.action) {
|
switch (event.action) {
|
||||||
case PERMISSIONS_ACTIONS.grant: {
|
case PERMISSIONS_ACTIONS.grant: {
|
||||||
this.controlledParticipant = userId;
|
this._controlledParticipant = userId;
|
||||||
logger.log("Remote control permissions granted to: "
|
logger.log('Remote control permissions granted to:', userId);
|
||||||
+ userId);
|
this._start();
|
||||||
this._start();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case PERMISSIONS_ACTIONS.deny:
|
case PERMISSIONS_ACTIONS.deny:
|
||||||
return false;
|
return false;
|
||||||
case PERMISSIONS_ACTIONS.error:
|
case PERMISSIONS_ACTIONS.error:
|
||||||
throw new Error("Error occurred on receiver side");
|
throw new Error('Error occurred on receiver side');
|
||||||
default:
|
default:
|
||||||
throw new Error("Unknown reply received!");
|
throw new Error('Unknown reply received!');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//different message type or another user -> ignoring the message
|
// different message type or another user -> ignoring the message
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles remote control stopped.
|
* Handles remote control stopped.
|
||||||
* @param {JitsiParticipant} participant the participant that has sent the
|
*
|
||||||
* event
|
* @param {JitsiParticipant} participant - The participant that has sent the
|
||||||
* @param {Object} event EndpointMessage event from the data channels.
|
* event.
|
||||||
* @property {string} type property. The function process only events with
|
* @param {Object} event - EndpointMessage event from the data channels.
|
||||||
* name REMOTE_CONTROL_EVENT_NAME
|
* @property {string} type - The function process only events with
|
||||||
* @property {RemoteControlEvent} event - the remote control event.
|
* name REMOTE_CONTROL_EVENT_NAME.
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_handleRemoteControlStoppedEvent(participant, event) {
|
_handleRemoteControlStoppedEvent(participant: Object, event: Object) {
|
||||||
if(this.enabled
|
if (this.enabled
|
||||||
&& event.name === REMOTE_CONTROL_EVENT_NAME
|
&& event.name === REMOTE_CONTROL_EVENT_NAME
|
||||||
&& event.type === EVENT_TYPES.stop
|
&& event.type === EVENT_TYPES.stop
|
||||||
&& participant.getId() === this.controlledParticipant) {
|
&& participant.getId() === this._controlledParticipant) {
|
||||||
this._stop();
|
this._stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,9 +216,11 @@ export default class Controller extends RemoteControlParticipant {
|
||||||
/**
|
/**
|
||||||
* Starts processing the mouse and keyboard events. Sets conference
|
* Starts processing the mouse and keyboard events. Sets conference
|
||||||
* listeners. Disables keyboard events.
|
* listeners. Disables keyboard events.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_start() {
|
_start() {
|
||||||
logger.log("Starting remote control controller.");
|
logger.log('Starting remote control controller.');
|
||||||
APP.UI.addListener(UIEvents.LARGE_VIDEO_ID_CHANGED,
|
APP.UI.addListener(UIEvents.LARGE_VIDEO_ID_CHANGED,
|
||||||
this._largeVideoChangedListener);
|
this._largeVideoChangedListener);
|
||||||
APP.conference.addConferenceListener(
|
APP.conference.addConferenceListener(
|
||||||
|
@ -200,35 +233,53 @@ export default class Controller extends RemoteControlParticipant {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables the keyboatd shortcuts. Starts collecting remote control
|
* 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
|
* @returns {void}
|
||||||
* paused with this.pause().
|
|
||||||
*/
|
*/
|
||||||
resume() {
|
resume() {
|
||||||
if(!this.enabled || this.isCollectingEvents) {
|
if (!this.enabled || this._isCollectingEvents || !this._area) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.log("Resuming remote control controller.");
|
logger.log('Resuming remote control controller.');
|
||||||
this.isCollectingEvents = true;
|
this._isCollectingEvents = true;
|
||||||
APP.keyboardshortcut.enable(false);
|
APP.keyboardshortcut.enable(false);
|
||||||
this.area.mousemove(event => {
|
|
||||||
const position = this.area.position();
|
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
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,
|
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));
|
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));
|
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._onMouseClickHandler.bind(this, EVENT_TYPES.mousedblclick));
|
||||||
this.area.contextmenu(() => false);
|
|
||||||
this.area[0].onmousewheel = event => {
|
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
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,
|
type: EVENT_TYPES.mousescroll,
|
||||||
x: event.deltaX,
|
x: event.deltaX,
|
||||||
y: event.deltaY
|
y: event.deltaY
|
||||||
|
@ -243,12 +294,14 @@ export default class Controller extends RemoteControlParticipant {
|
||||||
* Stops processing the mouse and keyboard events. Removes added listeners.
|
* Stops processing the mouse and keyboard events. Removes added listeners.
|
||||||
* Enables the keyboard shortcuts. Displays dialog to notify the user that
|
* Enables the keyboard shortcuts. Displays dialog to notify the user that
|
||||||
* remote control session has ended.
|
* remote control session has ended.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_stop() {
|
_stop() {
|
||||||
if(!this.controlledParticipant) {
|
if (!this._controlledParticipant) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.log("Stopping remote control controller.");
|
logger.log('Stopping remote control controller.');
|
||||||
APP.UI.removeListener(UIEvents.LARGE_VIDEO_ID_CHANGED,
|
APP.UI.removeListener(UIEvents.LARGE_VIDEO_ID_CHANGED,
|
||||||
this._largeVideoChangedListener);
|
this._largeVideoChangedListener);
|
||||||
APP.conference.removeConferenceListener(
|
APP.conference.removeConferenceListener(
|
||||||
|
@ -256,29 +309,28 @@ export default class Controller extends RemoteControlParticipant {
|
||||||
this._stopListener);
|
this._stopListener);
|
||||||
APP.conference.removeConferenceListener(ConferenceEvents.USER_LEFT,
|
APP.conference.removeConferenceListener(ConferenceEvents.USER_LEFT,
|
||||||
this._userLeftListener);
|
this._userLeftListener);
|
||||||
this.controlledParticipant = null;
|
this._controlledParticipant = null;
|
||||||
this.pause();
|
this.pause();
|
||||||
this.area = null;
|
this._area = undefined;
|
||||||
APP.UI.messageHandler.openMessageDialog(
|
APP.UI.messageHandler.openMessageDialog(
|
||||||
"dialog.remoteControlTitle",
|
'dialog.remoteControlTitle',
|
||||||
"dialog.remoteControlStopMessage"
|
'dialog.remoteControlStopMessage'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes this._stop() mehtod:
|
* Executes this._stop() mehtod which stops processing the mouse and
|
||||||
* Stops processing the mouse and keyboard events. Removes added listeners.
|
* keyboard events, removes added listeners, enables the keyboard shortcuts,
|
||||||
* Enables the keyboard shortcuts. Displays dialog to notify the user that
|
* displays dialog to notify the user that remote control session has ended.
|
||||||
* remote control session has ended.
|
* In addition sends stop message to the controlled participant.
|
||||||
*
|
*
|
||||||
* In addition:
|
* @returns {void}
|
||||||
* Sends stop message to the controlled participant.
|
|
||||||
*/
|
*/
|
||||||
stop() {
|
stop() {
|
||||||
if(!this.controlledParticipant) {
|
if (!this._controlledParticipant) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
this.sendRemoteControlEvent(this._controlledParticipant, {
|
||||||
type: EVENT_TYPES.stop
|
type: EVENT_TYPES.stop
|
||||||
});
|
});
|
||||||
this._stop();
|
this._stop();
|
||||||
|
@ -288,88 +340,112 @@ export default class Controller extends RemoteControlParticipant {
|
||||||
* Pauses the collecting of events and enables the keyboard shortcus. But
|
* Pauses the collecting of events and enables the keyboard shortcus. But
|
||||||
* it doesn't removes any other listeners. Basically the remote control
|
* it doesn't removes any other listeners. Basically the remote control
|
||||||
* session will be still active after this.pause(), but no events from the
|
* 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() {
|
pause() {
|
||||||
if(!this.controlledParticipant) {
|
if (!this._controlledParticipant) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.log("Pausing remote control controller.");
|
logger.log('Pausing remote control controller.');
|
||||||
this.isCollectingEvents = false;
|
this._isCollectingEvents = false;
|
||||||
APP.keyboardshortcut.enable(true);
|
APP.keyboardshortcut.enable(true);
|
||||||
this.area.off( "mousemove" );
|
|
||||||
this.area.off( "mousedown" );
|
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||||
this.area.off( "mouseup" );
|
this._area.off('mousemove');
|
||||||
this.area.off( "contextmenu" );
|
|
||||||
this.area.off( "dblclick" );
|
// $FlowDisableNextLine: we are sure that this._area is not null.
|
||||||
$(window).off( "keydown");
|
this._area.off('mousedown');
|
||||||
$(window).off( "keyup");
|
|
||||||
this.area[0].onmousewheel = undefined;
|
// $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.
|
* 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) {
|
_onMouseClickHandler(type: string, event: Object) {
|
||||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
this.sendRemoteControlEvent(this._controlledParticipant, {
|
||||||
type: type,
|
type,
|
||||||
button: event.which
|
button: event.which
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the remote control session is started.
|
* Returns true if the remote control session is started.
|
||||||
|
*
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
isStarted() {
|
isStarted() {
|
||||||
return this.controlledParticipant !== null;
|
return this._controlledParticipant !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the id of the requested participant
|
* Returns the id of the requested participant.
|
||||||
* @returns {string} this.requestedParticipant.
|
*
|
||||||
|
* @returns {string} The id of the requested participant.
|
||||||
* NOTE: This id should be the result of JitsiParticipant.getId() call.
|
* NOTE: This id should be the result of JitsiParticipant.getId() call.
|
||||||
*/
|
*/
|
||||||
getRequestedParticipant() {
|
getRequestedParticipant() {
|
||||||
return this.requestedParticipant;
|
return this._requestedParticipant;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for key press events.
|
* 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) {
|
_onKeyPessHandler(type: string, event: Object) {
|
||||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
this.sendRemoteControlEvent(this._controlledParticipant, {
|
||||||
type: type,
|
type,
|
||||||
key: getKey(event),
|
key: getKey(event),
|
||||||
modifiers: getModifiers(event),
|
modifiers: getModifiers(event)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls the stop method if the other side have left.
|
* 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) {
|
_onUserLeft(id: string) {
|
||||||
if(this.controlledParticipant === id) {
|
if (this._controlledParticipant === id) {
|
||||||
this._stop();
|
this._stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles changes of the participant displayed on the large video.
|
* 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) {
|
_onLargeVideoIdChanged(id: string) {
|
||||||
if (!this.controlledParticipant) {
|
if (!this._controlledParticipant) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(this.controlledParticipant == id) {
|
if (this._controlledParticipant === id) {
|
||||||
this.resume();
|
this.resume();
|
||||||
} else {
|
} else {
|
||||||
this.pause();
|
this.pause();
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
/* global APP, config, interfaceConfig, JitsiMeetJS */
|
/* @flow */
|
||||||
|
|
||||||
|
import { getLogger } from 'jitsi-meet-logger';
|
||||||
|
|
||||||
import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
|
import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
|
||||||
|
import {
|
||||||
|
openRemoteControlAuthorizationDialog
|
||||||
|
} from '../../react/features/remote-control';
|
||||||
import {
|
import {
|
||||||
DISCO_REMOTE_CONTROL_FEATURE,
|
DISCO_REMOTE_CONTROL_FEATURE,
|
||||||
EVENT_TYPES,
|
EVENT_TYPES,
|
||||||
|
@ -11,8 +16,13 @@ import { getJitsiMeetTransport } from '../transport';
|
||||||
|
|
||||||
import RemoteControlParticipant from './RemoteControlParticipant';
|
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 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.
|
* The transport instance used for communication with external apps.
|
||||||
|
@ -28,17 +38,23 @@ const transport = getJitsiMeetTransport();
|
||||||
* and executed.
|
* and executed.
|
||||||
*/
|
*/
|
||||||
export default class Receiver extends RemoteControlParticipant {
|
export default class Receiver extends RemoteControlParticipant {
|
||||||
|
_controller: ?string;
|
||||||
|
_enabled: boolean;
|
||||||
|
_hangupListener: Function;
|
||||||
|
_remoteControlEventsListener: Function;
|
||||||
|
_userLeftListener: Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance.
|
* Creates new instance.
|
||||||
* @constructor
|
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.controller = null;
|
this._controller = null;
|
||||||
this._remoteControlEventsListener
|
this._remoteControlEventsListener
|
||||||
= this._onRemoteControlEvent.bind(this);
|
= this._onRemoteControlEvent.bind(this);
|
||||||
this._userLeftListener = this._onUserLeft.bind(this);
|
this._userLeftListener = this._onUserLeft.bind(this);
|
||||||
this._hangupListener = this._onHangup.bind(this);
|
this._hangupListener = this._onHangup.bind(this);
|
||||||
|
|
||||||
// We expect here that even if we receive the supported event earlier
|
// We expect here that even if we receive the supported event earlier
|
||||||
// it will be cached and we'll receive it.
|
// it will be cached and we'll receive it.
|
||||||
transport.on('event', event => {
|
transport.on('event', event => {
|
||||||
|
@ -53,16 +69,19 @@ export default class Receiver extends RemoteControlParticipant {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables / Disables the remote control
|
* Enables / Disables the remote control.
|
||||||
* @param {boolean} enabled the new state.
|
*
|
||||||
|
* @param {boolean} enabled - The new state.
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_enable(enabled) {
|
_enable(enabled: boolean) {
|
||||||
if(this.enabled === enabled) {
|
if (this._enabled === enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.enabled = enabled;
|
this._enabled = enabled;
|
||||||
if(enabled === true) {
|
if (enabled === true) {
|
||||||
logger.log("Remote control receiver enabled.");
|
logger.log('Remote control receiver enabled.');
|
||||||
|
|
||||||
// Announce remote control support.
|
// Announce remote control support.
|
||||||
APP.connection.addFeature(DISCO_REMOTE_CONTROL_FEATURE, true);
|
APP.connection.addFeature(DISCO_REMOTE_CONTROL_FEATURE, true);
|
||||||
APP.conference.addConferenceListener(
|
APP.conference.addConferenceListener(
|
||||||
|
@ -71,7 +90,7 @@ export default class Receiver extends RemoteControlParticipant {
|
||||||
APP.conference.addListener(JitsiMeetConferenceEvents.BEFORE_HANGUP,
|
APP.conference.addListener(JitsiMeetConferenceEvents.BEFORE_HANGUP,
|
||||||
this._hangupListener);
|
this._hangupListener);
|
||||||
} else {
|
} else {
|
||||||
logger.log("Remote control receiver disabled.");
|
logger.log('Remote control receiver disabled.');
|
||||||
this._stop(true);
|
this._stop(true);
|
||||||
APP.connection.removeFeature(DISCO_REMOTE_CONTROL_FEATURE);
|
APP.connection.removeFeature(DISCO_REMOTE_CONTROL_FEATURE);
|
||||||
APP.conference.removeConferenceListener(
|
APP.conference.removeConferenceListener(
|
||||||
|
@ -88,36 +107,43 @@ export default class Receiver extends RemoteControlParticipant {
|
||||||
* events. Sends stop message to the wrapper application. Optionally
|
* events. Sends stop message to the wrapper application. Optionally
|
||||||
* displays dialog for informing the user that remote control session
|
* displays dialog for informing the user that remote control session
|
||||||
* ended.
|
* 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) {
|
_stop(dontShowDialog: boolean = false) {
|
||||||
if(!this.controller) {
|
if (!this._controller) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.log("Remote control receiver stop.");
|
logger.log('Remote control receiver stop.');
|
||||||
this.controller = null;
|
this._controller = null;
|
||||||
APP.conference.removeConferenceListener(ConferenceEvents.USER_LEFT,
|
APP.conference.removeConferenceListener(ConferenceEvents.USER_LEFT,
|
||||||
this._userLeftListener);
|
this._userLeftListener);
|
||||||
transport.sendEvent({
|
if (this.remoteControlExternalAuth) {
|
||||||
name: REMOTE_CONTROL_EVENT_NAME,
|
transport.sendEvent({
|
||||||
type: EVENT_TYPES.stop
|
name: REMOTE_CONTROL_EVENT_NAME,
|
||||||
});
|
type: EVENT_TYPES.stop
|
||||||
if(!dontShowDialog) {
|
});
|
||||||
|
}
|
||||||
|
if (!dontShowDialog) {
|
||||||
APP.UI.messageHandler.openMessageDialog(
|
APP.UI.messageHandler.openMessageDialog(
|
||||||
"dialog.remoteControlTitle",
|
'dialog.remoteControlTitle',
|
||||||
"dialog.remoteControlStopMessage"
|
'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() {
|
stop() {
|
||||||
if(!this.controller) {
|
if (!this._controller) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._sendRemoteControlEvent(this.controller, {
|
this.sendRemoteControlEvent(this._controller, {
|
||||||
type: EVENT_TYPES.stop
|
type: EVENT_TYPES.stop
|
||||||
});
|
});
|
||||||
this._stop();
|
this._stop();
|
||||||
|
@ -127,91 +153,145 @@ export default class Receiver extends RemoteControlParticipant {
|
||||||
* Listens for data channel EndpointMessage events. Handles only events of
|
* Listens for data channel EndpointMessage events. Handles only events of
|
||||||
* type remote control. Sends "remote-control-event" events to the API
|
* type remote control. Sends "remote-control-event" events to the API
|
||||||
* module.
|
* module.
|
||||||
* @param {JitsiParticipant} participant the controller participant
|
*
|
||||||
* @param {Object} event EndpointMessage event from the data channels.
|
* @param {JitsiParticipant} participant - The controller participant.
|
||||||
* @property {string} type property. The function process only events with
|
* @param {Object} event - EndpointMessage event from the data channels.
|
||||||
* name REMOTE_CONTROL_EVENT_NAME
|
* @param {string} event.name - The function process only events with
|
||||||
* @property {RemoteControlEvent} event - the remote control event.
|
* name REMOTE_CONTROL_EVENT_NAME.
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onRemoteControlEvent(participant, event) {
|
_onRemoteControlEvent(participant: Object, event: Object) {
|
||||||
if (event.name !== REMOTE_CONTROL_EVENT_NAME) {
|
if (event.name !== REMOTE_CONTROL_EVENT_NAME) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const remoteControlEvent = Object.assign({}, event);
|
const remoteControlEvent = Object.assign({}, event);
|
||||||
|
|
||||||
if (this.enabled) {
|
if (this._enabled) {
|
||||||
if (this.controller === null
|
if (this._controller === null
|
||||||
&& event.type === EVENT_TYPES.permissions
|
&& event.type === EVENT_TYPES.permissions
|
||||||
&& event.action === PERMISSIONS_ACTIONS.request) {
|
&& 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???
|
// FIXME: Maybe use transport.sendRequest in this case???
|
||||||
remoteControlEvent.userId = participant.getId();
|
remoteControlEvent.userId = userId;
|
||||||
remoteControlEvent.userJID = participant.getJid();
|
remoteControlEvent.userJID = participant.getJid();
|
||||||
remoteControlEvent.displayName = participant.getDisplayName()
|
remoteControlEvent.displayName = participant.getDisplayName()
|
||||||
|| interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
|| interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
||||||
remoteControlEvent.screenSharing
|
remoteControlEvent.screenSharing
|
||||||
= APP.conference.isSharingScreen;
|
= APP.conference.isSharingScreen;
|
||||||
} else if (this.controller !== participant.getId()) {
|
} else if (this._controller !== participant.getId()) {
|
||||||
return;
|
return;
|
||||||
} else if (event.type === EVENT_TYPES.stop) {
|
} else if (event.type === EVENT_TYPES.stop) {
|
||||||
this._stop();
|
this._stop();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
transport.sendEvent(remoteControlEvent);
|
transport.sendEvent(remoteControlEvent);
|
||||||
} else {
|
} else {
|
||||||
logger.log("Remote control event is ignored because remote "
|
logger.log('Remote control event is ignored because remote '
|
||||||
+ "control is disabled", event);
|
+ 'control is disabled', event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles remote control permission events.
|
* 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.
|
* 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) {
|
_onRemoteControlPermissionsEvent(userId: string, action: string) {
|
||||||
if (action === PERMISSIONS_ACTIONS.grant) {
|
switch (action) {
|
||||||
APP.conference.addConferenceListener(ConferenceEvents.USER_LEFT,
|
case PERMISSIONS_ACTIONS.grant:
|
||||||
this._userLeftListener);
|
this.grant(userId);
|
||||||
this.controller = userId;
|
break;
|
||||||
logger.log("Remote control permissions granted to: " + userId);
|
case PERMISSIONS_ACTIONS.deny:
|
||||||
if(!APP.conference.isSharingScreen) {
|
this.deny(userId);
|
||||||
APP.conference.toggleScreenSharing();
|
break;
|
||||||
APP.conference.screenSharingPromise.then(() => {
|
case PERMISSIONS_ACTIONS.error:
|
||||||
if(APP.conference.isSharingScreen) {
|
this.sendRemoteControlEvent(userId, {
|
||||||
this._sendRemoteControlEvent(userId, {
|
type: EVENT_TYPES.permissions,
|
||||||
type: EVENT_TYPES.permissions,
|
action
|
||||||
action: action
|
});
|
||||||
});
|
break;
|
||||||
} else {
|
default:
|
||||||
this._sendRemoteControlEvent(userId, {
|
|
||||||
type: EVENT_TYPES.permissions,
|
// Unknown action. Ignore.
|
||||||
action: PERMISSIONS_ACTIONS.error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
this._sendRemoteControlEvent(userId, {
|
|
||||||
type: EVENT_TYPES.permissions,
|
|
||||||
action: PERMISSIONS_ACTIONS.error
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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,
|
type: EVENT_TYPES.permissions,
|
||||||
action
|
action: PERMISSIONS_ACTIONS.deny
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles remote control events from the external app. Currently only
|
* Grants remote control access to user associated with the passed user id.
|
||||||
* events with type = EVENT_TYPES.supported or EVENT_TYPES.permissions
|
*
|
||||||
* @param {RemoteControlEvent} event the remote control event.
|
* @param {string} userId - The id associated with the user who sent the
|
||||||
|
* request for remote control authorization.
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onRemoteControlAPIEvent(event) {
|
grant(userId: string) {
|
||||||
switch(event.type) {
|
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:
|
case EVENT_TYPES.permissions:
|
||||||
this._onRemoteControlPermissionsEvent(event.userId, event.action);
|
this._onRemoteControlPermissionsEvent(event.userId, event.action);
|
||||||
break;
|
break;
|
||||||
|
@ -224,11 +304,13 @@ export default class Receiver extends RemoteControlParticipant {
|
||||||
/**
|
/**
|
||||||
* Handles events for support for executing remote control events into
|
* Handles events for support for executing remote control events into
|
||||||
* the wrapper application.
|
* the wrapper application.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onRemoteControlSupported() {
|
_onRemoteControlSupported() {
|
||||||
logger.log("Remote Control supported.");
|
logger.log('Remote Control supported.');
|
||||||
if (config.disableRemoteControl) {
|
if (config.disableRemoteControl) {
|
||||||
logger.log("Remote Control disabled.");
|
logger.log('Remote Control disabled.');
|
||||||
} else {
|
} else {
|
||||||
this._enable(true);
|
this._enable(true);
|
||||||
}
|
}
|
||||||
|
@ -236,16 +318,20 @@ export default class Receiver extends RemoteControlParticipant {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls the stop method if the other side have left.
|
* 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) {
|
_onUserLeft(id: string) {
|
||||||
if(this.controller === id) {
|
if (this._controller === id) {
|
||||||
this._stop();
|
this._stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles hangup events. Disables the receiver.
|
* Handles hangup events. Disables the receiver.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onHangup() {
|
_onHangup() {
|
||||||
this._enable(false);
|
this._enable(false);
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
/* global APP, config */
|
/* @flow */
|
||||||
|
|
||||||
|
import { getLogger } from 'jitsi-meet-logger';
|
||||||
|
|
||||||
import { DISCO_REMOTE_CONTROL_FEATURE }
|
import { DISCO_REMOTE_CONTROL_FEATURE }
|
||||||
from '../../service/remotecontrol/Constants';
|
from '../../service/remotecontrol/Constants';
|
||||||
|
@ -6,44 +8,53 @@ import { DISCO_REMOTE_CONTROL_FEATURE }
|
||||||
import Controller from './Controller';
|
import Controller from './Controller';
|
||||||
import Receiver from './Receiver';
|
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.
|
* Implements the remote control functionality.
|
||||||
*/
|
*/
|
||||||
class RemoteControl {
|
class RemoteControl {
|
||||||
|
_initialized: boolean;
|
||||||
|
controller: Controller;
|
||||||
|
receiver: Receiver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs new instance. Creates controller and receiver properties.
|
* Constructs new instance. Creates controller and receiver properties.
|
||||||
* @constructor
|
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
this.controller = new Controller();
|
this.controller = new Controller();
|
||||||
this.initialized = false;
|
this._initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the remote control - checks if the remote control should be
|
* Initializes the remote control - checks if the remote control should be
|
||||||
* enabled or not.
|
* enabled or not.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
init() {
|
init() {
|
||||||
if(config.disableRemoteControl
|
if (config.disableRemoteControl
|
||||||
|| this.initialized
|
|| this._initialized
|
||||||
|| !APP.conference.isDesktopSharingEnabled) {
|
|| !APP.conference.isDesktopSharingEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.log("Initializing remote control.");
|
logger.log('Initializing remote control.');
|
||||||
this.initialized = true;
|
this._initialized = true;
|
||||||
this.controller.enable(true);
|
this.controller.enable(true);
|
||||||
this.receiver = new Receiver();
|
this.receiver = new Receiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the passed user supports remote control or not
|
* 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
|
* @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.
|
* the user supports remote control and with false if not.
|
||||||
*/
|
*/
|
||||||
checkUserRemoteControlSupport(user) {
|
checkUserRemoteControlSupport(user: Object) {
|
||||||
return user.getFeatures().then(
|
return user.getFeatures().then(
|
||||||
features => features.has(DISCO_REMOTE_CONTROL_FEATURE),
|
features => features.has(DISCO_REMOTE_CONTROL_FEATURE),
|
||||||
() => false);
|
() => false);
|
||||||
|
|
|
@ -1,50 +1,70 @@
|
||||||
/* global APP */
|
/* @flow */
|
||||||
|
|
||||||
|
import { getLogger } from 'jitsi-meet-logger';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
REMOTE_CONTROL_EVENT_NAME
|
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 {
|
export default class RemoteControlParticipant {
|
||||||
|
_enabled: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance.
|
* Creates new instance.
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
this.enabled = false;
|
this._enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables / Disables the remote control
|
* Enables / Disables the remote control.
|
||||||
* @param {boolean} enabled the new state.
|
*
|
||||||
|
* @param {boolean} enabled - The new state.
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
enable(enabled) {
|
enable(enabled: boolean) {
|
||||||
this.enabled = enabled;
|
this._enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends remote control event to other participant trough data channel.
|
* 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 = () => {}) {
|
sendRemoteControlEvent(
|
||||||
if(!this.enabled || !to) {
|
to: ?string,
|
||||||
|
event: Object,
|
||||||
|
onDataChannelFail: ?Function) {
|
||||||
|
if (!this._enabled || !to) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
"Remote control: Skip sending remote control event. Params:",
|
'Remote control: Skip sending remote control event. Params:',
|
||||||
this.enable,
|
this.enable,
|
||||||
to);
|
to);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try{
|
try {
|
||||||
APP.conference.sendEndpointMessage(to, {
|
APP.conference.sendEndpointMessage(to, {
|
||||||
name: REMOTE_CONTROL_EVENT_NAME,
|
name: REMOTE_CONTROL_EVENT_NAME,
|
||||||
...event
|
...event
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(
|
logger.error(
|
||||||
"Failed to send EndpointMessage via the datachannels",
|
'Failed to send EndpointMessage via the datachannels',
|
||||||
e);
|
e);
|
||||||
onDataChannelFail(e);
|
if (typeof onDataChannelFail === 'function') {
|
||||||
|
onDataChannelFail(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,8 @@ export class AbstractApp extends Component {
|
||||||
localParticipant = {
|
localParticipant = {
|
||||||
avatarID: APP.settings.getAvatarId(),
|
avatarID: APP.settings.getAvatarId(),
|
||||||
avatarURL: APP.settings.getAvatarUrl(),
|
avatarURL: APP.settings.getAvatarUrl(),
|
||||||
email: APP.settings.getEmail()
|
email: APP.settings.getEmail(),
|
||||||
|
name: APP.settings.getDisplayName()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
dispatch(localParticipantJoined(localParticipant));
|
dispatch(localParticipantJoined(localParticipant));
|
||||||
|
|
|
@ -24,7 +24,7 @@ export default class AbstractDialog extends Component {
|
||||||
cancelTitleKey: React.PropTypes.string,
|
cancelTitleKey: React.PropTypes.string,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to show hide the dialog on cancel.
|
* Used to show/hide the dialog on cancel.
|
||||||
*/
|
*/
|
||||||
dispatch: React.PropTypes.func,
|
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