mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-05-21 12:37:49 +00:00
Compare commits
31 Commits
1620
...
jitsi-meet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b22f1965aa | ||
|
|
df6f920b44 | ||
|
|
f13b2462c8 | ||
|
|
c0e80c14f8 | ||
|
|
2b1176df53 | ||
|
|
05bfbf5620 | ||
|
|
4af706bd83 | ||
|
|
b62e4d5ee9 | ||
|
|
bd98d661d3 | ||
|
|
1f7c5529e9 | ||
|
|
0453346cf4 | ||
|
|
15090243d0 | ||
|
|
b22e3ee253 | ||
|
|
e693554961 | ||
|
|
0efca9a9a8 | ||
|
|
5d22061c0a | ||
|
|
84be7fd739 | ||
|
|
5d269ad0aa | ||
|
|
a4d5c41b3a | ||
|
|
846fb9abb0 | ||
|
|
0f33e59e4d | ||
|
|
896650d005 | ||
|
|
6d6b5a7917 | ||
|
|
db5010be9d | ||
|
|
be8860080c | ||
|
|
42f124b0f6 | ||
|
|
7249ababb7 | ||
|
|
079be92468 | ||
|
|
dda2a2feba | ||
|
|
ae726cfdc0 | ||
|
|
1a04e5a9af |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,2 +1,3 @@
|
||||
*.bundle.js -text -diff
|
||||
*.pbxproj -text
|
||||
lib-jitsi-meet.js -text -diff
|
||||
|
||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -36,12 +36,13 @@ DerivedData
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
|
||||
# Android/IJ
|
||||
# Android/IntelliJ
|
||||
#
|
||||
*.iml
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
|
||||
# node.js
|
||||
#
|
||||
@@ -53,4 +54,15 @@ npm-debug.log
|
||||
buck-out/
|
||||
\.buckd/
|
||||
android/app/libs
|
||||
android/keystores/debug.keystore
|
||||
*.keystore
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo. Instead, use
|
||||
# fastlane to re-generate the screenshots whenever they are needed. For more
|
||||
# information about the recommended setup visit:
|
||||
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
|
||||
#
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
|
||||
4
ConferenceEvents.js
Normal file
4
ConferenceEvents.js
Normal file
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Notifies interested parties that hangup procedure will start.
|
||||
*/
|
||||
export const BEFORE_HANGUP = "conference.before_hangup";
|
||||
@@ -144,7 +144,7 @@ dependencies {
|
||||
compile project(':react-native-webrtc')
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:23.0.1'
|
||||
compile 'com.facebook.react:react-native:+'
|
||||
compile 'com.facebook.react:react-native:+' // From node_modules
|
||||
}
|
||||
|
||||
apply from: '../../node_modules/react-native-vector-icons/fonts.gradle'
|
||||
@@ -152,6 +152,6 @@ apply from: '../../node_modules/react-native-vector-icons/fonts.gradle'
|
||||
// Run this once to be able to run the application with BUCK
|
||||
// puts all compile dependencies into folder libs for BUCK to use
|
||||
task copyDownloadableDepsToLibs(type: Copy) {
|
||||
from configurations.compile
|
||||
into 'libs'
|
||||
from configurations.compile
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.app.Application;
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -48,6 +49,8 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
|
||||
if (!getReactNativeHost()
|
||||
.getReactInstanceManager()
|
||||
.getDevSupportManager()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:windowTranslucentStatus">true</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
4
app.js
4
app.js
@@ -22,6 +22,7 @@ import conference from './conference';
|
||||
import API from './modules/API/API';
|
||||
|
||||
import translation from "./modules/translation/translation";
|
||||
import remoteControl from "./modules/remotecontrol/RemoteControl";
|
||||
|
||||
const APP = {
|
||||
// Used by do_external_connect.js if we receive the attach data after
|
||||
@@ -59,7 +60,8 @@ const APP = {
|
||||
*/
|
||||
ConferenceUrl : null,
|
||||
connection: null,
|
||||
API
|
||||
API,
|
||||
remoteControl
|
||||
};
|
||||
|
||||
// TODO The execution of the mobile app starts from react/index.native.js.
|
||||
|
||||
@@ -14,9 +14,12 @@ import {reportError} from './modules/util/helpers';
|
||||
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
import UIUtil from './modules/UI/util/UIUtil';
|
||||
import * as JitsiMeetConferenceEvents from './ConferenceEvents';
|
||||
|
||||
import analytics from './modules/analytics/analytics';
|
||||
|
||||
import EventEmitter from "events";
|
||||
|
||||
const ConnectionEvents = JitsiMeetJS.events.connection;
|
||||
const ConnectionErrors = JitsiMeetJS.errors.connection;
|
||||
|
||||
@@ -28,6 +31,8 @@ const TrackErrors = JitsiMeetJS.errors.track;
|
||||
|
||||
const ConnectionQualityEvents = JitsiMeetJS.events.connectionQuality;
|
||||
|
||||
const eventEmitter = new EventEmitter();
|
||||
|
||||
let room, connection, localAudio, localVideo;
|
||||
|
||||
/**
|
||||
@@ -485,10 +490,11 @@ export default {
|
||||
}).then(([tracks, con]) => {
|
||||
logger.log('initialized with %s local tracks', tracks.length);
|
||||
APP.connection = connection = con;
|
||||
this._bindConnectionFailedHandler(con);
|
||||
this._createRoom(tracks);
|
||||
this.isDesktopSharingEnabled =
|
||||
JitsiMeetJS.isDesktopSharingEnabled();
|
||||
APP.remoteControl.init();
|
||||
this._bindConnectionFailedHandler(con);
|
||||
this._createRoom(tracks);
|
||||
|
||||
if (UIUtil.isButtonEnabled('contacts')
|
||||
&& !interfaceConfig.filmStripOnly) {
|
||||
@@ -981,7 +987,7 @@ export default {
|
||||
let externalInstallation = false;
|
||||
|
||||
if (shareScreen) {
|
||||
createLocalTracks({
|
||||
this.screenSharingPromise = createLocalTracks({
|
||||
devices: ['desktop'],
|
||||
desktopSharingExtensionExternalInstallation: {
|
||||
interval: 500,
|
||||
@@ -1070,7 +1076,10 @@ export default {
|
||||
dialogTitleKey, dialogTxt, false);
|
||||
});
|
||||
} else {
|
||||
createLocalTracks({ devices: ['video'] }).then(
|
||||
APP.remoteControl.receiver.stop();
|
||||
this.screenSharingPromise = createLocalTracks(
|
||||
{ devices: ['video'] })
|
||||
.then(
|
||||
([stream]) => this.useVideoStream(stream)
|
||||
).then(() => {
|
||||
this.videoSwitchInProgress = false;
|
||||
@@ -1102,6 +1111,8 @@ export default {
|
||||
}
|
||||
);
|
||||
|
||||
room.on(ConferenceEvents.PARTCIPANT_FEATURES_CHANGED,
|
||||
user => APP.UI.onUserFeaturesChanged(user));
|
||||
room.on(ConferenceEvents.USER_JOINED, (id, user) => {
|
||||
if (user.isHidden())
|
||||
return;
|
||||
@@ -1600,12 +1611,23 @@ export default {
|
||||
},
|
||||
/**
|
||||
* Adds any room listener.
|
||||
* @param eventName one of the ConferenceEvents
|
||||
* @param callBack the function to be called when the event occurs
|
||||
* @param {string} eventName one of the ConferenceEvents
|
||||
* @param {Function} listener the function to be called when the event
|
||||
* occurs
|
||||
*/
|
||||
addConferenceListener(eventName, callBack) {
|
||||
room.on(eventName, callBack);
|
||||
addConferenceListener(eventName, listener) {
|
||||
room.on(eventName, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes any room listener.
|
||||
* @param {string} eventName one of the ConferenceEvents
|
||||
* @param {Function} listener the listener to be removed.
|
||||
*/
|
||||
removeConferenceListener(eventName, listener) {
|
||||
room.off(eventName, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Inits list of current devices and event listener for device change.
|
||||
* @private
|
||||
@@ -1763,6 +1785,7 @@ export default {
|
||||
* requested
|
||||
*/
|
||||
hangup (requestFeedback = false) {
|
||||
eventEmitter.emit(JitsiMeetConferenceEvents.BEFORE_HANGUP);
|
||||
APP.UI.hideRingOverLay();
|
||||
let requestFeedbackPromise = requestFeedback
|
||||
? APP.UI.requestFeedbackOnHangup()
|
||||
@@ -1813,5 +1836,36 @@ export default {
|
||||
APP.settings.setAvatarUrl(url);
|
||||
APP.UI.setUserAvatarUrl(room.myUserId(), url);
|
||||
sendData(commands.AVATAR_URL, url);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a message via the data channel.
|
||||
* @param {string} to the id of the endpoint that should receive the
|
||||
* message. If "" - the message will be sent to all participants.
|
||||
* @param {object} payload the payload of the message.
|
||||
* @throws NetworkError or InvalidStateError or Error if the operation
|
||||
* fails.
|
||||
*/
|
||||
sendEndpointMessage (to, payload) {
|
||||
room.sendEndpointMessage(to, payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds new listener.
|
||||
* @param {String} eventName the name of the event
|
||||
* @param {Function} listener the listener.
|
||||
*/
|
||||
addListener (eventName, listener) {
|
||||
eventEmitter.addListener(eventName, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes listener.
|
||||
* @param {String} eventName the name of the event that triggers the
|
||||
* listener
|
||||
* @param {Function} listener the listener.
|
||||
*/
|
||||
removeListener (eventName, listener) {
|
||||
eventEmitter.removeListener(eventName, listener);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -20,10 +20,10 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
//focusUserJid: 'focus@auth.jitsi-meet.example.com', // The real JID of focus participant - can be overridden here
|
||||
//defaultSipNumber: '', // Default SIP number
|
||||
|
||||
// Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
|
||||
desktopSharingChromeMethod: 'ext',
|
||||
// The ID of the jidesha extension for Chrome.
|
||||
desktopSharingChromeExtId: 'diibjkoicjeejcmhdnailmkgecihlobk',
|
||||
desktopSharingChromeExtId: null,
|
||||
// Whether desktop sharing should be disabled on Chrome.
|
||||
desktopSharingChromeDisabled: true,
|
||||
// The media sources to use when using screen sharing with the Chrome
|
||||
// extension.
|
||||
desktopSharingChromeSources: ['screen', 'window', 'tab'],
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
0 0 3px $videoThumbnailSelected !important;
|
||||
}
|
||||
|
||||
.remotevideomenu {
|
||||
.remotevideomenu > .icon-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
box-shadow: inset 0 0 3px $videoThumbnailHovered,
|
||||
0 0 3px $videoThumbnailHovered;
|
||||
|
||||
.remotevideomenu {
|
||||
.remotevideomenu > .icon-menu {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
@@ -121,4 +121,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
padding: 0;
|
||||
margin: 2px 0;
|
||||
bottom: 0;
|
||||
width: 100px;
|
||||
height: auto;
|
||||
|
||||
&:first-child {
|
||||
@@ -66,4 +65,9 @@
|
||||
|
||||
span.remotevideomenu:hover ul.popupmenu, ul.popupmenu:hover {
|
||||
display:block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.remote-control-spinner {
|
||||
top: 6px;
|
||||
left: 2px;
|
||||
}
|
||||
|
||||
@@ -131,9 +131,9 @@ Or autostart it by adding the line in `/etc/rc.local`:
|
||||
|
||||
## Install Jitsi Conference Focus (jicofo)
|
||||
|
||||
Install JDK and Ant if missing:
|
||||
Install JDK and Maven if missing:
|
||||
```
|
||||
apt-get install default-jdk ant
|
||||
apt-get install default-jdk maven
|
||||
```
|
||||
|
||||
_NOTE: When installing on older Debian releases keep in mind that you need JDK >= 1.7._
|
||||
@@ -145,12 +145,14 @@ git clone https://github.com/jitsi/jicofo.git
|
||||
Build distribution package. Replace {os-name} with one of: 'lin', 'lin64', 'macosx', 'win', 'win64'.
|
||||
```sh
|
||||
cd jicofo
|
||||
ant dist.{os-name}
|
||||
mvn package -DskipTests -Dassembly.skipAssembly=false
|
||||
```
|
||||
Run jicofo:
|
||||
```sh
|
||||
cd dist/{os-name}'
|
||||
./jicofo.sh --host=127.0.0.1 --domain=jitsi.example.com --secret=YOURSECRET2 --user_domain=auth.jitsi.example.com --user_name=focus --user_password=YOURSECRET3
|
||||
=======
|
||||
unzip target/jicofo-{os-name}-1.0-SNAPSHOT.zip
|
||||
cd jicofo-{os-name}-1.0-SNAPSHOT'
|
||||
./jicofo.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET2 --user_domain=auth.jitsi.example.com --user_name=focus --user_password=YOURSECRET3
|
||||
```
|
||||
|
||||
## Deploy Jitsi Meet
|
||||
@@ -159,6 +161,8 @@ Checkout and configure Jitsi Meet:
|
||||
cd /srv
|
||||
git clone https://github.com/jitsi/jitsi-meet.git
|
||||
mv jitsi-meet/ jitsi.example.com
|
||||
npm install
|
||||
make
|
||||
```
|
||||
|
||||
Edit host names in `/srv/jitsi.example.com/config.js` (see also the example config file):
|
||||
@@ -167,11 +171,11 @@ var config = {
|
||||
hosts: {
|
||||
domain: 'jitsi.example.com',
|
||||
muc: 'conference.jitsi.example.com',
|
||||
bridge: 'jitsi-videobridge.jitsi.example.com'
|
||||
bridge: 'jitsi-videobridge.jitsi.example.com',
|
||||
focus: 'focus.jitsi.example.com'
|
||||
},
|
||||
useNicks: false,
|
||||
bosh: '//jitsi.example.com/http-bind', // FIXME: use xep-0156 for that
|
||||
desktopSharing: 'false' // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
|
||||
//chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension
|
||||
//minChromeExtVersion: '0.1' // Required version of Chrome extension
|
||||
};
|
||||
|
||||
@@ -43,11 +43,11 @@
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string></string>
|
||||
<string>Participate in conferences with video.</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string></string>
|
||||
<string>Participate in conferences with audio.</string>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>jitsi.ttf</string>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
|
||||
2602576C1D0A7703001E3363 /* jitsi.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2602576B1D0A7703001E3363 /* jitsi.ttf */; };
|
||||
3847F11906B4479A9162628F /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 821D8ABD506944B4BDBB069B /* libRNVectorIcons.a */; };
|
||||
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
|
||||
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
|
||||
901FE90FA5744B5B94DCDC41 /* libKCKeepAwake.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EA8C046B2BF46279796F07D /* libKCKeepAwake.a */; };
|
||||
B30EF2311DC0ED7C00690F45 /* WebRTC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B30EF2301DC0ED7C00690F45 /* WebRTC.framework */; };
|
||||
@@ -113,6 +114,62 @@
|
||||
remoteGlobalIDString = 5DBEB1501B18CEA900B34395;
|
||||
remoteInfo = RNVectorIcons;
|
||||
};
|
||||
3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 3D3C059A1DE3340900C268FA;
|
||||
remoteInfo = yoga;
|
||||
};
|
||||
3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 3D3C06751DE3340C00C268FA;
|
||||
remoteInfo = "yoga-tvOS";
|
||||
};
|
||||
3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4;
|
||||
remoteInfo = cxxreact;
|
||||
};
|
||||
3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4;
|
||||
remoteInfo = "cxxreact-tvOS";
|
||||
};
|
||||
3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4;
|
||||
remoteInfo = jschelpers;
|
||||
};
|
||||
3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4;
|
||||
remoteInfo = "jschelpers-tvOS";
|
||||
};
|
||||
5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RCTAnimation;
|
||||
};
|
||||
5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 2D2A28201D9B03D100D4039D;
|
||||
remoteInfo = "RCTAnimation-tvOS";
|
||||
};
|
||||
78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
|
||||
@@ -221,6 +278,7 @@
|
||||
22418656B14845609F953A42 /* RNVectorIcons.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNVectorIcons.xcodeproj; path = "../node_modules/react-native-vector-icons/RNVectorIcons.xcodeproj"; sourceTree = "<group>"; };
|
||||
2602576B1D0A7703001E3363 /* jitsi.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = jitsi.ttf; path = ../android/app/src/main/assets/fonts/jitsi.ttf; sourceTree = "<group>"; };
|
||||
5B09C20C78C74A548AAAC1FA /* KCKeepAwake.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = KCKeepAwake.xcodeproj; path = "../node_modules/react-native-keep-awake/ios/KCKeepAwake.xcodeproj"; sourceTree = "<group>"; };
|
||||
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; };
|
||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
|
||||
821D8ABD506944B4BDBB069B /* libRNVectorIcons.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNVectorIcons.a; sourceTree = "<group>"; };
|
||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
|
||||
@@ -259,6 +317,7 @@
|
||||
BF96438E1C34FBE100B0BBDF /* VideoToolbox.framework in Frameworks */,
|
||||
146834051AC3E58100842450 /* libReact.a in Frameworks */,
|
||||
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
|
||||
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */,
|
||||
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,
|
||||
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */,
|
||||
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */,
|
||||
@@ -364,7 +423,13 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
146834041AC3E56700842450 /* libReact.a */,
|
||||
B3BA19EF1DC6B37B00BCD481 /* libReact-tvOS.a */,
|
||||
B3BA19EF1DC6B37B00BCD481 /* libReact.a */,
|
||||
3DAD3EA51DF850E9000B6D8A /* libyoga.a */,
|
||||
3DAD3EA71DF850E9000B6D8A /* libyoga.a */,
|
||||
3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */,
|
||||
3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */,
|
||||
3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */,
|
||||
3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -377,6 +442,15 @@
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5E91572E1DD0AC6500FF2AA8 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */,
|
||||
5E9157351DD0AC6500FF2AA8 /* libRCTAnimation-tvOS.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6956B374CC3C453DB7B8E82D /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -403,6 +477,7 @@
|
||||
BF9643911C34FBF100B0BBDF /* libsqlite3.tbd */,
|
||||
BF9643931C34FBF900B0BBDF /* libstdc++.tbd */,
|
||||
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
|
||||
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */,
|
||||
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,
|
||||
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */,
|
||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
|
||||
@@ -501,7 +576,7 @@
|
||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0800;
|
||||
LastUpgradeCheck = 0820;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
TargetAttributes = {
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
@@ -534,6 +609,10 @@
|
||||
ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */;
|
||||
ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */;
|
||||
ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */;
|
||||
ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
|
||||
@@ -657,6 +736,62 @@
|
||||
remoteRef = 26D58A061D0B42EE00FC396B /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
3DAD3EA51DF850E9000B6D8A /* libyoga.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libyoga.a;
|
||||
remoteRef = 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
3DAD3EA71DF850E9000B6D8A /* libyoga.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libyoga.a;
|
||||
remoteRef = 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libcxxreact.a;
|
||||
remoteRef = 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libcxxreact.a;
|
||||
remoteRef = 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libjschelpers.a;
|
||||
remoteRef = 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libjschelpers.a;
|
||||
remoteRef = 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRCTAnimation.a;
|
||||
remoteRef = 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
5E9157351DD0AC6500FF2AA8 /* libRCTAnimation-tvOS.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = "libRCTAnimation-tvOS.a";
|
||||
remoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
@@ -713,10 +848,10 @@
|
||||
remoteRef = B3BA19EA1DC6B37B00BCD481 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
B3BA19EF1DC6B37B00BCD481 /* libReact-tvOS.a */ = {
|
||||
B3BA19EF1DC6B37B00BCD481 /* libReact.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = "libReact-tvOS.a";
|
||||
path = libReact.a;
|
||||
remoteRef = B3BA19EE1DC6B37B00BCD481 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
@@ -834,7 +969,6 @@
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.JitsiMeet.ios;
|
||||
PRODUCT_NAME = "jitsi-meet-react";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -871,7 +1005,6 @@
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.JitsiMeet.ios;
|
||||
PRODUCT_NAME = "jitsi-meet-react";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -888,15 +1021,19 @@
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
@@ -936,8 +1073,10 @@
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -945,6 +1084,7 @@
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "0820"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
parallelizeBuildables = "NO"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
|
||||
BuildableName = "libReact.a"
|
||||
BlueprintName = "React"
|
||||
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
|
||||
@@ -156,8 +156,8 @@
|
||||
"kick": "Kick out",
|
||||
"muted": "Muted",
|
||||
"domute": "Mute",
|
||||
"flip": "Flip"
|
||||
|
||||
"flip": "Flip",
|
||||
"remoteControl": "Remote control"
|
||||
},
|
||||
"connectionindicator":
|
||||
{
|
||||
@@ -316,7 +316,12 @@
|
||||
"externalInstallationMsg": "You need to install our desktop sharing extension.",
|
||||
"muteParticipantTitle": "Mute this participant?",
|
||||
"muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
|
||||
"muteParticipantButton": "Mute"
|
||||
"muteParticipantButton": "Mute",
|
||||
"remoteControlTitle": "Remote Control",
|
||||
"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__!",
|
||||
"remoteControlStopMessage": "The remote control session ended!"
|
||||
},
|
||||
"email":
|
||||
{
|
||||
|
||||
@@ -55,7 +55,9 @@ function initCommands() {
|
||||
APP.conference.toggleScreenSharing.bind(APP.conference),
|
||||
"video-hangup": () => APP.conference.hangup(),
|
||||
"email": APP.conference.changeLocalEmail,
|
||||
"avatar-url": APP.conference.changeLocalAvatarUrl
|
||||
"avatar-url": APP.conference.changeLocalAvatarUrl,
|
||||
"remote-control-event": event =>
|
||||
APP.remoteControl.onRemoteControlAPIEvent(event)
|
||||
};
|
||||
Object.keys(commands).forEach(function (key) {
|
||||
postis.listen(key, args => commands[key](...args));
|
||||
@@ -94,7 +96,13 @@ function triggerEvent (name, object) {
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
class API {
|
||||
/**
|
||||
* Constructs new instance
|
||||
* @constructor
|
||||
*/
|
||||
constructor() { }
|
||||
|
||||
/**
|
||||
* Initializes the APIConnector. Setups message event listeners that will
|
||||
* receive information from external applications that embed Jitsi Meet.
|
||||
@@ -108,6 +116,17 @@ export default {
|
||||
return;
|
||||
|
||||
enabled = true;
|
||||
|
||||
if(!postis) {
|
||||
this._initPostis();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initializes postis library.
|
||||
* @private
|
||||
*/
|
||||
_initPostis() {
|
||||
let postisOptions = {
|
||||
window: target
|
||||
};
|
||||
@@ -116,7 +135,7 @@ export default {
|
||||
= "jitsi_meet_external_api_" + jitsi_meet_external_api_id;
|
||||
postis = postisInit(postisOptions);
|
||||
initCommands();
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that message was sent.
|
||||
@@ -124,7 +143,7 @@ export default {
|
||||
*/
|
||||
notifySendingChatMessage (body) {
|
||||
triggerEvent("outgoing-message", {"message": body});
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that
|
||||
@@ -143,7 +162,7 @@ export default {
|
||||
"incoming-message",
|
||||
{"from": id, "nick": nick, "message": body, "stamp": ts}
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that
|
||||
@@ -152,7 +171,7 @@ export default {
|
||||
*/
|
||||
notifyUserJoined (id) {
|
||||
triggerEvent("participant-joined", {id});
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that
|
||||
@@ -161,7 +180,7 @@ export default {
|
||||
*/
|
||||
notifyUserLeft (id) {
|
||||
triggerEvent("participant-left", {id});
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that
|
||||
@@ -171,7 +190,7 @@ export default {
|
||||
*/
|
||||
notifyDisplayNameChanged (id, displayName) {
|
||||
triggerEvent("display-name-change", {id, displayname: displayName});
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that
|
||||
@@ -181,7 +200,7 @@ export default {
|
||||
*/
|
||||
notifyConferenceJoined (room) {
|
||||
triggerEvent("video-conference-joined", {roomName: room});
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that
|
||||
@@ -191,7 +210,7 @@ export default {
|
||||
*/
|
||||
notifyConferenceLeft (room) {
|
||||
triggerEvent("video-conference-left", {roomName: room});
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that
|
||||
@@ -199,13 +218,23 @@ export default {
|
||||
*/
|
||||
notifyReadyToClose () {
|
||||
triggerEvent("video-ready-to-close", {});
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends remote control event.
|
||||
* @param {RemoteControlEvent} event the remote control event.
|
||||
*/
|
||||
sendRemoteControlEvent(event) {
|
||||
sendMessage({method: "remote-control-event", params: event});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listeners.
|
||||
*/
|
||||
dispose: function () {
|
||||
dispose () {
|
||||
if(enabled)
|
||||
postis.destroy();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default new API();
|
||||
|
||||
@@ -1441,4 +1441,11 @@ UI.hideUserMediaPermissionsGuidanceOverlay = function () {
|
||||
GumPermissionsOverlay.hide();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles user's features changes.
|
||||
*/
|
||||
UI.onUserFeaturesChanged = function (user) {
|
||||
VideoLayout.onUserFeaturesChanged(user);
|
||||
};
|
||||
|
||||
module.exports = UI;
|
||||
|
||||
@@ -3,6 +3,7 @@ const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import Avatar from "../avatar/Avatar";
|
||||
import {createDeferred} from '../../util/helpers';
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
import UIUtil from "../util/UIUtil";
|
||||
import {VideoContainer, VIDEO_CONTAINER_TYPE} from "./VideoContainer";
|
||||
|
||||
@@ -19,6 +20,7 @@ export default class LargeVideoManager {
|
||||
* @type {Object.<string, LargeContainer>}
|
||||
*/
|
||||
this.containers = {};
|
||||
this.eventEmitter = emitter;
|
||||
|
||||
this.state = VIDEO_CONTAINER_TYPE;
|
||||
this.videoContainer = new VideoContainer(
|
||||
@@ -164,6 +166,7 @@ export default class LargeVideoManager {
|
||||
// after everything is done check again if there are any pending
|
||||
// new streams.
|
||||
this.updateInProcess = false;
|
||||
this.eventEmitter.emit(UIEvents.LARGE_VIDEO_ID_CHANGED, this.id);
|
||||
this.scheduleLargeVideoUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ function RemoteVideo(user, VideoLayout, emitter) {
|
||||
this.videoSpanId = `participant_${this.id}`;
|
||||
SmallVideo.call(this, VideoLayout);
|
||||
this.hasRemoteVideoMenu = false;
|
||||
this._supportsRemoteControl = false;
|
||||
this.addRemoteVideoContainer();
|
||||
this.connectionIndicator = new ConnectionIndicator(this, this.id);
|
||||
this.setDisplayName();
|
||||
@@ -64,7 +65,7 @@ RemoteVideo.prototype.addRemoteVideoContainer = function() {
|
||||
|
||||
this.initBrowserSpecificProperties();
|
||||
|
||||
if (APP.conference.isModerator) {
|
||||
if (APP.conference.isModerator || this._supportsRemoteControl) {
|
||||
this.addRemoteVideoMenu();
|
||||
}
|
||||
|
||||
@@ -106,14 +107,6 @@ RemoteVideo.prototype._initPopupMenu = function (popupMenuElement) {
|
||||
// call the original show, passing its actual this
|
||||
origShowFunc.call(this.popover);
|
||||
}.bind(this);
|
||||
|
||||
// override popover hide method so we can cleanup click handlers
|
||||
let origHideFunc = this.popover.forceHide;
|
||||
this.popover.forceHide = function () {
|
||||
$(document).off("click", '#mutelink_' + this.id);
|
||||
$(document).off("click", '#ejectlink_' + this.id);
|
||||
origHideFunc.call(this.popover);
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -139,38 +132,68 @@ RemoteVideo.prototype._generatePopupContent = function () {
|
||||
let popupmenuElement = document.createElement('ul');
|
||||
popupmenuElement.className = 'popupmenu';
|
||||
popupmenuElement.id = `remote_popupmenu_${this.id}`;
|
||||
let menuItems = [];
|
||||
|
||||
let muteTranslationKey;
|
||||
let muteClassName;
|
||||
if (this.isAudioMuted) {
|
||||
muteTranslationKey = 'videothumbnail.muted';
|
||||
muteClassName = 'mutelink disabled';
|
||||
} else {
|
||||
muteTranslationKey = 'videothumbnail.domute';
|
||||
muteClassName = 'mutelink';
|
||||
if(APP.conference.isModerator) {
|
||||
let muteTranslationKey;
|
||||
let muteClassName;
|
||||
if (this.isAudioMuted) {
|
||||
muteTranslationKey = 'videothumbnail.muted';
|
||||
muteClassName = 'mutelink disabled';
|
||||
} else {
|
||||
muteTranslationKey = 'videothumbnail.domute';
|
||||
muteClassName = 'mutelink';
|
||||
}
|
||||
|
||||
let muteHandler = this._muteHandler.bind(this);
|
||||
let kickHandler = this._kickHandler.bind(this);
|
||||
|
||||
menuItems = [
|
||||
{
|
||||
id: 'mutelink_' + this.id,
|
||||
handler: muteHandler,
|
||||
icon: 'icon-mic-disabled',
|
||||
className: muteClassName,
|
||||
data: {
|
||||
i18n: muteTranslationKey
|
||||
}
|
||||
}, {
|
||||
id: 'ejectlink_' + this.id,
|
||||
handler: kickHandler,
|
||||
icon: 'icon-kick',
|
||||
data: {
|
||||
i18n: 'videothumbnail.kick'
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
let muteHandler = this._muteHandler.bind(this);
|
||||
let kickHandler = this._kickHandler.bind(this);
|
||||
|
||||
let menuItems = [
|
||||
{
|
||||
id: 'mutelink_' + this.id,
|
||||
handler: muteHandler,
|
||||
icon: 'icon-mic-disabled',
|
||||
className: muteClassName,
|
||||
data: {
|
||||
i18n: muteTranslationKey
|
||||
}
|
||||
}, {
|
||||
id: 'ejectlink_' + this.id,
|
||||
handler: kickHandler,
|
||||
icon: 'icon-kick',
|
||||
data: {
|
||||
i18n: 'videothumbnail.kick'
|
||||
}
|
||||
if(this._supportsRemoteControl) {
|
||||
let icon, handler, className;
|
||||
if(APP.remoteControl.controller.getRequestedParticipant()
|
||||
=== this.id) {
|
||||
handler = () => {};
|
||||
className = "requestRemoteControlLink disabled";
|
||||
icon = "remote-control-spinner fa fa-spinner fa-spin";
|
||||
} else if(!APP.remoteControl.controller.isStarted()) {
|
||||
handler = this._requestRemoteControlPermissions.bind(this);
|
||||
icon = "fa fa-play";
|
||||
className = "requestRemoteControlLink";
|
||||
} else {
|
||||
handler = this._stopRemoteControl.bind(this);
|
||||
icon = "fa fa-stop";
|
||||
className = "requestRemoteControlLink";
|
||||
}
|
||||
];
|
||||
menuItems.push({
|
||||
id: 'remoteControl_' + this.id,
|
||||
handler,
|
||||
icon,
|
||||
className,
|
||||
data: {
|
||||
i18n: 'videothumbnail.remoteControl'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
menuItems.forEach(el => {
|
||||
let menuItem = this._generatePopupMenuItem(el);
|
||||
@@ -182,6 +205,76 @@ RemoteVideo.prototype._generatePopupContent = function () {
|
||||
return popupmenuElement;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the remote control supported value and initializes or updates the menu
|
||||
* depending on the remote control is supported or not.
|
||||
* @param {boolean} isSupported
|
||||
*/
|
||||
RemoteVideo.prototype.setRemoteControlSupport = function(isSupported = false) {
|
||||
if(this._supportsRemoteControl === isSupported) {
|
||||
return;
|
||||
}
|
||||
this._supportsRemoteControl = isSupported;
|
||||
if(!isSupported) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.hasRemoteVideoMenu) {
|
||||
//create menu
|
||||
this.addRemoteVideoMenu();
|
||||
} else {
|
||||
//update the content
|
||||
this.updateRemoteVideoMenu(this.isAudioMuted, true);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Requests permissions for remote control session.
|
||||
*/
|
||||
RemoteVideo.prototype._requestRemoteControlPermissions = function () {
|
||||
APP.remoteControl.controller.requestPermissions(
|
||||
this.id, this.VideoLayout.getLargeVideoWrapper()).then(result => {
|
||||
if(result === null) {
|
||||
return;
|
||||
}
|
||||
this.updateRemoteVideoMenu(this.isAudioMuted, true);
|
||||
APP.UI.messageHandler.openMessageDialog(
|
||||
"dialog.remoteControlTitle",
|
||||
(result === false) ? "dialog.remoteControlDeniedMessage"
|
||||
: "dialog.remoteControlAllowedMessage",
|
||||
{user: this.user.getDisplayName()
|
||||
|| interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME}
|
||||
);
|
||||
if(result === true) {//the remote control permissions has been granted
|
||||
// pin the controlled participant
|
||||
let pinnedId = this.VideoLayout.getPinnedId();
|
||||
if(pinnedId !== this.id) {
|
||||
this.VideoLayout.handleVideoThumbClicked(this.id);
|
||||
}
|
||||
}
|
||||
}, error => {
|
||||
logger.error(error);
|
||||
this.updateRemoteVideoMenu(this.isAudioMuted, true);
|
||||
APP.UI.messageHandler.openMessageDialog(
|
||||
"dialog.remoteControlTitle",
|
||||
"dialog.remoteControlErrorMessage",
|
||||
{user: this.user.getDisplayName()
|
||||
|| interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME}
|
||||
);
|
||||
});
|
||||
this.updateRemoteVideoMenu(this.isAudioMuted, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stops remote control session.
|
||||
*/
|
||||
RemoteVideo.prototype._stopRemoteControl = function () {
|
||||
// send message about stopping
|
||||
APP.remoteControl.controller.stop();
|
||||
this.updateRemoteVideoMenu(this.isAudioMuted, true);
|
||||
};
|
||||
|
||||
RemoteVideo.prototype._muteHandler = function () {
|
||||
if (this.isAudioMuted)
|
||||
return;
|
||||
@@ -244,8 +337,7 @@ RemoteVideo.prototype._generatePopupMenuItem = function (opts = {}) {
|
||||
linkItem.appendChild(textContent);
|
||||
linkItem.id = id;
|
||||
|
||||
// Delegate event to the document.
|
||||
$(document).on("click", `#${id}`, handler);
|
||||
linkItem.onclick = handler;
|
||||
menuItem.appendChild(linkItem);
|
||||
|
||||
return menuItem;
|
||||
|
||||
@@ -406,6 +406,7 @@ var VideoLayout = {
|
||||
remoteVideo = smallVideo;
|
||||
else
|
||||
remoteVideo = new RemoteVideo(user, VideoLayout, eventEmitter);
|
||||
this._setRemoteControlProperties(user, remoteVideo);
|
||||
this.addRemoteVideoContainer(id, remoteVideo);
|
||||
},
|
||||
|
||||
@@ -1158,12 +1159,44 @@ var VideoLayout = {
|
||||
* Sets the flipX state of the local video.
|
||||
* @param {boolean} true for flipped otherwise false;
|
||||
*/
|
||||
setLocalFlipX: function (val) {
|
||||
setLocalFlipX (val) {
|
||||
this.localFlipX = val;
|
||||
|
||||
},
|
||||
|
||||
getEventEmitter: () => {return eventEmitter;}
|
||||
getEventEmitter() {return eventEmitter;},
|
||||
|
||||
/**
|
||||
* Handles user's features changes.
|
||||
*/
|
||||
onUserFeaturesChanged (user) {
|
||||
let video = this.getSmallVideo(user.getId());
|
||||
|
||||
if (!video) {
|
||||
return;
|
||||
}
|
||||
this._setRemoteControlProperties(user, video);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the remote control properties (checks whether remote control
|
||||
* is supported and executes remoteVideo.setRemoteControlSupport).
|
||||
* @param {JitsiParticipant} user the user that will be checked for remote
|
||||
* control support.
|
||||
* @param {RemoteVideo} remoteVideo the remoteVideo on which the properties
|
||||
* will be set.
|
||||
*/
|
||||
_setRemoteControlProperties (user, remoteVideo) {
|
||||
APP.remoteControl.checkUserRemoteControlSupport(user).then(result =>
|
||||
remoteVideo.setRemoteControlSupport(result));
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the wrapper jquery selector for the largeVideo
|
||||
* @returns {JQuerySelector} the wrapper jquery selector for the largeVideo
|
||||
*/
|
||||
getLargeVideoWrapper() {
|
||||
return this.getCurrentlyOnLargeContainer().$wrapper;
|
||||
}
|
||||
};
|
||||
|
||||
export default VideoLayout;
|
||||
|
||||
@@ -65,6 +65,12 @@ function showKeyboardShortcutsPanel(show) {
|
||||
*/
|
||||
let _shortcuts = {};
|
||||
|
||||
/**
|
||||
* True if the keyboard shortcuts are enabled and false if not.
|
||||
* @type {boolean}
|
||||
*/
|
||||
let enabled = true;
|
||||
|
||||
/**
|
||||
* Maps keycode to character, id of popover for given function and function.
|
||||
*/
|
||||
@@ -74,6 +80,9 @@ var KeyboardShortcut = {
|
||||
|
||||
var self = this;
|
||||
window.onkeyup = function(e) {
|
||||
if(!enabled) {
|
||||
return;
|
||||
}
|
||||
var key = self._getKeyboardKey(e).toUpperCase();
|
||||
var num = parseInt(key, 10);
|
||||
if(!($(":focus").is("input[type=text]") ||
|
||||
@@ -93,6 +102,9 @@ var KeyboardShortcut = {
|
||||
};
|
||||
|
||||
window.onkeydown = function(e) {
|
||||
if(!enabled) {
|
||||
return;
|
||||
}
|
||||
if(!($(":focus").is("input[type=text]") ||
|
||||
$(":focus").is("input[type=password]") ||
|
||||
$(":focus").is("textarea"))) {
|
||||
@@ -105,6 +117,14 @@ var KeyboardShortcut = {
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables/Disables the keyboard shortcuts.
|
||||
* @param {boolean} value - the new value.
|
||||
*/
|
||||
enable: function (value) {
|
||||
enabled = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Registers a new shortcut.
|
||||
*
|
||||
|
||||
163
modules/keycode/keycode.js
Normal file
163
modules/keycode/keycode.js
Normal file
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* Enumerates the supported keys.
|
||||
* NOTE: The maps represents physical keys on the keyboard, not chars.
|
||||
* @readonly
|
||||
* @enum {string}
|
||||
*/
|
||||
export const KEYS = {
|
||||
BACKSPACE: "backspace" ,
|
||||
DELETE : "delete",
|
||||
RETURN : "enter",
|
||||
TAB : "tab",
|
||||
ESCAPE : "escape",
|
||||
UP : "up",
|
||||
DOWN : "down",
|
||||
RIGHT : "right",
|
||||
LEFT : "left",
|
||||
HOME : "home",
|
||||
END : "end",
|
||||
PAGEUP : "pageup",
|
||||
PAGEDOWN : "pagedown",
|
||||
|
||||
F1 : "f1",
|
||||
F2 : "f2",
|
||||
F3 : "f3",
|
||||
F4 : "f4",
|
||||
F5 : "f5",
|
||||
F6 : "f6",
|
||||
F7 : "f7",
|
||||
F8 : "f8",
|
||||
F9 : "f9",
|
||||
F10 : "f10",
|
||||
F11 : "f11",
|
||||
F12 : "f12",
|
||||
META : "command",
|
||||
CMD_L: "command",
|
||||
CMD_R: "command",
|
||||
ALT : "alt",
|
||||
CONTROL : "control",
|
||||
SHIFT : "shift",
|
||||
CAPS_LOCK: "caps_lock", //not supported by robotjs
|
||||
SPACE : "space",
|
||||
PRINTSCREEN : "printscreen",
|
||||
INSERT : "insert",
|
||||
|
||||
NUMPAD_0 : "numpad_0",
|
||||
NUMPAD_1 : "numpad_1",
|
||||
NUMPAD_2 : "numpad_2",
|
||||
NUMPAD_3 : "numpad_3",
|
||||
NUMPAD_4 : "numpad_4",
|
||||
NUMPAD_5 : "numpad_5",
|
||||
NUMPAD_6 : "numpad_6",
|
||||
NUMPAD_7 : "numpad_7",
|
||||
NUMPAD_8 : "numpad_8",
|
||||
NUMPAD_9 : "numpad_9",
|
||||
|
||||
COMMA: ",",
|
||||
|
||||
PERIOD: ".",
|
||||
SEMICOLON: ";",
|
||||
QUOTE: "'",
|
||||
BRACKET_LEFT: "[",
|
||||
BRACKET_RIGHT: "]",
|
||||
BACKQUOTE: "`",
|
||||
BACKSLASH: "\\",
|
||||
MINUS: "-",
|
||||
EQUAL: "=",
|
||||
SLASH: "/"
|
||||
};
|
||||
|
||||
/**
|
||||
* Mapping between the key codes and keys deined in KEYS.
|
||||
* The mappings are based on
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Specifications
|
||||
*/
|
||||
let keyCodeToKey = {
|
||||
8: KEYS.BACKSPACE,
|
||||
9: KEYS.TAB,
|
||||
13: KEYS.RETURN,
|
||||
16: KEYS.SHIFT,
|
||||
17: KEYS.CONTROL,
|
||||
18: KEYS.ALT,
|
||||
20: KEYS.CAPS_LOCK,
|
||||
27: KEYS.ESCAPE,
|
||||
32: KEYS.SPACE,
|
||||
33: KEYS.PAGEUP,
|
||||
34: KEYS.PAGEDOWN,
|
||||
35: KEYS.END,
|
||||
36: KEYS.HOME,
|
||||
37: KEYS.LEFT,
|
||||
38: KEYS.UP,
|
||||
39: KEYS.RIGHT,
|
||||
40: KEYS.DOWN,
|
||||
42: KEYS.PRINTSCREEN,
|
||||
44: KEYS.PRINTSCREEN,
|
||||
45: KEYS.INSERT,
|
||||
46: KEYS.DELETE,
|
||||
59: KEYS.SEMICOLON,
|
||||
61: KEYS.EQUAL,
|
||||
91: KEYS.CMD_L,
|
||||
92: KEYS.CMD_R,
|
||||
93: KEYS.CMD_R,
|
||||
96: KEYS.NUMPAD_0,
|
||||
97: KEYS.NUMPAD_1,
|
||||
98: KEYS.NUMPAD_2,
|
||||
99: KEYS.NUMPAD_3,
|
||||
100: KEYS.NUMPAD_4,
|
||||
101: KEYS.NUMPAD_5,
|
||||
102: KEYS.NUMPAD_6,
|
||||
103: KEYS.NUMPAD_7,
|
||||
104: KEYS.NUMPAD_8,
|
||||
105: KEYS.NUMPAD_9,
|
||||
112: KEYS.F1,
|
||||
113: KEYS.F2,
|
||||
114: KEYS.F3,
|
||||
115: KEYS.F4,
|
||||
116: KEYS.F5,
|
||||
117: KEYS.F6,
|
||||
118: KEYS.F7,
|
||||
119: KEYS.F8,
|
||||
120: KEYS.F9,
|
||||
121: KEYS.F10,
|
||||
122: KEYS.F11,
|
||||
123: KEYS.F12,
|
||||
124: KEYS.PRINTSCREEN,
|
||||
173: KEYS.MINUS,
|
||||
186: KEYS.SEMICOLON,
|
||||
187: KEYS.EQUAL,
|
||||
188: KEYS.COMMA,
|
||||
189: KEYS.MINUS,
|
||||
190: KEYS.PERIOD,
|
||||
191: KEYS.SLASH,
|
||||
192: KEYS.BACKQUOTE,
|
||||
219: KEYS.BRACKET_LEFT,
|
||||
220: KEYS.BACKSLASH,
|
||||
221: KEYS.BRACKET_RIGHT,
|
||||
222: KEYS.QUOTE,
|
||||
224: KEYS.META,
|
||||
229: KEYS.SEMICOLON
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate codes for digit keys (0-9)
|
||||
*/
|
||||
for(let i = 0; i < 10; i++) {
|
||||
keyCodeToKey[i + 48] = `${i}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate codes for letter keys (a-z)
|
||||
*/
|
||||
for(let i = 0; i < 26; i++) {
|
||||
let keyCode = i + 65;
|
||||
keyCodeToKey[keyCode] = String.fromCharCode(keyCode).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns key associated with the keyCode from the passed event.
|
||||
* @param {KeyboardEvent} event the event
|
||||
* @returns {KEYS} the key on the keyboard.
|
||||
*/
|
||||
export function keyboardEventToKey(event) {
|
||||
return keyCodeToKey[event.which];
|
||||
}
|
||||
374
modules/remotecontrol/Controller.js
Normal file
374
modules/remotecontrol/Controller.js
Normal file
@@ -0,0 +1,374 @@
|
||||
/* global $, JitsiMeetJS, APP */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
import * as KeyCodes from "../keycode/keycode";
|
||||
import {EVENT_TYPES, REMOTE_CONTROL_EVENT_TYPE, PERMISSIONS_ACTIONS}
|
||||
from "../../service/remotecontrol/Constants";
|
||||
import RemoteControlParticipant from "./RemoteControlParticipant";
|
||||
import UIEvents from "../../service/UI/UIEvents";
|
||||
|
||||
const ConferenceEvents = JitsiMeetJS.events.conference;
|
||||
|
||||
/**
|
||||
* Extract the keyboard key from the keyboard event.
|
||||
* @param event {KeyboardEvent} the event.
|
||||
* @returns {KEYS} the key that is pressed or undefined.
|
||||
*/
|
||||
function getKey(event) {
|
||||
return KeyCodes.keyboardEventToKey(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the modifiers from the keyboard event.
|
||||
* @param event {KeyboardEvent} the event.
|
||||
* @returns {Array} with possible values: "shift", "control", "alt", "command".
|
||||
*/
|
||||
function getModifiers(event) {
|
||||
let modifiers = [];
|
||||
if(event.shiftKey) {
|
||||
modifiers.push("shift");
|
||||
}
|
||||
|
||||
if(event.ctrlKey) {
|
||||
modifiers.push("control");
|
||||
}
|
||||
|
||||
|
||||
if(event.altKey) {
|
||||
modifiers.push("alt");
|
||||
}
|
||||
|
||||
if(event.metaKey) {
|
||||
modifiers.push("command");
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents the controller party for a remote controller session.
|
||||
* It listens for mouse and keyboard events and sends them to the receiver
|
||||
* party of the remote control session.
|
||||
*/
|
||||
export default class Controller extends RemoteControlParticipant {
|
||||
/**
|
||||
* Creates new instance.
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this.isCollectingEvents = false;
|
||||
this.controlledParticipant = null;
|
||||
this.requestedParticipant = null;
|
||||
this._stopListener = this._handleRemoteControlStoppedEvent.bind(this);
|
||||
this._userLeftListener = this._onUserLeft.bind(this);
|
||||
this._largeVideoChangedListener
|
||||
= this._onLargeVideoIdChanged.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests permissions from the remote control receiver side.
|
||||
* @param {string} userId the user id of the participant that will be
|
||||
* requested.
|
||||
* @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.
|
||||
*/
|
||||
requestPermissions(userId, eventCaptureArea) {
|
||||
if(!this.enabled) {
|
||||
return Promise.reject(new Error("Remote control is disabled!"));
|
||||
}
|
||||
this.area = eventCaptureArea;// $("#largeVideoWrapper")
|
||||
logger.log("Requsting remote control permissions from: " + userId);
|
||||
return new Promise((resolve, reject) => {
|
||||
const clearRequest = () => {
|
||||
this.requestedParticipant = null;
|
||||
APP.conference.removeConferenceListener(
|
||||
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||
permissionsReplyListener);
|
||||
APP.conference.removeConferenceListener(
|
||||
ConferenceEvents.USER_LEFT,
|
||||
onUserLeft);
|
||||
};
|
||||
const permissionsReplyListener = (participant, event) => {
|
||||
let result = null;
|
||||
try {
|
||||
result = this._handleReply(participant, event);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
if(result !== null) {
|
||||
clearRequest();
|
||||
resolve(result);
|
||||
}
|
||||
};
|
||||
const 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, {
|
||||
type: EVENT_TYPES.permissions,
|
||||
action: PERMISSIONS_ACTIONS.request
|
||||
}, e => {
|
||||
clearRequest();
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the reply of the permissions request.
|
||||
* @param {JitsiParticipant} participant the participant that has sent the
|
||||
* reply
|
||||
* @param {RemoteControlEvent} event the remote control event.
|
||||
*/
|
||||
_handleReply(participant, event) {
|
||||
const remoteControlEvent = event.event;
|
||||
const userId = participant.getId();
|
||||
if(this.enabled && event.type === REMOTE_CONTROL_EVENT_TYPE
|
||||
&& remoteControlEvent.type === EVENT_TYPES.permissions
|
||||
&& userId === this.requestedParticipant) {
|
||||
if(remoteControlEvent.action !== PERMISSIONS_ACTIONS.grant) {
|
||||
this.area = null;
|
||||
}
|
||||
switch(remoteControlEvent.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
|
||||
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 of
|
||||
* type REMOTE_CONTROL_EVENT_TYPE
|
||||
* @property {RemoteControlEvent} event - the remote control event.
|
||||
*/
|
||||
_handleRemoteControlStoppedEvent(participant, event) {
|
||||
if(this.enabled && event.type === REMOTE_CONTROL_EVENT_TYPE
|
||||
&& event.event.type === EVENT_TYPES.stop
|
||||
&& participant.getId() === this.controlledParticipant) {
|
||||
this._stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts processing the mouse and keyboard events. Sets conference
|
||||
* listeners. Disables keyboard events.
|
||||
*/
|
||||
_start() {
|
||||
logger.log("Starting remote control controller.");
|
||||
APP.UI.addListener(UIEvents.LARGE_VIDEO_ID_CHANGED,
|
||||
this._largeVideoChangedListener);
|
||||
APP.conference.addConferenceListener(
|
||||
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||
this._stopListener);
|
||||
APP.conference.addConferenceListener(ConferenceEvents.USER_LEFT,
|
||||
this._userLeftListener);
|
||||
this.resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the keyboatd shortcuts. Starts collecting remote control
|
||||
* events.
|
||||
*
|
||||
* It can be used to resume an active remote control session wchich was
|
||||
* paused with this.pause().
|
||||
*/
|
||||
resume() {
|
||||
if(!this.enabled || this.isCollectingEvents) {
|
||||
return;
|
||||
}
|
||||
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, {
|
||||
type: EVENT_TYPES.mousemove,
|
||||
x: (event.pageX - position.left)/this.area.width(),
|
||||
y: (event.pageY - position.top)/this.area.height()
|
||||
});
|
||||
});
|
||||
this.area.mousedown(this._onMouseClickHandler.bind(this,
|
||||
EVENT_TYPES.mousedown));
|
||||
this.area.mouseup(this._onMouseClickHandler.bind(this,
|
||||
EVENT_TYPES.mouseup));
|
||||
this.area.dblclick(
|
||||
this._onMouseClickHandler.bind(this, EVENT_TYPES.mousedblclick));
|
||||
this.area.contextmenu(() => false);
|
||||
this.area[0].onmousewheel = event => {
|
||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
||||
type: EVENT_TYPES.mousescroll,
|
||||
x: event.deltaX,
|
||||
y: event.deltaY
|
||||
});
|
||||
};
|
||||
$(window).keydown(this._onKeyPessHandler.bind(this,
|
||||
EVENT_TYPES.keydown));
|
||||
$(window).keyup(this._onKeyPessHandler.bind(this, EVENT_TYPES.keyup));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
_stop() {
|
||||
if(!this.controlledParticipant) {
|
||||
return;
|
||||
}
|
||||
logger.log("Stopping remote control controller.");
|
||||
APP.UI.removeListener(UIEvents.LARGE_VIDEO_ID_CHANGED,
|
||||
this._largeVideoChangedListener);
|
||||
APP.conference.removeConferenceListener(
|
||||
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||
this._stopListener);
|
||||
APP.conference.removeConferenceListener(ConferenceEvents.USER_LEFT,
|
||||
this._userLeftListener);
|
||||
this.controlledParticipant = null;
|
||||
this.pause();
|
||||
this.area = null;
|
||||
APP.UI.messageHandler.openMessageDialog(
|
||||
"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.
|
||||
*
|
||||
* In addition:
|
||||
* Sends stop message to the controlled participant.
|
||||
*/
|
||||
stop() {
|
||||
if(!this.controlledParticipant) {
|
||||
return;
|
||||
}
|
||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
||||
type: EVENT_TYPES.stop
|
||||
});
|
||||
this._stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* You can resume the collecting of the events with this.resume().
|
||||
*/
|
||||
pause() {
|
||||
if(!this.controlledParticipant) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for mouse click events.
|
||||
* @param {String} type the type of event ("mousedown"/"mouseup")
|
||||
* @param {Event} event the mouse event.
|
||||
*/
|
||||
_onMouseClickHandler(type, event) {
|
||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
||||
type: type,
|
||||
button: event.which
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the remote control session is started.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isStarted() {
|
||||
return this.controlledParticipant !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the requested participant
|
||||
* @returns {string} this.requestedParticipant.
|
||||
* NOTE: This id should be the result of JitsiParticipant.getId() call.
|
||||
*/
|
||||
getRequestedParticipant() {
|
||||
return this.requestedParticipant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for key press events.
|
||||
* @param {String} type the type of event ("keydown"/"keyup")
|
||||
* @param {Event} event the key event.
|
||||
*/
|
||||
_onKeyPessHandler(type, event) {
|
||||
this._sendRemoteControlEvent(this.controlledParticipant, {
|
||||
type: type,
|
||||
key: getKey(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
|
||||
*/
|
||||
_onUserLeft(id) {
|
||||
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.
|
||||
*/
|
||||
_onLargeVideoIdChanged(id) {
|
||||
if (!this.controlledParticipant) {
|
||||
return;
|
||||
}
|
||||
if(this.controlledParticipant == id) {
|
||||
this.resume();
|
||||
} else {
|
||||
this.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
192
modules/remotecontrol/Receiver.js
Normal file
192
modules/remotecontrol/Receiver.js
Normal file
@@ -0,0 +1,192 @@
|
||||
/* global APP, JitsiMeetJS, interfaceConfig */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
import {DISCO_REMOTE_CONTROL_FEATURE, REMOTE_CONTROL_EVENT_TYPE, EVENT_TYPES,
|
||||
PERMISSIONS_ACTIONS} from "../../service/remotecontrol/Constants";
|
||||
import RemoteControlParticipant from "./RemoteControlParticipant";
|
||||
import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
|
||||
|
||||
const ConferenceEvents = JitsiMeetJS.events.conference;
|
||||
|
||||
/**
|
||||
* This class represents the receiver party for a remote controller session.
|
||||
* It handles "remote-control-event" events and sends them to the
|
||||
* API module. From there the events can be received from wrapper application
|
||||
* and executed.
|
||||
*/
|
||||
export default class Receiver extends RemoteControlParticipant {
|
||||
/**
|
||||
* Creates new instance.
|
||||
* @constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this.controller = null;
|
||||
this._remoteControlEventsListener
|
||||
= this._onRemoteControlEvent.bind(this);
|
||||
this._userLeftListener = this._onUserLeft.bind(this);
|
||||
this._hangupListener = this._onHangup.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables / Disables the remote control
|
||||
* @param {boolean} enabled the new state.
|
||||
*/
|
||||
enable(enabled) {
|
||||
if(this.enabled === enabled) {
|
||||
return;
|
||||
}
|
||||
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(
|
||||
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||
this._remoteControlEventsListener);
|
||||
APP.conference.addListener(JitsiMeetConferenceEvents.BEFORE_HANGUP,
|
||||
this._hangupListener);
|
||||
} else {
|
||||
logger.log("Remote control receiver disabled.");
|
||||
this._stop(true);
|
||||
APP.connection.removeFeature(DISCO_REMOTE_CONTROL_FEATURE);
|
||||
APP.conference.removeConferenceListener(
|
||||
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||
this._remoteControlEventsListener);
|
||||
APP.conference.removeListener(
|
||||
JitsiMeetConferenceEvents.BEFORE_HANGUP,
|
||||
this._hangupListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener for ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED
|
||||
* 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.
|
||||
*/
|
||||
_stop(dontShowDialog = false) {
|
||||
if(!this.controller) {
|
||||
return;
|
||||
}
|
||||
logger.log("Remote control receiver stop.");
|
||||
this.controller = null;
|
||||
APP.conference.removeConferenceListener(ConferenceEvents.USER_LEFT,
|
||||
this._userLeftListener);
|
||||
APP.API.sendRemoteControlEvent({
|
||||
type: EVENT_TYPES.stop
|
||||
});
|
||||
if(!dontShowDialog) {
|
||||
APP.UI.messageHandler.openMessageDialog(
|
||||
"dialog.remoteControlTitle",
|
||||
"dialog.remoteControlStopMessage"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls this._stop() and sends stop message to the controller participant
|
||||
*/
|
||||
stop() {
|
||||
if(!this.controller) {
|
||||
return;
|
||||
}
|
||||
this._sendRemoteControlEvent(this.controller, {
|
||||
type: EVENT_TYPES.stop
|
||||
});
|
||||
this._stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 of
|
||||
* type REMOTE_CONTROL_EVENT_TYPE
|
||||
* @property {RemoteControlEvent} event - the remote control event.
|
||||
*/
|
||||
_onRemoteControlEvent(participant, event) {
|
||||
if(this.enabled && event.type === REMOTE_CONTROL_EVENT_TYPE) {
|
||||
const remoteControlEvent = event.event;
|
||||
if(this.controller === null
|
||||
&& remoteControlEvent.type === EVENT_TYPES.permissions
|
||||
&& remoteControlEvent.action === PERMISSIONS_ACTIONS.request) {
|
||||
remoteControlEvent.userId = participant.getId();
|
||||
remoteControlEvent.userJID = participant.getJid();
|
||||
remoteControlEvent.displayName = participant.getDisplayName()
|
||||
|| interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
||||
remoteControlEvent.screenSharing
|
||||
= APP.conference.isSharingScreen;
|
||||
} else if(this.controller !== participant.getId()) {
|
||||
return;
|
||||
} else if(remoteControlEvent.type === EVENT_TYPES.stop) {
|
||||
this._stop();
|
||||
return;
|
||||
}
|
||||
APP.API.sendRemoteControlEvent(remoteControlEvent);
|
||||
} else if(event.type === REMOTE_CONTROL_EVENT_TYPE) {
|
||||
logger.log("Remote control event is ignored because remote "
|
||||
+ "control is disabled", event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles remote control permission events received from the API module.
|
||||
* @param {String} userId the user id of the participant related to the
|
||||
* event.
|
||||
* @param {PERMISSIONS_ACTIONS} action the action related to the event.
|
||||
*/
|
||||
_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;
|
||||
}
|
||||
}
|
||||
this._sendRemoteControlEvent(userId, {
|
||||
type: EVENT_TYPES.permissions,
|
||||
action: action
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the stop method if the other side have left.
|
||||
* @param {string} id - the user id for the participant that have left
|
||||
*/
|
||||
_onUserLeft(id) {
|
||||
if(this.controller === id) {
|
||||
this._stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles hangup events. Disables the receiver.
|
||||
*/
|
||||
_onHangup() {
|
||||
this.enable(false);
|
||||
}
|
||||
}
|
||||
89
modules/remotecontrol/RemoteControl.js
Normal file
89
modules/remotecontrol/RemoteControl.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/* global APP, config */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
import Controller from "./Controller";
|
||||
import Receiver from "./Receiver";
|
||||
import {EVENT_TYPES, DISCO_REMOTE_CONTROL_FEATURE}
|
||||
from "../../service/remotecontrol/Constants";
|
||||
|
||||
/**
|
||||
* Implements the remote control functionality.
|
||||
*/
|
||||
class RemoteControl {
|
||||
/**
|
||||
* Constructs new instance. Creates controller and receiver properties.
|
||||
* @constructor
|
||||
*/
|
||||
constructor() {
|
||||
this.controller = new Controller();
|
||||
this.receiver = new Receiver();
|
||||
this.enabled = false;
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the remote control - checks if the remote control should be
|
||||
* enabled or not, initializes the API module.
|
||||
*/
|
||||
init() {
|
||||
if(config.disableRemoteControl || this.initialized
|
||||
|| !APP.conference.isDesktopSharingEnabled) {
|
||||
return;
|
||||
}
|
||||
logger.log("Initializing remote control.");
|
||||
this.initialized = true;
|
||||
APP.API.init({
|
||||
forceEnable: true,
|
||||
});
|
||||
this.controller.enable(true);
|
||||
if(this.enabled) { // supported message came before init.
|
||||
this._onRemoteControlSupported();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles remote control events from the API module. Currently only events
|
||||
* with type = EVENT_TYPES.supported or EVENT_TYPES.permissions
|
||||
* @param {RemoteControlEvent} event the remote control event.
|
||||
*/
|
||||
onRemoteControlAPIEvent(event) {
|
||||
switch(event.type) {
|
||||
case EVENT_TYPES.supported:
|
||||
this._onRemoteControlSupported();
|
||||
break;
|
||||
case EVENT_TYPES.permissions:
|
||||
this.receiver._onRemoteControlPermissionsEvent(
|
||||
event.userId, event.action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles API event for support for executing remote control events into
|
||||
* the wrapper application.
|
||||
*/
|
||||
_onRemoteControlSupported() {
|
||||
logger.log("Remote Control supported.");
|
||||
if(!config.disableRemoteControl) {
|
||||
this.enabled = true;
|
||||
if(this.initialized) {
|
||||
this.receiver.enable(true);
|
||||
}
|
||||
} else {
|
||||
logger.log("Remote Control disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return user.getFeatures().then(features =>
|
||||
features.has(DISCO_REMOTE_CONTROL_FEATURE), () => false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default new RemoteControl();
|
||||
42
modules/remotecontrol/RemoteControlParticipant.js
Normal file
42
modules/remotecontrol/RemoteControlParticipant.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/* global APP */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
import {REMOTE_CONTROL_EVENT_TYPE}
|
||||
from "../../service/remotecontrol/Constants";
|
||||
|
||||
export default class RemoteControlParticipant {
|
||||
/**
|
||||
* Creates new instance.
|
||||
*/
|
||||
constructor() {
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables / Disables the remote control
|
||||
* @param {boolean} enabled the new state.
|
||||
*/
|
||||
enable(enabled) {
|
||||
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.
|
||||
*/
|
||||
_sendRemoteControlEvent(to, event, onDataChannelFail = () => {}) {
|
||||
if(!this.enabled || !to) {
|
||||
logger.warn("Remote control: Skip sending remote control event."
|
||||
+ " Params:", this.enable, to);
|
||||
return;
|
||||
}
|
||||
try{
|
||||
APP.conference.sendEndpointMessage(to,
|
||||
{type: REMOTE_CONTROL_EVENT_TYPE, event});
|
||||
} catch (e) {
|
||||
logger.error("Failed to send EndpointMessage via the datachannels",
|
||||
e);
|
||||
onDataChannelFail(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,10 +73,6 @@ class TokenData{
|
||||
|
||||
this.jwt = jwt;
|
||||
|
||||
//External API settings
|
||||
this.externalAPISettings = {
|
||||
forceEnable: true
|
||||
};
|
||||
this._decode();
|
||||
// Use JWT param as token if there is not other token set and if the
|
||||
// iss field is not anonymous. If you want to pass data with JWT token
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
"postis": "^2.2.0",
|
||||
"react": "15.4.2",
|
||||
"react-dom": "15.4.2",
|
||||
"react-native": "0.39.2",
|
||||
"react-native": "0.40.0",
|
||||
"react-native-keep-awake": "^1.0.7",
|
||||
"react-native-prompt": "^1.0.0",
|
||||
"react-native-vector-icons": "^3.0.0",
|
||||
"react-native-vector-icons": "^4.0.0",
|
||||
"react-native-webrtc": "jitsi/react-native-webrtc",
|
||||
"react-redux": "^5.0.2",
|
||||
"redux": "^3.5.2",
|
||||
@@ -57,7 +57,7 @@
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"babel-preset-react": "^6.16.0",
|
||||
"babel-preset-stage-1": "^6.16.0",
|
||||
"clean-css": "*",
|
||||
"clean-css": "^3.0.0",
|
||||
"css-loader": "*",
|
||||
"eslint": "^3.13.1",
|
||||
"eslint-plugin-jsdoc": "*",
|
||||
|
||||
@@ -23,7 +23,11 @@ export function init() {
|
||||
|
||||
APP.keyboardshortcut = KeyboardShortcut;
|
||||
APP.tokenData = getTokenData();
|
||||
APP.API.init(APP.tokenData.externalAPISettings);
|
||||
|
||||
// Force enable the API if jwt token is passed because most probably
|
||||
// jitsi meet is displayed inside of wrapper that will need to communicate
|
||||
// with jitsi meet.
|
||||
APP.API.init(APP.tokenData.jwt ? { forceEnable: true } : undefined);
|
||||
|
||||
APP.translation.init(settings.getLanguage());
|
||||
}
|
||||
|
||||
@@ -120,6 +120,11 @@ export default {
|
||||
*/
|
||||
LARGE_VIDEO_AVATAR_VISIBLE: "UI.large_video_avatar_visible",
|
||||
|
||||
/**
|
||||
* Notifies that the displayed particpant id on the largeVideo is changed.
|
||||
*/
|
||||
LARGE_VIDEO_ID_CHANGED: "UI.large_video_id_changed",
|
||||
|
||||
/**
|
||||
* Toggling room lock
|
||||
*/
|
||||
|
||||
69
service/remotecontrol/Constants.js
Normal file
69
service/remotecontrol/Constants.js
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* The value for the "var" attribute of feature tag in disco-info packets.
|
||||
*/
|
||||
export const DISCO_REMOTE_CONTROL_FEATURE
|
||||
= "http://jitsi.org/meet/remotecontrol";
|
||||
|
||||
/**
|
||||
* Types of remote-control-event events.
|
||||
* @readonly
|
||||
* @enum {string}
|
||||
*/
|
||||
export const EVENT_TYPES = {
|
||||
mousemove: "mousemove",
|
||||
mousedown: "mousedown",
|
||||
mouseup: "mouseup",
|
||||
mousedblclick: "mousedblclick",
|
||||
mousescroll: "mousescroll",
|
||||
keydown: "keydown",
|
||||
keyup: "keyup",
|
||||
permissions: "permissions",
|
||||
stop: "stop",
|
||||
supported: "supported"
|
||||
};
|
||||
|
||||
/**
|
||||
* Actions for the remote control permission events.
|
||||
* @readonly
|
||||
* @enum {string}
|
||||
*/
|
||||
export const PERMISSIONS_ACTIONS = {
|
||||
request: "request",
|
||||
grant: "grant",
|
||||
deny: "deny",
|
||||
error: "error"
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of remote control events sent trough the API module.
|
||||
*/
|
||||
export const REMOTE_CONTROL_EVENT_TYPE = "remote-control-event";
|
||||
|
||||
/**
|
||||
* The remote control event.
|
||||
* @typedef {object} RemoteControlEvent
|
||||
* @property {EVENT_TYPES} type - the type of the event
|
||||
* @property {int} x - avaibale for type === mousemove only. The new x
|
||||
* coordinate of the mouse
|
||||
* @property {int} y - For mousemove type - the new y
|
||||
* coordinate of the mouse and for mousescroll - represents the vertical
|
||||
* scrolling diff value
|
||||
* @property {int} button - 1(left), 2(middle) or 3 (right). Supported by
|
||||
* mousedown, mouseup and mousedblclick types.
|
||||
* @property {KEYS} key - Represents the key related to the event. Supported by
|
||||
* keydown and keyup types.
|
||||
* @property {KEYS[]} modifiers - Represents the modifier related to the event.
|
||||
* Supported by keydown and keyup types.
|
||||
* @property {PERMISSIONS_ACTIONS} action - Supported by type === permissions.
|
||||
* Represents the action related to the permissions event.
|
||||
*
|
||||
* Optional properties. Supported for permissions event for action === request:
|
||||
* @property {string} userId - The user id of the participant that has sent the
|
||||
* request.
|
||||
* @property {string} userJID - The full JID in the MUC of the user that has
|
||||
* sent the request.
|
||||
* @property {string} displayName - the displayName of the participant that has
|
||||
* sent the request.
|
||||
* @property {boolean} screenSharing - true if the SS is started for the local
|
||||
* participant and false if not.
|
||||
*/
|
||||
Reference in New Issue
Block a user